##// END OF EJS Templates
rust-pyo3: exposition of AncestorsIterator...
Georges Racinet -
r53428:4c9e3198 default
parent child Browse files
Show More
@@ -0,0 +1,87
1 // ancestors.rs
2 //
3 // Copyright 2024 Georges Racinet <georges.racinet@cloudcrane.io>
4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
7
8 //! Bindings for the `hg::ancestors` module provided by the
9 //! `hg-core` crate. From Python, this will be seen as `pyo3_rustext.ancestor`
10 //! and can be used as replacement for the the pure `ancestor` Python module.
11 use cpython::UnsafePyLeaked;
12 use pyo3::prelude::*;
13
14 use std::sync::RwLock;
15
16 use vcsgraph::lazy_ancestors::AncestorsIterator as VCGAncestorsIterator;
17
18 use crate::convert_cpython::{
19 proxy_index_extract, proxy_index_py_leak, py_leaked_borrow_mut,
20 py_leaked_or_map_err,
21 };
22 use crate::exceptions::{map_lock_error, GraphError};
23 use crate::revision::{rev_pyiter_collect, PyRevision};
24 use crate::util::new_submodule;
25 use rusthg::revlog::PySharedIndex;
26
27 #[pyclass]
28 struct AncestorsIterator {
29 inner: RwLock<UnsafePyLeaked<VCGAncestorsIterator<PySharedIndex>>>,
30 }
31
32 #[pymethods]
33 impl AncestorsIterator {
34 #[new]
35 fn new(
36 index_proxy: &Bound<'_, PyAny>,
37 initrevs: &Bound<'_, PyAny>,
38 stoprev: PyRevision,
39 inclusive: bool,
40 ) -> PyResult<Self> {
41 // Safety: we don't leak the "faked" reference out of
42 // `UnsafePyLeaked`
43 let initvec: Vec<_> = {
44 let borrowed_idx = unsafe { proxy_index_extract(index_proxy)? };
45 rev_pyiter_collect(initrevs, borrowed_idx)?
46 };
47 let (py, leaked_idx) = proxy_index_py_leak(index_proxy)?;
48 let res_ait = unsafe {
49 leaked_idx.map(py, |idx| {
50 VCGAncestorsIterator::new(
51 idx,
52 initvec.into_iter().map(|r| r.0),
53 stoprev.0,
54 inclusive,
55 )
56 })
57 };
58 let ait =
59 py_leaked_or_map_err(py, res_ait, GraphError::from_vcsgraph)?;
60 let inner = ait.into();
61 Ok(Self { inner })
62 }
63
64 fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
65 slf
66 }
67
68 fn __next__(slf: PyRefMut<'_, Self>) -> PyResult<Option<PyRevision>> {
69 let mut leaked = slf.inner.write().map_err(map_lock_error)?;
70 // Safety: we don't leak the inner 'static ref out of UnsafePyLeaked
71 let mut inner = unsafe { py_leaked_borrow_mut(&slf, &mut leaked)? };
72 match inner.next() {
73 Some(Err(e)) => Err(GraphError::from_vcsgraph(e)),
74 None => Ok(None),
75 Some(Ok(r)) => Ok(Some(PyRevision(r))),
76 }
77 }
78 }
79
80 pub fn init_module<'py>(
81 py: Python<'py>,
82 package: &str,
83 ) -> PyResult<Bound<'py, PyModule>> {
84 let m = new_submodule(py, package, "ancestor")?;
85 m.add_class::<AncestorsIterator>()?;
86 Ok(m)
87 }
@@ -263,7 +263,6 pub(crate) unsafe fn py_leaked_borrow_mu
263 /// This would spare users of the `cpython` crate the additional `unsafe` deref
263 /// This would spare users of the `cpython` crate the additional `unsafe` deref
264 /// to inspect the error and return it outside `UnsafePyLeaked`, and the
264 /// to inspect the error and return it outside `UnsafePyLeaked`, and the
265 /// subsequent unwrapping that this function performs.
265 /// subsequent unwrapping that this function performs.
266 #[allow(dead_code)]
267 pub(crate) fn py_leaked_or_map_err<T, E: std::fmt::Debug + Copy>(
266 pub(crate) fn py_leaked_or_map_err<T, E: std::fmt::Debug + Copy>(
268 py: cpython::Python,
267 py: cpython::Python,
269 leaked: cpython::UnsafePyLeaked<Result<T, E>>,
268 leaked: cpython::UnsafePyLeaked<Result<T, E>>,
@@ -1,4 +1,4
1 use pyo3::exceptions::PyValueError;
1 use pyo3::exceptions::{PyRuntimeError, PyValueError};
2 use pyo3::import_exception;
2 use pyo3::import_exception;
3 use pyo3::{create_exception, PyErr};
3 use pyo3::{create_exception, PyErr};
4
4
@@ -32,3 +32,7 impl GraphError {
32 }
32 }
33 }
33 }
34 }
34 }
35
36 pub fn map_lock_error<T>(e: std::sync::PoisonError<T>) -> PyErr {
37 PyRuntimeError::new_err(format!("In Rust PyO3 bindings: {e}"))
38 }
@@ -1,5 +1,6
1 use pyo3::prelude::*;
1 use pyo3::prelude::*;
2
2
3 mod ancestors;
3 mod convert_cpython;
4 mod convert_cpython;
4 mod dagops;
5 mod dagops;
5 mod exceptions;
6 mod exceptions;
@@ -17,6 +18,7 fn pyo3_rustext(py: Python<'_>, m: &Boun
17 let name: String = m.getattr("__name__")?.extract()?;
18 let name: String = m.getattr("__name__")?.extract()?;
18 let dotted_name = format!("mercurial.{}", name);
19 let dotted_name = format!("mercurial.{}", name);
19
20
21 m.add_submodule(&ancestors::init_module(py, &dotted_name)?)?;
20 m.add_submodule(&dagops::init_module(py, &dotted_name)?)?;
22 m.add_submodule(&dagops::init_module(py, &dotted_name)?)?;
21 m.add("GraphError", py.get_type::<exceptions::GraphError>())?;
23 m.add("GraphError", py.get_type::<exceptions::GraphError>())?;
22 Ok(())
24 Ok(())
General Comments 0
You need to be logged in to leave comments. Login now