Show More
@@ -13,8 +13,8 b' use cpython::{' | |||||
13 | }; |
|
13 | }; | |
14 | use exceptions::GraphError; |
|
14 | use exceptions::GraphError; | |
15 | use hg; |
|
15 | use hg; | |
16 | use hg::AncestorsIterator as CoreIterator; |
|
|||
17 | use hg::Revision; |
|
16 | use hg::Revision; | |
|
17 | use hg::{AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy}; | |||
18 | use std::cell::RefCell; |
|
18 | use std::cell::RefCell; | |
19 |
|
19 | |||
20 | /// Utility function to convert a Python iterable into a Vec<Revision> |
|
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 | /// Create the module, with __package__ given from parent |
|
104 | /// Create the module, with __package__ given from parent | |
74 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
105 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | |
75 | let dotted_name = &format!("{}.ancestor", package); |
|
106 | let dotted_name = &format!("{}.ancestor", package); | |
@@ -81,6 +112,7 b' pub fn init_module(py: Python, package: ' | |||||
81 | "Generic DAG ancestor algorithms - Rust implementation", |
|
112 | "Generic DAG ancestor algorithms - Rust implementation", | |
82 | )?; |
|
113 | )?; | |
83 | m.add_class::<AncestorsIterator>(py)?; |
|
114 | m.add_class::<AncestorsIterator>(py)?; | |
|
115 | m.add_class::<LazyAncestors>(py)?; | |||
84 |
|
116 | |||
85 | let sys = PyModule::import(py, "sys")?; |
|
117 | let sys = PyModule::import(py, "sys")?; | |
86 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
118 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
@@ -9,7 +9,10 b' except ImportError:' | |||||
9 | rustext = None |
|
9 | rustext = None | |
10 | else: |
|
10 | 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, | |||
|
14 | LazyAncestors | |||
|
15 | ) | |||
13 |
|
16 | |||
14 | try: |
|
17 | try: | |
15 | from mercurial.cext import parsers as cparsers |
|
18 | from mercurial.cext import parsers as cparsers | |
@@ -71,6 +74,37 b' class rustancestorstest(unittest.TestCas' | |||||
71 | ait = AncestorsIterator(idx, [3], 0, False) |
|
74 | ait = AncestorsIterator(idx, [3], 0, False) | |
72 | self.assertEqual([r for r in ait], [2, 1, 0]) |
|
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 | def testrefcount(self): |
|
108 | def testrefcount(self): | |
75 | idx = self.parseindex() |
|
109 | idx = self.parseindex() | |
76 | start_count = sys.getrefcount(idx) |
|
110 | start_count = sys.getrefcount(idx) | |
@@ -87,7 +121,7 b' class rustancestorstest(unittest.TestCas' | |||||
87 | # and removing ref to the index after iterator init is no issue |
|
121 | # and removing ref to the index after iterator init is no issue | |
88 | ait = AncestorsIterator(idx, [3], 0, True) |
|
122 | ait = AncestorsIterator(idx, [3], 0, True) | |
89 | del idx |
|
123 | del idx | |
90 |
self.assertEqual( |
|
124 | self.assertEqual(list(ait), [3, 2, 1, 0]) | |
91 |
|
125 | |||
92 | def testgrapherror(self): |
|
126 | def testgrapherror(self): | |
93 | data = (data_non_inlined[:64 + 27] + |
|
127 | data = (data_non_inlined[:64 + 27] + |
General Comments 0
You need to be logged in to leave comments.
Login now