Show More
@@ -10,9 +10,12 | |||||
10 | //! and can be used as replacement for the the pure `ancestor` Python module. |
|
10 | //! and can be used as replacement for the the pure `ancestor` Python module. | |
11 | use cpython::UnsafePyLeaked; |
|
11 | use cpython::UnsafePyLeaked; | |
12 | use pyo3::prelude::*; |
|
12 | use pyo3::prelude::*; | |
|
13 | use pyo3::types::PyTuple; | |||
13 |
|
14 | |||
|
15 | use std::collections::HashSet; | |||
14 | use std::sync::RwLock; |
|
16 | use std::sync::RwLock; | |
15 |
|
17 | |||
|
18 | use hg::MissingAncestors as CoreMissing; | |||
16 | use vcsgraph::lazy_ancestors::{ |
|
19 | use vcsgraph::lazy_ancestors::{ | |
17 | AncestorsIterator as VCGAncestorsIterator, |
|
20 | AncestorsIterator as VCGAncestorsIterator, | |
18 | LazyAncestors as VCGLazyAncestors, |
|
21 | LazyAncestors as VCGLazyAncestors, | |
@@ -153,6 +156,130 impl LazyAncestors { | |||||
153 | } |
|
156 | } | |
154 | } |
|
157 | } | |
155 |
|
158 | |||
|
159 | #[pyclass] | |||
|
160 | struct MissingAncestors { | |||
|
161 | inner: RwLock<UnsafePyLeaked<CoreMissing<PySharedIndex>>>, | |||
|
162 | proxy_index: PyObject, | |||
|
163 | } | |||
|
164 | ||||
|
165 | #[pymethods] | |||
|
166 | impl MissingAncestors { | |||
|
167 | #[new] | |||
|
168 | fn new( | |||
|
169 | index_proxy: &Bound<'_, PyAny>, | |||
|
170 | bases: &Bound<'_, PyAny>, | |||
|
171 | ) -> PyResult<Self> { | |||
|
172 | let cloned_proxy = index_proxy.clone().unbind(); | |||
|
173 | let bases_vec: Vec<_> = | |||
|
174 | rev_pyiter_collect_with_py_index(bases, index_proxy)?; | |||
|
175 | let (py, leaked_idx) = proxy_index_py_leak(index_proxy)?; | |||
|
176 | ||||
|
177 | // Safety: we don't leak the "faked" reference out of | |||
|
178 | // `UnsafePyLeaked` | |||
|
179 | let inner = unsafe { | |||
|
180 | leaked_idx.map(py, |idx| CoreMissing::new(idx, bases_vec)) | |||
|
181 | }; | |||
|
182 | Ok(Self { | |||
|
183 | inner: inner.into(), | |||
|
184 | proxy_index: cloned_proxy, | |||
|
185 | }) | |||
|
186 | } | |||
|
187 | ||||
|
188 | fn hasbases(slf: PyRef<'_, Self>) -> PyResult<bool> { | |||
|
189 | let leaked = slf.inner.read().map_err(map_lock_error)?; | |||
|
190 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
191 | let inner = unsafe { py_leaked_borrow(&slf, &leaked) }?; | |||
|
192 | Ok(inner.has_bases()) | |||
|
193 | } | |||
|
194 | ||||
|
195 | fn addbases( | |||
|
196 | slf: PyRefMut<'_, Self>, | |||
|
197 | bases: &Bound<'_, PyAny>, | |||
|
198 | ) -> PyResult<()> { | |||
|
199 | let index_proxy = slf.proxy_index.bind(slf.py()); | |||
|
200 | let bases_vec: Vec<_> = | |||
|
201 | rev_pyiter_collect_with_py_index(bases, index_proxy)?; | |||
|
202 | ||||
|
203 | let mut leaked = slf.inner.write().map_err(map_lock_error)?; | |||
|
204 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
205 | let mut inner = unsafe { py_leaked_borrow_mut(&slf, &mut leaked) }?; | |||
|
206 | inner.add_bases(bases_vec); | |||
|
207 | Ok(()) | |||
|
208 | } | |||
|
209 | ||||
|
210 | fn bases(slf: PyRef<'_, Self>) -> PyResult<HashSet<PyRevision>> { | |||
|
211 | let leaked = slf.inner.read().map_err(map_lock_error)?; | |||
|
212 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
213 | let inner = unsafe { py_leaked_borrow(&slf, &leaked) }?; | |||
|
214 | Ok(inner.get_bases().iter().map(|r| PyRevision(r.0)).collect()) | |||
|
215 | } | |||
|
216 | ||||
|
217 | fn basesheads(slf: PyRef<'_, Self>) -> PyResult<HashSet<PyRevision>> { | |||
|
218 | let leaked = slf.inner.read().map_err(map_lock_error)?; | |||
|
219 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
220 | let inner = unsafe { py_leaked_borrow(&slf, &leaked) }?; | |||
|
221 | Ok(inner | |||
|
222 | .bases_heads() | |||
|
223 | .map_err(GraphError::from_hg)? | |||
|
224 | .iter() | |||
|
225 | .map(|r| PyRevision(r.0)) | |||
|
226 | .collect()) | |||
|
227 | } | |||
|
228 | ||||
|
229 | fn removeancestorsfrom( | |||
|
230 | slf: PyRef<'_, Self>, | |||
|
231 | revs: &Bound<'_, PyAny>, | |||
|
232 | ) -> PyResult<()> { | |||
|
233 | // Original comment from hg-cpython: | |||
|
234 | // this is very lame: we convert to a Rust set, update it in place | |||
|
235 | // and then convert back to Python, only to have Python remove the | |||
|
236 | // excess (thankfully, Python is happy with a list or even an | |||
|
237 | // iterator) | |||
|
238 | // Leads to improve this: | |||
|
239 | // - have the CoreMissing instead do something emit revisions to | |||
|
240 | // discard | |||
|
241 | // - define a trait for sets of revisions in the core and implement | |||
|
242 | // it for a Python set rewrapped with the GIL marker | |||
|
243 | // PyO3 additional comment: the trait approach would probably be | |||
|
244 | // simpler because we can implement it without a Py wrappper, just | |||
|
245 | // on &Bound<'py, PySet> | |||
|
246 | let index_proxy = slf.proxy_index.bind(slf.py()); | |||
|
247 | let mut revs_set: HashSet<_> = | |||
|
248 | rev_pyiter_collect_with_py_index(revs, index_proxy)?; | |||
|
249 | ||||
|
250 | let mut leaked = slf.inner.write().map_err(map_lock_error)?; | |||
|
251 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
252 | let mut inner = unsafe { py_leaked_borrow_mut(&slf, &mut leaked) }?; | |||
|
253 | ||||
|
254 | inner | |||
|
255 | .remove_ancestors_from(&mut revs_set) | |||
|
256 | .map_err(GraphError::from_hg)?; | |||
|
257 | // convert as Python tuple and discard from original `revs` | |||
|
258 | let remaining_tuple = | |||
|
259 | PyTuple::new(slf.py(), revs_set.iter().map(|r| PyRevision(r.0)))?; | |||
|
260 | revs.call_method("intersection_update", (remaining_tuple,), None)?; | |||
|
261 | Ok(()) | |||
|
262 | } | |||
|
263 | ||||
|
264 | fn missingancestors( | |||
|
265 | slf: PyRefMut<'_, Self>, | |||
|
266 | bases: &Bound<'_, PyAny>, | |||
|
267 | ) -> PyResult<Vec<PyRevision>> { | |||
|
268 | let index_proxy = slf.proxy_index.bind(slf.py()); | |||
|
269 | let revs_vec: Vec<_> = | |||
|
270 | rev_pyiter_collect_with_py_index(bases, index_proxy)?; | |||
|
271 | ||||
|
272 | let mut leaked = slf.inner.write().map_err(map_lock_error)?; | |||
|
273 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |||
|
274 | let mut inner = unsafe { py_leaked_borrow_mut(&slf, &mut leaked) }?; | |||
|
275 | ||||
|
276 | let missing_vec = inner | |||
|
277 | .missing_ancestors(revs_vec) | |||
|
278 | .map_err(GraphError::from_hg)?; | |||
|
279 | Ok(missing_vec.iter().map(|r| PyRevision(r.0)).collect()) | |||
|
280 | } | |||
|
281 | } | |||
|
282 | ||||
156 | pub fn init_module<'py>( |
|
283 | pub fn init_module<'py>( | |
157 | py: Python<'py>, |
|
284 | py: Python<'py>, | |
158 | package: &str, |
|
285 | package: &str, | |
@@ -160,5 +287,6 pub fn init_module<'py>( | |||||
160 | let m = new_submodule(py, package, "ancestor")?; |
|
287 | let m = new_submodule(py, package, "ancestor")?; | |
161 | m.add_class::<AncestorsIterator>()?; |
|
288 | m.add_class::<AncestorsIterator>()?; | |
162 | m.add_class::<LazyAncestors>()?; |
|
289 | m.add_class::<LazyAncestors>()?; | |
|
290 | m.add_class::<MissingAncestors>()?; | |||
163 | Ok(m) |
|
291 | Ok(m) | |
164 | } |
|
292 | } |
@@ -172,12 +172,6 class RustAncestorsTestMixin: | |||||
172 | idx = self.parserustindex() |
|
172 | idx = self.parserustindex() | |
173 | self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3}) |
|
173 | self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3}) | |
174 |
|
174 | |||
175 |
|
||||
176 | class RustCPythonAncestorsTest( |
|
|||
177 | revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin |
|
|||
178 | ): |
|
|||
179 | rustext_pkg = rustext |
|
|||
180 |
|
||||
181 | def testmissingancestors(self): |
|
175 | def testmissingancestors(self): | |
182 | MissingAncestors = self.ancestors_mod().MissingAncestors |
|
176 | MissingAncestors = self.ancestors_mod().MissingAncestors | |
183 |
|
177 | |||
@@ -200,6 +194,12 class RustCPythonAncestorsTest( | |||||
200 | self.assertEqual(revs, {2, 3}) |
|
194 | self.assertEqual(revs, {2, 3}) | |
201 |
|
195 | |||
202 |
|
196 | |||
|
197 | class RustCPythonAncestorsTest( | |||
|
198 | revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin | |||
|
199 | ): | |||
|
200 | rustext_pkg = rustext | |||
|
201 | ||||
|
202 | ||||
203 | class PyO3AncestorsTest( |
|
203 | class PyO3AncestorsTest( | |
204 | revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin |
|
204 | revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin | |
205 | ): |
|
205 | ): |
General Comments 0
You need to be logged in to leave comments.
Login now