##// END OF EJS Templates
rust-pyo3: MissingAncestors
Georges Racinet -
r53432:507fec66 default
parent child Browse files
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