Show More
@@ -10,9 +10,12 | |||
|
10 | 10 | //! and can be used as replacement for the the pure `ancestor` Python module. |
|
11 | 11 | use cpython::UnsafePyLeaked; |
|
12 | 12 | use pyo3::prelude::*; |
|
13 | use pyo3::types::PyTuple; | |
|
13 | 14 | |
|
15 | use std::collections::HashSet; | |
|
14 | 16 | use std::sync::RwLock; |
|
15 | 17 | |
|
18 | use hg::MissingAncestors as CoreMissing; | |
|
16 | 19 | use vcsgraph::lazy_ancestors::{ |
|
17 | 20 | AncestorsIterator as VCGAncestorsIterator, |
|
18 | 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 | 283 | pub fn init_module<'py>( |
|
157 | 284 | py: Python<'py>, |
|
158 | 285 | package: &str, |
@@ -160,5 +287,6 pub fn init_module<'py>( | |||
|
160 | 287 | let m = new_submodule(py, package, "ancestor")?; |
|
161 | 288 | m.add_class::<AncestorsIterator>()?; |
|
162 | 289 | m.add_class::<LazyAncestors>()?; |
|
290 | m.add_class::<MissingAncestors>()?; | |
|
163 | 291 | Ok(m) |
|
164 | 292 | } |
@@ -172,12 +172,6 class RustAncestorsTestMixin: | |||
|
172 | 172 | idx = self.parserustindex() |
|
173 | 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 | 175 | def testmissingancestors(self): |
|
182 | 176 | MissingAncestors = self.ancestors_mod().MissingAncestors |
|
183 | 177 | |
@@ -200,6 +194,12 class RustCPythonAncestorsTest( | |||
|
200 | 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 | 203 | class PyO3AncestorsTest( |
|
204 | 204 | revlogtesting.RustRevlogBasedTestBase, RustAncestorsTestMixin |
|
205 | 205 | ): |
General Comments 0
You need to be logged in to leave comments.
Login now