Show More
@@ -13,8 +13,8 b' use cpython::{' | |||
|
13 | 13 | }; |
|
14 | 14 | use exceptions::GraphError; |
|
15 | 15 | use hg; |
|
16 | use hg::AncestorsIterator as CoreIterator; | |
|
17 | 16 | use hg::Revision; |
|
17 | use hg::{AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy}; | |
|
18 | 18 | use std::cell::RefCell; |
|
19 | 19 | |
|
20 | 20 | /// Utility function to convert a Python iterable into a Vec<Revision> |
@@ -70,6 +70,37 b' impl AncestorsIterator {' | |||
|
70 | 70 | } |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | py_class!(class LazyAncestors |py| { | |
|
74 | data inner: RefCell<Box<CoreLazy<Index>>>; | |
|
75 | ||
|
76 | def __contains__(&self, rev: Revision) -> PyResult<bool> { | |
|
77 | self.inner(py) | |
|
78 | .borrow_mut() | |
|
79 | .contains(rev) | |
|
80 | .map_err(|e| GraphError::pynew(py, e)) | |
|
81 | } | |
|
82 | ||
|
83 | def __iter__(&self) -> PyResult<AncestorsIterator> { | |
|
84 | AncestorsIterator::from_inner(py, self.inner(py).borrow().iter()) | |
|
85 | } | |
|
86 | ||
|
87 | def __bool__(&self) -> PyResult<bool> { | |
|
88 | Ok(!self.inner(py).borrow().is_empty()) | |
|
89 | } | |
|
90 | ||
|
91 | def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision, | |
|
92 | inclusive: bool) -> PyResult<Self> { | |
|
93 | let initvec = reviter_to_revvec(py, initrevs)?; | |
|
94 | ||
|
95 | let lazy = | |
|
96 | CoreLazy::new(Index::new(py, index)?, initvec, stoprev, inclusive) | |
|
97 | .map_err(|e| GraphError::pynew(py, e))?; | |
|
98 | ||
|
99 | Self::create_instance(py, RefCell::new(Box::new(lazy))) | |
|
100 | } | |
|
101 | ||
|
102 | }); | |
|
103 | ||
|
73 | 104 | /// Create the module, with __package__ given from parent |
|
74 | 105 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
75 | 106 | let dotted_name = &format!("{}.ancestor", package); |
@@ -81,6 +112,7 b' pub fn init_module(py: Python, package: ' | |||
|
81 | 112 | "Generic DAG ancestor algorithms - Rust implementation", |
|
82 | 113 | )?; |
|
83 | 114 | m.add_class::<AncestorsIterator>(py)?; |
|
115 | m.add_class::<LazyAncestors>(py)?; | |
|
84 | 116 | |
|
85 | 117 | let sys = PyModule::import(py, "sys")?; |
|
86 | 118 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
@@ -9,7 +9,10 b' except ImportError:' | |||
|
9 | 9 | rustext = None |
|
10 | 10 | else: |
|
11 | 11 | # this would fail already without appropriate ancestor.__package__ |
|
12 |
from mercurial.rustext.ancestor import |
|
|
12 | from mercurial.rustext.ancestor import ( | |
|
13 | AncestorsIterator, | |
|
14 | LazyAncestors | |
|
15 | ) | |
|
13 | 16 | |
|
14 | 17 | try: |
|
15 | 18 | from mercurial.cext import parsers as cparsers |
@@ -71,6 +74,37 b' class rustancestorstest(unittest.TestCas' | |||
|
71 | 74 | ait = AncestorsIterator(idx, [3], 0, False) |
|
72 | 75 | self.assertEqual([r for r in ait], [2, 1, 0]) |
|
73 | 76 | |
|
77 | def testlazyancestors(self): | |
|
78 | idx = self.parseindex() | |
|
79 | start_count = sys.getrefcount(idx) # should be 2 (see Python doc) | |
|
80 | self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)}, | |
|
81 | {0: (-1, -1), | |
|
82 | 1: (0, -1), | |
|
83 | 2: (1, -1), | |
|
84 | 3: (2, -1)}) | |
|
85 | lazy = LazyAncestors(idx, [3], 0, True) | |
|
86 | # we have two more references to the index: | |
|
87 | # - in its inner iterator for __contains__ and __bool__ | |
|
88 | # - in the LazyAncestors instance itself (to spawn new iterators) | |
|
89 | self.assertEqual(sys.getrefcount(idx), start_count + 2) | |
|
90 | ||
|
91 | self.assertTrue(2 in lazy) | |
|
92 | self.assertTrue(bool(lazy)) | |
|
93 | self.assertEqual(list(lazy), [3, 2, 1, 0]) | |
|
94 | # a second time to validate that we spawn new iterators | |
|
95 | self.assertEqual(list(lazy), [3, 2, 1, 0]) | |
|
96 | ||
|
97 | # now let's watch the refcounts closer | |
|
98 | ait = iter(lazy) | |
|
99 | self.assertEqual(sys.getrefcount(idx), start_count + 3) | |
|
100 | del ait | |
|
101 | self.assertEqual(sys.getrefcount(idx), start_count + 2) | |
|
102 | del lazy | |
|
103 | self.assertEqual(sys.getrefcount(idx), start_count) | |
|
104 | ||
|
105 | # let's check bool for an empty one | |
|
106 | self.assertFalse(LazyAncestors(idx, [0], 0, False)) | |
|
107 | ||
|
74 | 108 | def testrefcount(self): |
|
75 | 109 | idx = self.parseindex() |
|
76 | 110 | start_count = sys.getrefcount(idx) |
@@ -87,7 +121,7 b' class rustancestorstest(unittest.TestCas' | |||
|
87 | 121 | # and removing ref to the index after iterator init is no issue |
|
88 | 122 | ait = AncestorsIterator(idx, [3], 0, True) |
|
89 | 123 | del idx |
|
90 |
self.assertEqual( |
|
|
124 | self.assertEqual(list(ait), [3, 2, 1, 0]) | |
|
91 | 125 | |
|
92 | 126 | def testgrapherror(self): |
|
93 | 127 | data = (data_non_inlined[:64 + 27] + |
General Comments 0
You need to be logged in to leave comments.
Login now