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