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 | 263 | /// This would spare users of the `cpython` crate the additional `unsafe` deref |
|
264 | 264 | /// to inspect the error and return it outside `UnsafePyLeaked`, and the |
|
265 | 265 | /// subsequent unwrapping that this function performs. |
|
266 | #[allow(dead_code)] | |
|
267 | 266 | pub(crate) fn py_leaked_or_map_err<T, E: std::fmt::Debug + Copy>( |
|
268 | 267 | py: cpython::Python, |
|
269 | 268 | leaked: cpython::UnsafePyLeaked<Result<T, E>>, |
@@ -1,4 +1,4 | |||
|
1 | use pyo3::exceptions::PyValueError; | |
|
1 | use pyo3::exceptions::{PyRuntimeError, PyValueError}; | |
|
2 | 2 | use pyo3::import_exception; |
|
3 | 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 | 1 | use pyo3::prelude::*; |
|
2 | 2 | |
|
3 | mod ancestors; | |
|
3 | 4 | mod convert_cpython; |
|
4 | 5 | mod dagops; |
|
5 | 6 | mod exceptions; |
@@ -17,6 +18,7 fn pyo3_rustext(py: Python<'_>, m: &Boun | |||
|
17 | 18 | let name: String = m.getattr("__name__")?.extract()?; |
|
18 | 19 | let dotted_name = format!("mercurial.{}", name); |
|
19 | 20 | |
|
21 | m.add_submodule(&ancestors::init_module(py, &dotted_name)?)?; | |
|
20 | 22 | m.add_submodule(&dagops::init_module(py, &dotted_name)?)?; |
|
21 | 23 | m.add("GraphError", py.get_type::<exceptions::GraphError>())?; |
|
22 | 24 | Ok(()) |
General Comments 0
You need to be logged in to leave comments.
Login now