Mercurial > hg
annotate rust/hg-cpython/src/ancestors.rs @ 41188:006c9ce486fa
rust-cpython: bindings for MissingAncestors
The exposition is rather straightforward, except for the
remove_ancestors_from() method, which forces us to an inefficient
conversion between Python sets and Rust HashSets.
Two alternatives are proposed in comments:
- changing the inner API to "emit" the revision numbers to discard
this would be a substantial change, and it would be better only in the
cases where there are more to retain than to discard
- mutating the Python set directly: this would force us to define an abstract
`RevisionSet` trait, and implement it both for plain `HashSet` and for
a struct enclosing a Python set with the GIL marker `Python<'p>`, also a non
trivial effort.
The main (and seemingly only) caller of this method being
`mercurial.setdiscovery`, which is currently undergoing serious refactoring,
it's not clear whether these improvements would be worth the effort right now,
so we're leaving it as-is.
Also, in `get_bases()` (will also be used by `setdiscovery`), we'd prefer
to build a Python set directly, but we resort to returning a tuple, waiting
to hear back from our PR onto rust-cpython about that
Differential Revision: https://phab.mercurial-scm.org/D5550
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Fri, 30 Nov 2018 20:05:34 +0100 |
parents | 3bf6979a1785 |
children | ff333620a4cc |
rev | line source |
---|---|
40965
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
1 // ancestors.rs |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
2 // |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr> |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
4 // |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
5 // This software may be used and distributed according to the terms of the |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
6 // GNU General Public License version 2 or any later version. |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
7 |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
8 //! Bindings for the `hg::ancestors` module provided by the |
40965
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
9 //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor` |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
10 //! and can be used as replacement for the the pure `ancestor` Python module. |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
11 //! |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
12 //! # Classes visible from Python: |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
13 //! - [`LazyAncestors`] is the Rust implementation of |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
14 //! `mercurial.ancestor.lazyancestors`. |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
15 //! The only difference is that it is instantiated with a C `parsers.index` |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
16 //! instance instead of a parents function. |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
17 //! |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
18 //! - [`MissingAncestors`] is the Rust implementation of |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
19 //! `mercurial.ancestor.incrementalmissingancestors`. |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
20 //! |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
21 //! API differences: |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
22 //! + it is instantiated with a C `parsers.index` |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
23 //! instance instead of a parents function. |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
24 //! + `MissingAncestors.bases` is a method returning a tuple instead of |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
25 //! a set-valued attribute. We could return a Python set easily if our |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
26 //! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
27 //! is accepted. |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
28 //! |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
29 //! - [`AncestorsIterator`] is the Rust counterpart of the |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
30 //! `ancestor._lazyancestorsiter` Python generator. |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
31 //! From Python, instances of this should be mainly obtained by calling |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
32 //! `iter()` on a [`LazyAncestors`] instance. |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
33 //! |
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
34 //! [`LazyAncestors`]: struct.LazyAncestors.html |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
35 //! [`MissingAncestors`]: struct.MissingAncestors.html |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
37 use cindex::Index; |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
38 use cpython::{ |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
39 ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
40 PyResult, PyTuple, Python, PythonObject, ToPyObject, |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
41 }; |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
42 use exceptions::GraphError; |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
43 use hg::Revision; |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
44 use hg::{ |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
45 AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy, |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
46 MissingAncestors as CoreMissing, |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
47 }; |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
48 use std::cell::RefCell; |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
49 use std::iter::FromIterator; |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
50 use std::collections::HashSet; |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
51 |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
52 /// Utility function to convert a Python iterable into various collections |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
53 /// |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
54 /// We need this in particular to feed to various methods of inner objects |
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
55 /// with `impl IntoIterator<Item=Revision>` arguments, because |
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
56 /// a `PyErr` can arise at each step of iteration, whereas these methods |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
57 /// expect iterables over `Revision`, not over some `Result<Revision, PyErr>` |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
58 fn rev_pyiter_collect<C>(py: Python, revs: &PyObject) -> PyResult<C> |
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
59 where |
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
60 C: FromIterator<Revision>, |
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
61 { |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
62 revs.iter(py)? |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
63 .map(|r| r.and_then(|o| o.extract::<Revision>(py))) |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
64 .collect() |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
65 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
66 |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
67 py_class!(pub class AncestorsIterator |py| { |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
68 data inner: RefCell<Box<CoreIterator<Index>>>; |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
69 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
70 def __next__(&self) -> PyResult<Option<Revision>> { |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
71 match self.inner(py).borrow_mut().next() { |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
72 Some(Err(e)) => Err(GraphError::pynew(py, e)), |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
73 None => Ok(None), |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
74 Some(Ok(r)) => Ok(Some(r)), |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
75 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
76 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
77 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
78 def __contains__(&self, rev: Revision) -> PyResult<bool> { |
41186
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
79 self.inner(py).borrow_mut().contains(rev) |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
80 .map_err(|e| GraphError::pynew(py, e)) |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
81 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
82 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
83 def __iter__(&self) -> PyResult<Self> { |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
84 Ok(self.clone_ref(py)) |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
85 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
86 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
87 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
88 inclusive: bool) -> PyResult<AncestorsIterator> { |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
89 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?; |
41186
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
90 let ait = CoreIterator::new( |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
91 Index::new(py, index)?, |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
92 initvec, |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
93 stoprev, |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
94 inclusive, |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
95 ) |
1b4b94bac8a0
rust-cpython: style consistency leftovers
Georges Racinet <georges.racinet@octobus.net>
parents:
41185
diff
changeset
|
96 .map_err(|e| GraphError::pynew(py, e))?; |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
97 AncestorsIterator::from_inner(py, ait) |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
98 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
99 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
100 }); |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
101 |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
102 impl AncestorsIterator { |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
103 pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> { |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
104 Self::create_instance(py, RefCell::new(Box::new(ait))) |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
105 } |
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
106 } |
40965
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
107 |
41184
dcf818267bc1
rust-cpython: rustdoc improvements
Georges Racinet <georges.racinet@octobus.net>
parents:
41114
diff
changeset
|
108 py_class!(pub class LazyAncestors |py| { |
41114
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
109 data inner: RefCell<Box<CoreLazy<Index>>>; |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
110 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
111 def __contains__(&self, rev: Revision) -> PyResult<bool> { |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
112 self.inner(py) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
113 .borrow_mut() |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
114 .contains(rev) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
115 .map_err(|e| GraphError::pynew(py, e)) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
116 } |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
117 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
118 def __iter__(&self) -> PyResult<AncestorsIterator> { |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
119 AncestorsIterator::from_inner(py, self.inner(py).borrow().iter()) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
120 } |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
121 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
122 def __bool__(&self) -> PyResult<bool> { |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
123 Ok(!self.inner(py).borrow().is_empty()) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
124 } |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
125 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
126 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
127 inclusive: bool) -> PyResult<Self> { |
41187
3bf6979a1785
rust-cpython: generalised conversion function
Georges Racinet <georges.racinet@octobus.net>
parents:
41186
diff
changeset
|
128 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?; |
41114
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
129 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
130 let lazy = |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
131 CoreLazy::new(Index::new(py, index)?, initvec, stoprev, inclusive) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
132 .map_err(|e| GraphError::pynew(py, e))?; |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
133 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
134 Self::create_instance(py, RefCell::new(Box::new(lazy))) |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
135 } |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
136 |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
137 }); |
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
138 |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
139 py_class!(pub class MissingAncestors |py| { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
140 data inner: RefCell<Box<CoreMissing<Index>>>; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
141 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
142 def __new__(_cls, index: PyObject, bases: PyObject) -> PyResult<MissingAncestors> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
143 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
144 let inner = CoreMissing::new(Index::new(py, index)?, bases_vec); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
145 MissingAncestors::create_instance(py, RefCell::new(Box::new(inner))) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
146 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
147 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
148 def hasbases(&self) -> PyResult<bool> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
149 Ok(self.inner(py).borrow().has_bases()) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
150 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
151 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
152 def addbases(&self, bases: PyObject) -> PyResult<PyObject> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
153 let mut inner = self.inner(py).borrow_mut(); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
154 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
155 inner.add_bases(bases_vec); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
156 // cpython doc has examples with PyResult<()> but this gives me |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
157 // the trait `cpython::ToPyObject` is not implemented for `()` |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
158 // so let's return an explicit None |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
159 Ok(py.None()) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
160 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
161 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
162 def bases(&self) -> PyResult<PyTuple> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
163 let inner = self.inner(py).borrow(); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
164 let bases_set = inner.get_bases(); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
165 // convert as Python tuple TODO how to return a proper Python set? |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
166 let mut bases_vec: Vec<PyObject> = Vec::with_capacity( |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
167 bases_set.len()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
168 for rev in bases_set { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
169 bases_vec.push(rev.to_py_object(py).into_object()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
170 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
171 Ok(PyTuple::new(py, bases_vec.as_slice())) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
172 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
173 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
174 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
175 let mut inner = self.inner(py).borrow_mut(); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
176 // this is very lame: we convert to a Rust set, update it in place |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
177 // and then convert back to Python, only to have Python remove the |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
178 // excess (thankfully, Python is happy with a list or even an iterator) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
179 // Leads to improve this: |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
180 // - have the CoreMissing instead do something emit revisions to |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
181 // discard |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
182 // - define a trait for sets of revisions in the core and implement |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
183 // it for a Python set rewrapped with the GIL marker |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
184 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
185 inner.remove_ancestors_from(&mut revs_pyset) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
186 .map_err(|e| GraphError::pynew(py, e))?; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
187 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
188 // convert as Python list |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
189 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity( |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
190 revs_pyset.len()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
191 for rev in revs_pyset { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
192 remaining_pyint_vec.push(rev.to_py_object(py).into_object()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
193 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
194 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
195 revs.call_method(py, "intersection_update", (remaining_pylist, ), None) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
196 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
197 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
198 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
199 let mut inner = self.inner(py).borrow_mut(); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
200 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
201 let missing_vec = match inner.missing_ancestors(revs_vec) { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
202 Ok(missing) => missing, |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
203 Err(e) => { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
204 return Err(GraphError::pynew(py, e)); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
205 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
206 }; |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
207 // convert as Python list |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
208 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity( |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
209 missing_vec.len()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
210 for rev in missing_vec { |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
211 missing_pyint_vec.push(rev.to_py_object(py).into_object()); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
212 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
213 Ok(PyList::new(py, missing_pyint_vec.as_slice())) |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
214 } |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
215 }); |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
216 |
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
217 /// Create the module, with __package__ given from parent |
40965
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
218 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
219 let dotted_name = &format!("{}.ancestor", package); |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
220 let m = PyModule::new(py, dotted_name)?; |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
221 m.add(py, "__package__", package)?; |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
222 m.add( |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
223 py, |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
224 "__doc__", |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
225 "Generic DAG ancestor algorithms - Rust implementation", |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
226 )?; |
41053
d9f439fcdb4c
rust-cpython: binding for AncestorsIterator
Georges Racinet <gracinet@anybox.fr>
parents:
40965
diff
changeset
|
227 m.add_class::<AncestorsIterator>(py)?; |
41114
b31a41f24864
rust-cpython: binding for LazyAncestors
Georges Racinet <gracinet@anybox.fr>
parents:
41053
diff
changeset
|
228 m.add_class::<LazyAncestors>(py)?; |
41188
006c9ce486fa
rust-cpython: bindings for MissingAncestors
Georges Racinet <georges.racinet@octobus.net>
parents:
41187
diff
changeset
|
229 m.add_class::<MissingAncestors>(py)?; |
40965
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
230 |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
231 let sys = PyModule::import(py, "sys")?; |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
232 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
233 sys_modules.set_item(py, dotted_name, &m)?; |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
234 // Example C code (see pyexpat.c and import.c) will "give away the |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
235 // reference", but we won't because it will be consumed once the |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
236 // Rust PyObject is dropped. |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
237 Ok(m) |
5532823e8c18
rust-cpython: start cpython crate bindings
Georges Racinet <gracinet@anybox.fr>
parents:
diff
changeset
|
238 } |