##// END OF EJS Templates
rust-cpython: bindings for MissingAncestors...
Georges Racinet -
r41224:006c9ce4 default
parent child Browse files
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, PyResult, Python,
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