Show More
@@ -15,22 +15,39 b'' | |||||
15 | //! The only difference is that it is instantiated with a C `parsers.index` |
|
15 | //! The only difference is that it is instantiated with a C `parsers.index` | |
16 | //! instance instead of a parents function. |
|
16 | //! instance instead of a parents function. | |
17 | //! |
|
17 | //! | |
|
18 | //! - [`MissingAncestors`] is the Rust implementation of | |||
|
19 | //! `mercurial.ancestor.incrementalmissingancestors`. | |||
|
20 | //! | |||
|
21 | //! API differences: | |||
|
22 | //! + it is instantiated with a C `parsers.index` | |||
|
23 | //! instance instead of a parents function. | |||
|
24 | //! + `MissingAncestors.bases` is a method returning a tuple instead of | |||
|
25 | //! a set-valued attribute. We could return a Python set easily if our | |||
|
26 | //! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165) | |||
|
27 | //! is accepted. | |||
|
28 | //! | |||
18 | //! - [`AncestorsIterator`] is the Rust counterpart of the |
|
29 | //! - [`AncestorsIterator`] is the Rust counterpart of the | |
19 | //! `ancestor._lazyancestorsiter` Python generator. |
|
30 | //! `ancestor._lazyancestorsiter` Python generator. | |
20 | //! From Python, instances of this should be mainly obtained by calling |
|
31 | //! From Python, instances of this should be mainly obtained by calling | |
21 | //! `iter()` on a [`LazyAncestors`] instance. |
|
32 | //! `iter()` on a [`LazyAncestors`] instance. | |
22 | //! |
|
33 | //! | |
23 | //! [`LazyAncestors`]: struct.LazyAncestors.html |
|
34 | //! [`LazyAncestors`]: struct.LazyAncestors.html | |
|
35 | //! [`MissingAncestors`]: struct.MissingAncestors.html | |||
24 | //! [`AncestorsIterator`]: struct.AncestorsIterator.html |
|
36 | //! [`AncestorsIterator`]: struct.AncestorsIterator.html | |
25 | use cindex::Index; |
|
37 | use cindex::Index; | |
26 | use cpython::{ |
|
38 | use cpython::{ | |
27 |
ObjectProtocol, PyClone, PyDict, PyModule, PyObject, |
|
39 | ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, | |
|
40 | PyResult, PyTuple, Python, PythonObject, ToPyObject, | |||
28 | }; |
|
41 | }; | |
29 | use exceptions::GraphError; |
|
42 | use exceptions::GraphError; | |
30 | use hg::Revision; |
|
43 | use hg::Revision; | |
31 | use hg::{AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy}; |
|
44 | use hg::{ | |
|
45 | AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy, | |||
|
46 | MissingAncestors as CoreMissing, | |||
|
47 | }; | |||
32 | use std::cell::RefCell; |
|
48 | use std::cell::RefCell; | |
33 | use std::iter::FromIterator; |
|
49 | use std::iter::FromIterator; | |
|
50 | use std::collections::HashSet; | |||
34 |
|
51 | |||
35 | /// Utility function to convert a Python iterable into various collections |
|
52 | /// Utility function to convert a Python iterable into various collections | |
36 | /// |
|
53 | /// | |
@@ -119,7 +136,85 b' py_class!(pub class LazyAncestors |py| {' | |||||
119 |
|
136 | |||
120 | }); |
|
137 | }); | |
121 |
|
138 | |||
122 | /// Create the module, with `__package__` given from parent |
|
139 | py_class!(pub class MissingAncestors |py| { | |
|
140 | data inner: RefCell<Box<CoreMissing<Index>>>; | |||
|
141 | ||||
|
142 | def __new__(_cls, index: PyObject, bases: PyObject) -> PyResult<MissingAncestors> { | |||
|
143 | let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?; | |||
|
144 | let inner = CoreMissing::new(Index::new(py, index)?, bases_vec); | |||
|
145 | MissingAncestors::create_instance(py, RefCell::new(Box::new(inner))) | |||
|
146 | } | |||
|
147 | ||||
|
148 | def hasbases(&self) -> PyResult<bool> { | |||
|
149 | Ok(self.inner(py).borrow().has_bases()) | |||
|
150 | } | |||
|
151 | ||||
|
152 | def addbases(&self, bases: PyObject) -> PyResult<PyObject> { | |||
|
153 | let mut inner = self.inner(py).borrow_mut(); | |||
|
154 | let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?; | |||
|
155 | inner.add_bases(bases_vec); | |||
|
156 | // cpython doc has examples with PyResult<()> but this gives me | |||
|
157 | // the trait `cpython::ToPyObject` is not implemented for `()` | |||
|
158 | // so let's return an explicit None | |||
|
159 | Ok(py.None()) | |||
|
160 | } | |||
|
161 | ||||
|
162 | def bases(&self) -> PyResult<PyTuple> { | |||
|
163 | let inner = self.inner(py).borrow(); | |||
|
164 | let bases_set = inner.get_bases(); | |||
|
165 | // convert as Python tuple TODO how to return a proper Python set? | |||
|
166 | let mut bases_vec: Vec<PyObject> = Vec::with_capacity( | |||
|
167 | bases_set.len()); | |||
|
168 | for rev in bases_set { | |||
|
169 | bases_vec.push(rev.to_py_object(py).into_object()); | |||
|
170 | } | |||
|
171 | Ok(PyTuple::new(py, bases_vec.as_slice())) | |||
|
172 | } | |||
|
173 | ||||
|
174 | def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> { | |||
|
175 | let mut inner = self.inner(py).borrow_mut(); | |||
|
176 | // this is very lame: we convert to a Rust set, update it in place | |||
|
177 | // and then convert back to Python, only to have Python remove the | |||
|
178 | // excess (thankfully, Python is happy with a list or even an iterator) | |||
|
179 | // Leads to improve this: | |||
|
180 | // - have the CoreMissing instead do something emit revisions to | |||
|
181 | // discard | |||
|
182 | // - define a trait for sets of revisions in the core and implement | |||
|
183 | // it for a Python set rewrapped with the GIL marker | |||
|
184 | let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?; | |||
|
185 | inner.remove_ancestors_from(&mut revs_pyset) | |||
|
186 | .map_err(|e| GraphError::pynew(py, e))?; | |||
|
187 | ||||
|
188 | // convert as Python list | |||
|
189 | let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity( | |||
|
190 | revs_pyset.len()); | |||
|
191 | for rev in revs_pyset { | |||
|
192 | remaining_pyint_vec.push(rev.to_py_object(py).into_object()); | |||
|
193 | } | |||
|
194 | let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice()); | |||
|
195 | revs.call_method(py, "intersection_update", (remaining_pylist, ), None) | |||
|
196 | } | |||
|
197 | ||||
|
198 | def missingancestors(&self, revs: PyObject) -> PyResult<PyList> { | |||
|
199 | let mut inner = self.inner(py).borrow_mut(); | |||
|
200 | let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?; | |||
|
201 | let missing_vec = match inner.missing_ancestors(revs_vec) { | |||
|
202 | Ok(missing) => missing, | |||
|
203 | Err(e) => { | |||
|
204 | return Err(GraphError::pynew(py, e)); | |||
|
205 | } | |||
|
206 | }; | |||
|
207 | // convert as Python list | |||
|
208 | let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity( | |||
|
209 | missing_vec.len()); | |||
|
210 | for rev in missing_vec { | |||
|
211 | missing_pyint_vec.push(rev.to_py_object(py).into_object()); | |||
|
212 | } | |||
|
213 | Ok(PyList::new(py, missing_pyint_vec.as_slice())) | |||
|
214 | } | |||
|
215 | }); | |||
|
216 | ||||
|
217 | /// Create the module, with __package__ given from parent | |||
123 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
218 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | |
124 | let dotted_name = &format!("{}.ancestor", package); |
|
219 | let dotted_name = &format!("{}.ancestor", package); | |
125 | let m = PyModule::new(py, dotted_name)?; |
|
220 | let m = PyModule::new(py, dotted_name)?; | |
@@ -131,6 +226,7 b' pub fn init_module(py: Python, package: ' | |||||
131 | )?; |
|
226 | )?; | |
132 | m.add_class::<AncestorsIterator>(py)?; |
|
227 | m.add_class::<AncestorsIterator>(py)?; | |
133 | m.add_class::<LazyAncestors>(py)?; |
|
228 | m.add_class::<LazyAncestors>(py)?; | |
|
229 | m.add_class::<MissingAncestors>(py)?; | |||
134 |
|
230 | |||
135 | let sys = PyModule::import(py, "sys")?; |
|
231 | let sys = PyModule::import(py, "sys")?; | |
136 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
232 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
@@ -11,7 +11,8 b' else:' | |||||
11 | # this would fail already without appropriate ancestor.__package__ |
|
11 | # this would fail already without appropriate ancestor.__package__ | |
12 | from mercurial.rustext.ancestor import ( |
|
12 | from mercurial.rustext.ancestor import ( | |
13 | AncestorsIterator, |
|
13 | AncestorsIterator, | |
14 | LazyAncestors |
|
14 | LazyAncestors, | |
|
15 | MissingAncestors, | |||
15 | ) |
|
16 | ) | |
16 |
|
17 | |||
17 | try: |
|
18 | try: | |
@@ -105,6 +106,22 b' class rustancestorstest(unittest.TestCas' | |||||
105 | # let's check bool for an empty one |
|
106 | # let's check bool for an empty one | |
106 | self.assertFalse(LazyAncestors(idx, [0], 0, False)) |
|
107 | self.assertFalse(LazyAncestors(idx, [0], 0, False)) | |
107 |
|
108 | |||
|
109 | def testmissingancestors(self): | |||
|
110 | idx = self.parseindex() | |||
|
111 | missanc = MissingAncestors(idx, [1]) | |||
|
112 | self.assertTrue(missanc.hasbases()) | |||
|
113 | self.assertEqual(missanc.missingancestors([3]), [2, 3]) | |||
|
114 | missanc.addbases({2}) | |||
|
115 | self.assertEqual(set(missanc.bases()), {1, 2}) | |||
|
116 | self.assertEqual(missanc.missingancestors([3]), [3]) | |||
|
117 | ||||
|
118 | def testmissingancestorsremove(self): | |||
|
119 | idx = self.parseindex() | |||
|
120 | missanc = MissingAncestors(idx, [1]) | |||
|
121 | revs = {0, 1, 2, 3} | |||
|
122 | missanc.removeancestorsfrom(revs) | |||
|
123 | self.assertEqual(revs, {2, 3}) | |||
|
124 | ||||
108 | def testrefcount(self): |
|
125 | def testrefcount(self): | |
109 | idx = self.parseindex() |
|
126 | idx = self.parseindex() | |
110 | start_count = sys.getrefcount(idx) |
|
127 | start_count = sys.getrefcount(idx) |
General Comments 0
You need to be logged in to leave comments.
Login now