pymatcher: extract pure Rust matchers when possible

Summary:
Avoiding the GIL can speed up tree diff/iteration a lot. Let's extract
the pure Rust matcher when possible. We also convert a Python differencematcher
into a rust DifferenceMatcher when possible, since that is the core use case for
sparse profile change computation.

Reviewed By: andll

Differential Revision: D30553727

fbshipit-source-id: 41d2f13130da18a55f64f9f0f047825bd24144c6
This commit is contained in:
Durham Goode 2021-08-25 23:29:27 -07:00 committed by Facebook GitHub Bot
parent cb3b28bc5d
commit 3c1e7ac330

View File

@ -12,10 +12,12 @@ use std::sync::Arc;
use cpython::*;
use cpython_ext::error::{AnyhowResultExt, ResultPyErrExt};
use cpython_ext::{PyPath, PyPathBuf, Str};
use cpython_ext::{ExtractInner, ExtractInnerRef, PyPath, PyPathBuf, Str};
use anyhow::Result;
use pathmatcher::{AlwaysMatcher, DirectoryMatch, GitignoreMatcher, Matcher, TreeMatcher};
use pathmatcher::{
AlwaysMatcher, DifferenceMatcher, DirectoryMatch, GitignoreMatcher, Matcher, TreeMatcher,
};
use types::RepoPath;
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
@ -51,12 +53,12 @@ py_class!(class gitignorematcher |py| {
}
});
py_class!(class treematcher |py| {
data matcher: TreeMatcher;
py_class!(pub class treematcher |py| {
data matcher: Arc<TreeMatcher>;
def __new__(_cls, rules: Vec<String>) -> PyResult<Self> {
let matcher = TreeMatcher::from_rules(rules.into_iter()).map_pyerr(py)?;
Self::create_instance(py, matcher)
Self::create_instance(py, Arc::new(matcher))
}
def matches(&self, path: &PyPath) -> PyResult<bool> {
@ -72,6 +74,14 @@ py_class!(class treematcher |py| {
}
});
impl ExtractInnerRef for treematcher {
type Inner = Arc<TreeMatcher>;
fn extract_inner_ref<'a>(&'a self, py: Python<'a>) -> &'a Self::Inner {
self.matcher(py)
}
}
fn normalize_glob(_py: Python, path: &str) -> PyResult<Str> {
Ok(pathmatcher::normalize_glob(path).into())
}
@ -197,7 +207,21 @@ fn matches_file_impl(py: Python, py_matcher: &PyObject, path: &RepoPath) -> PyRe
Ok(matches)
}
pub fn extract_matcher(_py: Python, matcher: PyObject) -> PyResult<Arc<dyn Matcher + Sync + Send>> {
/// Extracts a Rust matcher from a Python Object
/// When possible it converts it into a pure-Rust matcher.
pub fn extract_matcher(py: Python, matcher: PyObject) -> PyResult<Arc<dyn Matcher + Sync + Send>> {
if let Ok(matcher) = treematcher::downcast_from(py, matcher.clone_ref(py)) {
return Ok(matcher.extract_inner(py));
}
if matcher.get_type(py).name(py).as_ref() == "treematcher" {
return extract_matcher(py, matcher.getattr(py, "_matcher")?);
}
if matcher.get_type(py).name(py).as_ref() == "differencematcher" {
let include = extract_matcher(py, matcher.getattr(py, "_m1")?)?;
let exclude = extract_matcher(py, matcher.getattr(py, "_m2")?)?;
return Ok(Arc::new(DifferenceMatcher::new(include, exclude)));
}
Ok(Arc::new(ThreadPythonMatcher::new(matcher)))
}