Mercurial > public > mercurial-scm > hg
changeset 52837:01aff9437828
rust-pyo3: translate discovery module from hg-cpython
This is a small module and the only one left that needs to know about the
index. Building the conversion machinery would have taken longer and have
been more dangerous.
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Mon, 06 Jan 2025 01:14:52 +0100 |
parents | 9749a97d3cfb |
children | e52dc683bf6b |
files | rust/hg-pyo3/src/discovery.rs rust/hg-pyo3/src/lib.rs |
diffstat | 2 files changed, 218 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-pyo3/src/discovery.rs Mon Jan 06 01:14:52 2025 +0100 @@ -0,0 +1,216 @@ +//! Discovery of common node sets +use std::collections::HashSet; + +use hg::{discovery::PartialDiscovery as CorePartialDiscovery, Revision}; +use pyo3::{ + intern, pyclass, pymethods, + types::{PyAnyMethods, PyDict, PyModule, PyModuleMethods, PyTuple}, + Bound, Py, PyAny, PyObject, PyResult, Python, +}; +use pyo3_sharedref::SharedByPyObject; + +use crate::{ + exceptions::GraphError, + revision::{rev_pyiter_collect, PyRevision}, + revlog::PySharedIndex, + utils::{new_submodule, py_rust_index_to_graph}, +}; + +#[pyclass] +struct PartialDiscovery { + inner: SharedByPyObject<CorePartialDiscovery<PySharedIndex>>, + idx: SharedByPyObject<PySharedIndex>, +} + +#[pymethods] +impl PartialDiscovery { + #[pyo3(signature = (repo, targetheads, respectsize, randomize=true))] + #[new] + fn new( + py: Python, + repo: &Bound<'_, PyAny>, + targetheads: &Bound<'_, PyAny>, + respectsize: bool, + randomize: bool, + ) -> PyResult<Self> { + let index = repo + .getattr(intern!(py, "changelog"))? + .getattr(intern!(py, "index"))?; + let cloned_index = py_rust_index_to_graph(&index.clone())?; + let index = py_rust_index_to_graph(&index)?; + + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let target_heads = { + let borrowed_idx = unsafe { index.try_borrow(py)? }; + rev_pyiter_collect(targetheads, &*borrowed_idx)? + }; + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let lazy_disco = unsafe { + index.map(py, |idx| { + CorePartialDiscovery::new( + idx, + target_heads, + respectsize, + randomize, + ) + }) + }; + Ok(Self { + inner: lazy_disco, + idx: cloned_index, + }) + } + + fn addcommons( + &mut self, + py: Python, + commons: &Bound<'_, PyAny>, + ) -> PyResult<PyObject> { + let commons = self.pyiter_to_vec(commons)?; + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let mut inner = unsafe { self.inner.try_borrow_mut(py)? }; + inner + .add_common_revisions(commons) + .map_err(GraphError::from_hg)?; + Ok(py.None()) + } + + fn addmissings( + &mut self, + py: Python, + missings: &Bound<'_, PyAny>, + ) -> PyResult<PyObject> { + let missings = self.pyiter_to_vec(missings)?; + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let mut inner = unsafe { self.inner.try_borrow_mut(py)? }; + inner + .add_missing_revisions(missings) + .map_err(GraphError::from_hg)?; + Ok(py.None()) + } + + fn addinfo( + &mut self, + py: Python, + sample: &Bound<'_, PyAny>, + ) -> PyResult<PyObject> { + let mut missing: Vec<Revision> = vec![]; + let mut common: Vec<Revision> = vec![]; + for info in sample.try_iter()? { + // info is a pair (Revision, bool) + let info = info?; + let info = info.downcast::<PyTuple>()?; + let rev: PyRevision = info.get_item(0)?.extract()?; + // This is fine since we're just using revisions as integers + // for the purposes of discovery + let rev = Revision(rev.0); + let known: bool = info.get_item(1)?.extract()?; + if known { + common.push(rev); + } else { + missing.push(rev); + } + } + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let mut inner = unsafe { self.inner.try_borrow_mut(py)? }; + inner + .add_common_revisions(common) + .map_err(GraphError::from_hg)?; + inner + .add_missing_revisions(missing) + .map_err(GraphError::from_hg)?; + Ok(py.None()) + } + + fn hasinfo(&self, py: Python<'_>) -> PyResult<bool> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let inner = unsafe { self.inner.try_borrow(py)? }; + Ok(inner.has_info()) + } + + fn iscomplete(&self, py: Python<'_>) -> PyResult<bool> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let inner = unsafe { self.inner.try_borrow(py)? }; + Ok(inner.is_complete()) + } + + fn stats(&self, py: Python<'_>) -> PyResult<Py<PyDict>> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let inner = unsafe { self.inner.try_borrow(py)? }; + let stats = inner.stats(); + let as_dict = PyDict::new(py); + as_dict.set_item("undecided", stats.undecided)?; + Ok(as_dict.unbind()) + } + + fn commonheads(&self, py: Python<'_>) -> PyResult<HashSet<PyRevision>> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let inner = unsafe { self.inner.try_borrow(py)? }; + let common_heads = + inner.common_heads().map_err(GraphError::from_hg)?; + Ok(common_heads.into_iter().map(Into::into).collect()) + } + + fn takefullsample( + &mut self, + py: Python, + _headrevs: &Bound<'_, PyAny>, + size: usize, + ) -> PyResult<Py<PyTuple>> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let mut inner = unsafe { self.inner.try_borrow_mut(py)? }; + let sample = + inner.take_full_sample(size).map_err(GraphError::from_hg)?; + let as_pyrevision = sample.into_iter().map(|rev| PyRevision(rev.0)); + Ok(PyTuple::new(py, as_pyrevision)?.unbind()) + } + + fn takequicksample( + &mut self, + py: Python, + headrevs: &Bound<'_, PyAny>, + size: usize, + ) -> PyResult<Py<PyTuple>> { + let revs = self.pyiter_to_vec(headrevs)?; + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let mut inner = unsafe { self.inner.try_borrow_mut(py)? }; + let sample = inner + .take_quick_sample(revs, size) + .map_err(GraphError::from_hg)?; + let as_pyrevision = sample.into_iter().map(|rev| PyRevision(rev.0)); + Ok(PyTuple::new(py, as_pyrevision)?.unbind()) + } +} + +impl PartialDiscovery { + /// Convert a Python iterator of revisions into a vector + fn pyiter_to_vec( + &self, + iter: &Bound<'_, PyAny>, + ) -> PyResult<Vec<Revision>> { + // Safety: we don't leak any reference derived form the "faked" one in + // `SharedByPyObject` + let index = unsafe { self.idx.try_borrow(iter.py())? }; + rev_pyiter_collect(iter, &*index) + } +} + +pub fn init_module<'py>( + py: Python<'py>, + package: &str, +) -> PyResult<Bound<'py, PyModule>> { + let m = new_submodule(py, package, "discovery")?; + m.add_class::<PartialDiscovery>()?; + Ok(m) +}
--- a/rust/hg-pyo3/src/lib.rs Sun Jan 05 23:33:24 2025 +0100 +++ b/rust/hg-pyo3/src/lib.rs Mon Jan 06 01:14:52 2025 +0100 @@ -3,6 +3,7 @@ mod ancestors; mod convert_cpython; mod dagops; +mod discovery; mod exceptions; mod node; mod revision; @@ -23,6 +24,7 @@ m.add_submodule(&ancestors::init_module(py, &dotted_name)?)?; m.add_submodule(&dagops::init_module(py, &dotted_name)?)?; + m.add_submodule(&discovery::init_module(py, &dotted_name)?)?; m.add_submodule(&revlog::init_module(py, &dotted_name)?)?; m.add("GraphError", py.get_type::<exceptions::GraphError>())?; Ok(())