##// END OF EJS Templates
matchers: use correct method for finding index in vector...
matchers: use correct method for finding index in vector The path matcher has an optimization for when all paths are `rootfilesin:`. This optimization exists in both Python and Rust. However, the Rust implementation currently has a bug that makes it fail in most cases. The bug is that it `rfind()` where it was clearly intended to use `rposition()`. This patch fixes that and adds a test.

File last commit:

r52148:24d32981 default
r52167:bec6e9c1 default
Show More
discovery.rs
277 lines | 9.6 KiB | application/rls-services+xml | RustLexer
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 // discovery.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.
//! Bindings for the `hg::discovery` module provided by the
//! `hg-core` crate. From Python, this will be seen as `rustext.discovery`
//!
//! # Classes visible from Python:
Georges Racinet
rust: fix cargo doc for hg-cpython
r52078 //! - [`PartialDiscovery`] is the Rust implementation of
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 //! `mercurial.setdiscovery.partialdiscovery`.
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-index: using `hg::index::Index` in discovery...
r52138 conversion::rev_pyiter_collect, exceptions::GraphError,
revlog::PySharedIndex,
Raphaël Gomès
rust: switch hg-core and hg-cpython to rust 2018 edition...
r42828 };
Georges Racinet
rust-discovery: implementing and exposing stats()...
r42357 use cpython::{
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, PyTuple,
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 Python, PythonObject, ToPyObject, UnsafePyLeaked,
Georges Racinet
rust-discovery: implementing and exposing stats()...
r42357 };
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 use hg::discovery::PartialDiscovery as CorePartialDiscovery;
use hg::Revision;
Georges Racinet
rust-cpython: removed now useless py_set() conversion...
r43563 use std::collections::HashSet;
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356
use std::cell::RefCell;
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 use crate::revlog::py_rust_index_to_graph;
rust-index: add a function to convert PyObject index for hg-core...
r44398
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 py_class!(pub class PartialDiscovery |py| {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 data inner: RefCell<UnsafePyLeaked<CorePartialDiscovery<PySharedIndex>>>;
data index: RefCell<UnsafePyLeaked<PySharedIndex>>;
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356
Georges Racinet
rust-discovery: accept the new 'respectsize' init arg...
r42963 // `_respectsize` is currently only here to replicate the Python API and
// will be used in future patches inside methods that are yet to be
// implemented.
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 def __new__(
_cls,
Georges Racinet
rust-discovery: read the index from a repo passed at init...
r42964 repo: PyObject,
Georges Racinet
rust-discovery: accept the new 'respectsize' init arg...
r42963 targetheads: PyObject,
Georges Racinet
rust-discovery: optionally don't randomize at all, for tests...
r42968 respectsize: bool,
randomize: bool = true
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 ) -> PyResult<PartialDiscovery> {
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 Self::inner_new(py, repo, targetheads, respectsize, randomize)
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 self.inner_addcommons(py, commons)
}
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356
def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 self.inner_addmissings(py, missings)
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
def addinfo(&self, sample: PyObject) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 self.inner_addinfo(py, sample)
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
def hasinfo(&self) -> PyResult<bool> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let leaked = self.inner(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let inner = unsafe { leaked.try_borrow(py)? };
Ok(inner.has_info())
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
def iscomplete(&self) -> PyResult<bool> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let leaked = self.inner(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let inner = unsafe { leaked.try_borrow(py)? };
Ok(inner.is_complete())
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
Georges Racinet
rust-discovery: implementing and exposing stats()...
r42357 def stats(&self) -> PyResult<PyDict> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let leaked = self.inner(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let inner = unsafe { leaked.try_borrow(py)? };
let stats = inner.stats();
Georges Racinet
rust-discovery: implementing and exposing stats()...
r42357 let as_dict: PyDict = PyDict::new(py);
as_dict.set_item(py, "undecided",
Georges Racinet
rust-python3: compatibility fix for integer conversion...
r42519 stats.undecided.map(
|l| l.to_py_object(py).into_object())
.unwrap_or_else(|| py.None()))?;
Georges Racinet
rust-discovery: implementing and exposing stats()...
r42357 Ok(as_dict)
}
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def commonheads(&self) -> PyResult<HashSet<PyRevision>> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let leaked = self.inner(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let inner = unsafe { leaked.try_borrow(py)? };
let res = inner.common_heads()
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 .map_err(|e| GraphError::pynew(py, e))?;
Ok(res.into_iter().map(Into::into).collect())
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356 }
Georges Racinet
rust-discovery: exposing sampling to python...
r42967
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 def takefullsample(&self, headrevs: PyObject,
Georges Racinet
rust-discovery: exposing sampling to python...
r42967 size: usize) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 self.inner_takefullsample(py, headrevs, size)
}
def takequicksample(&self, headrevs: PyObject,
size: usize) -> PyResult<PyObject> {
self.inner_takequicksample(py, headrevs, size)
}
});
impl PartialDiscovery {
fn inner_new(
py: Python,
repo: PyObject,
targetheads: PyObject,
respectsize: bool,
randomize: bool,
) -> PyResult<Self> {
let index = repo.getattr(py, "changelog")?.getattr(py, "index")?;
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let cloned_index = py_rust_index_to_graph(py, index.clone_ref(py))?;
let index = py_rust_index_to_graph(py, index)?;
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let target_heads = {
let borrowed_idx = unsafe { index.try_borrow(py)? };
rev_pyiter_collect(py, &targetheads, &*borrowed_idx)?
};
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let lazy_disco = unsafe {
index.map(py, |idx| {
CorePartialDiscovery::new(
idx,
target_heads,
respectsize,
randomize,
)
})
};
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 Self::create_instance(
py,
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 RefCell::new(lazy_disco),
RefCell::new(cloned_index),
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 )
}
Georges Racinet
rust-discovery: encapsulated conversions to vec for instance methods...
r52136 /// Convert a Python iterator of revisions into a vector
fn pyiter_to_vec(
&self,
py: Python,
iter: &PyObject,
) -> PyResult<Vec<Revision>> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let leaked = self.index(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let index = unsafe { leaked.try_borrow(py)? };
Georges Racinet
rust-discovery: encapsulated conversions to vec for instance methods...
r52136 rev_pyiter_collect(py, iter, &*index)
}
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 fn inner_addinfo(
&self,
py: Python,
sample: PyObject,
) -> PyResult<PyObject> {
let mut missing: Vec<Revision> = Vec::new();
let mut common: Vec<Revision> = Vec::new();
for info in sample.iter(py)? {
// info is a pair (Revision, bool)
let mut revknown = info?.iter(py)?;
let rev: PyRevision = revknown.next().unwrap()?.extract(py)?;
// This is fine since we're just using revisions as integers
// for the purposes of discovery
let rev = Revision(rev.0);
let known: bool = revknown.next().unwrap()?.extract(py)?;
if known {
common.push(rev);
} else {
missing.push(rev);
}
}
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut leaked = self.inner(py).borrow_mut();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut inner = unsafe { leaked.try_borrow_mut(py)? };
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 inner
.add_common_revisions(common)
.map_err(|e| GraphError::pynew(py, e))?;
inner
.add_missing_revisions(missing)
.map_err(|e| GraphError::pynew(py, e))?;
Ok(py.None())
}
fn inner_addcommons(
&self,
py: Python,
commons: PyObject,
) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: encapsulated conversions to vec for instance methods...
r52136 let commons_vec = self.pyiter_to_vec(py, &commons)?;
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut leaked = self.inner(py).borrow_mut();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut inner = unsafe { leaked.try_borrow_mut(py)? };
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 inner
.add_common_revisions(commons_vec)
.map_err(|e| GraphError::pynew(py, e))?;
Ok(py.None())
}
fn inner_addmissings(
&self,
py: Python,
missings: PyObject,
) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: encapsulated conversions to vec for instance methods...
r52136 let missings_vec = self.pyiter_to_vec(py, &missings)?;
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut leaked = self.inner(py).borrow_mut();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut inner = unsafe { leaked.try_borrow_mut(py)? };
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 inner
.add_missing_revisions(missings_vec)
.map_err(|e| GraphError::pynew(py, e))?;
Ok(py.None())
}
fn inner_takefullsample(
&self,
py: Python,
_headrevs: PyObject,
size: usize,
) -> PyResult<PyObject> {
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut leaked = self.inner(py).borrow_mut();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut inner = unsafe { leaked.try_borrow_mut(py)? };
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 let sample = inner
.take_full_sample(size)
Georges Racinet
rust-discovery: exposing sampling to python...
r42967 .map_err(|e| GraphError::pynew(py, e))?;
let as_vec: Vec<PyObject> = sample
.iter()
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 .map(|rev| PyRevision(rev.0).to_py_object(py).into_object())
Georges Racinet
rust-discovery: exposing sampling to python...
r42967 .collect();
Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
}
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 fn inner_takequicksample(
&self,
py: Python,
headrevs: PyObject,
size: usize,
) -> PyResult<PyObject> {
Georges Racinet
rust-discovery: encapsulated conversions to vec for instance methods...
r52136 let revsvec = self.pyiter_to_vec(py, &headrevs)?;
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut leaked = self.inner(py).borrow_mut();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-index: using `hg::index::Index` in discovery...
r52138 let mut inner = unsafe { leaked.try_borrow_mut(py)? };
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 let sample = inner
.take_quick_sample(revsvec, size)
Georges Racinet
rust-discovery: exposing sampling to python...
r42967 .map_err(|e| GraphError::pynew(py, e))?;
let as_vec: Vec<PyObject> = sample
.iter()
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 .map(|rev| PyRevision(rev.0).to_py_object(py).into_object())
Georges Racinet
rust-discovery: exposing sampling to python...
r42967 .collect();
Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
}
Georges Racinet
rust-discovery: moving most of hg-cpython methods to regular code blocks...
r52135 }
Georges Racinet
rust-discovery: cpython bindings for the core logic...
r42356
/// Create the module, with __package__ given from parent
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
let dotted_name = &format!("{}.discovery", package);
let m = PyModule::new(py, dotted_name)?;
m.add(py, "__package__", package)?;
m.add(
py,
"__doc__",
"Discovery of common node sets - Rust implementation",
)?;
m.add_class::<PartialDiscovery>(py)?;
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)
}