##// END OF EJS Templates
unstable: do not consider internal phases when computing unstable...
unstable: do not consider internal phases when computing unstable The revisions that are not part of the "working" set by other means should not be considered for the evolution related computation. This impact the test introduced in 5f9af8422b31 as this is actually a more semantic fix of the issue.

File last commit:

r51872:4c5f6e95 default
r52017:80bda425 default
Show More
ancestors.rs
279 lines | 9.3 KiB | application/rls-services+xml | RustLexer
Georges Racinet
rust-cpython: start cpython crate bindings...
r41001 // ancestors.rs
//
// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //! Bindings for the `hg::ancestors` module provided by the
Georges Racinet
rust-cpython: start cpython crate bindings...
r41001 //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //! and can be used as replacement for the the pure `ancestor` Python module.
//!
//! # Classes visible from Python:
//! - [`LazyAncestors`] is the Rust implementation of
Georges Racinet
rust-cpython: set conversion for MissingAncestors.bases()...
r41279 //! `mercurial.ancestor.lazyancestors`. The only difference is that it is
//! instantiated with a C `parsers.index` instance instead of a parents
//! function.
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //!
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 //! - [`MissingAncestors`] is the Rust implementation of
//! `mercurial.ancestor.incrementalmissingancestors`.
//!
//! API differences:
//! + it is instantiated with a C `parsers.index`
//! instance instead of a parents function.
//! + `MissingAncestors.bases` is a method returning a tuple instead of
//! a set-valued attribute. We could return a Python set easily if our
//! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165)
//! is accepted.
//!
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //! - [`AncestorsIterator`] is the Rust counterpart of the
Georges Racinet
rust-cpython: set conversion for MissingAncestors.bases()...
r41279 //! `ancestor._lazyancestorsiter` Python generator. From Python, instances of
//! this should be mainly obtained by calling `iter()` on a [`LazyAncestors`]
//! instance.
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //!
//! [`LazyAncestors`]: struct.LazyAncestors.html
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 //! [`MissingAncestors`]: struct.MissingAncestors.html
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
rust-index: add a function to convert PyObject index for hg-core...
r44398 use crate::revlog::pyindex_to_graph;
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 use crate::PyRevision;
Raphaël Gomès
rust: switch hg-core and hg-cpython to rust 2018 edition...
r42828 use crate::{
Georges Racinet
rust-cpython: removed now useless py_set() conversion...
r43563 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
Raphaël Gomès
rust: switch hg-core and hg-cpython to rust 2018 edition...
r42828 };
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 use cpython::{
Georges Racinet
rust-cpython: set conversion for MissingAncestors.bases()...
r41279 ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, PyResult,
Georges Racinet
rust-cpython: moved py_set() utility to conversion module...
r41842 Python, PythonObject, ToPyObject,
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 };
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 use hg::MissingAncestors as CoreMissing;
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 use hg::Revision;
use std::cell::RefCell;
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 use std::collections::HashSet;
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 use vcsgraph::lazy_ancestors::{
AncestorsIterator as VCGAncestorsIterator,
LazyAncestors as VCGLazyAncestors,
};
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 py_class!(pub class AncestorsIterator |py| {
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 data inner: RefCell<Box<VCGAncestorsIterator<Index>>>;
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def __next__(&self) -> PyResult<Option<PyRevision>> {
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 match self.inner(py).borrow_mut().next() {
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 Some(Err(e)) => Err(GraphError::pynew_from_vcsgraph(py, e)),
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 None => Ok(None),
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 Some(Ok(r)) => Ok(Some(PyRevision(r))),
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 }
}
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def __contains__(&self, rev: PyRevision) -> PyResult<bool> {
self.inner(py).borrow_mut().contains(rev.0)
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 }
def __iter__(&self) -> PyResult<Self> {
Ok(self.clone_ref(py))
}
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def __new__(
_cls,
index: PyObject,
initrevs: PyObject,
stoprev: PyRevision,
inclusive: bool
) -> PyResult<AncestorsIterator> {
let index = pyindex_to_graph(py, index)?;
let initvec: Vec<_> = rev_pyiter_collect(py, &initrevs, &index)?;
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 let ait = VCGAncestorsIterator::new(
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 index,
initvec.into_iter().map(|r| r.0),
stoprev.0,
Georges Racinet
rust-cpython: style consistency leftovers...
r41222 inclusive,
)
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 AncestorsIterator::from_inner(py, ait)
}
});
impl AncestorsIterator {
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 pub fn from_inner(
py: Python,
ait: VCGAncestorsIterator<Index>,
) -> PyResult<Self> {
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 Self::create_instance(py, RefCell::new(Box::new(ait)))
}
}
Georges Racinet
rust-cpython: start cpython crate bindings...
r41001
Georges Racinet
rust-cpython: rustdoc improvements...
r41220 py_class!(pub class LazyAncestors |py| {
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 data inner: RefCell<Box<VCGLazyAncestors<Index>>>;
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def __contains__(&self, rev: PyRevision) -> PyResult<bool> {
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 self.inner(py)
.borrow_mut()
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 .contains(rev.0)
pacien
hg-cpython: use ancestor iterator impls from vcsgraph...
r49350 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 }
def __iter__(&self) -> PyResult<AncestorsIterator> {
AncestorsIterator::from_inner(py, self.inner(py).borrow().iter())
}
def __bool__(&self) -> PyResult<bool> {
Ok(!self.inner(py).borrow().is_empty())
}
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def __new__(
_cls,
index: PyObject,
initrevs: PyObject,
stoprev: PyRevision,
inclusive: bool
) -> PyResult<Self> {
let index = pyindex_to_graph(py, index)?;
let initvec: Vec<_> = rev_pyiter_collect(py, &initrevs, &index)?;
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149
let lazy =
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 VCGLazyAncestors::new(
index,
initvec.into_iter().map(|r| r.0),
stoprev.0,
inclusive
)
.map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149
Self::create_instance(py, RefCell::new(Box::new(lazy)))
}
});
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 py_class!(pub class MissingAncestors |py| {
data inner: RefCell<Box<CoreMissing<Index>>>;
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 data index: RefCell<Index>;
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224
Raphaël Gomès
rust-format: cleanup ancestors.rs to make rustfmt happy...
r44927 def __new__(
_cls,
index: PyObject,
bases: PyObject
)
-> PyResult<MissingAncestors> {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let index = pyindex_to_graph(py, index)?;
let bases_vec: Vec<_> = rev_pyiter_collect(py, &bases, &index)?;
let inner = CoreMissing::new(index.clone_ref(py), bases_vec);
MissingAncestors::create_instance(
py,
RefCell::new(Box::new(inner)),
RefCell::new(index)
)
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 }
def hasbases(&self) -> PyResult<bool> {
Ok(self.inner(py).borrow().has_bases())
}
def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let index = self.index(py).borrow();
let bases_vec: Vec<_> = rev_pyiter_collect(py, &bases, &*index)?;
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 let mut inner = self.inner(py).borrow_mut();
inner.add_bases(bases_vec);
// cpython doc has examples with PyResult<()> but this gives me
// the trait `cpython::ToPyObject` is not implemented for `()`
// so let's return an explicit None
Ok(py.None())
}
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def bases(&self) -> PyResult<HashSet<PyRevision>> {
Ok(
self.inner(py)
.borrow()
.get_bases()
.iter()
.map(|r| PyRevision(r.0))
.collect()
)
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 }
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def basesheads(&self) -> PyResult<HashSet<PyRevision>> {
Georges Racinet
rust: MissingAncestors.basesheads()...
r41282 let inner = self.inner(py).borrow();
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 Ok(
inner
.bases_heads()
.map_err(|e| GraphError::pynew(py, e))?
.into_iter()
.map(|r| PyRevision(r.0))
.collect()
)
Georges Racinet
rust: MissingAncestors.basesheads()...
r41282 }
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let index = self.index(py).borrow();
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 // this is very lame: we convert to a Rust set, update it in place
// and then convert back to Python, only to have Python remove the
// excess (thankfully, Python is happy with a list or even an iterator)
// Leads to improve this:
// - have the CoreMissing instead do something emit revisions to
// discard
// - define a trait for sets of revisions in the core and implement
// it for a Python set rewrapped with the GIL marker
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(
py, &revs, &*index
)?;
let mut inner = self.inner(py).borrow_mut();
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 inner.remove_ancestors_from(&mut revs_pyset)
.map_err(|e| GraphError::pynew(py, e))?;
// convert as Python list
let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
revs_pyset.len());
for rev in revs_pyset {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 remaining_pyint_vec.push(
PyRevision(rev.0).to_py_object(py).into_object()
);
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 }
let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
}
def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let index = self.index(py).borrow();
let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs, &*index)?;
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 let mut inner = self.inner(py).borrow_mut();
let missing_vec = match inner.missing_ancestors(revs_vec) {
Ok(missing) => missing,
Err(e) => {
return Err(GraphError::pynew(py, e));
}
};
// convert as Python list
let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
missing_vec.len());
for rev in missing_vec {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 missing_pyint_vec.push(
PyRevision(rev.0).to_py_object(py).into_object()
);
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 }
Ok(PyList::new(py, missing_pyint_vec.as_slice()))
}
});
/// Create the module, with __package__ given from parent
Georges Racinet
rust-cpython: start cpython crate bindings...
r41001 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
let dotted_name = &format!("{}.ancestor", package);
let m = PyModule::new(py, dotted_name)?;
m.add(py, "__package__", package)?;
m.add(
py,
"__doc__",
"Generic DAG ancestor algorithms - Rust implementation",
)?;
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 m.add_class::<AncestorsIterator>(py)?;
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 m.add_class::<LazyAncestors>(py)?;
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 m.add_class::<MissingAncestors>(py)?;
Georges Racinet
rust-cpython: start cpython crate bindings...
r41001
let sys = PyModule::import(py, "sys")?;
let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
sys_modules.set_item(py, dotted_name, &m)?;
// Example C code (see pyexpat.c and import.c) will "give away the
// reference", but we won't because it will be consumed once the
// Rust PyObject is dropped.
Ok(m)
}