ancestors.rs
414 lines
| 15.3 KiB
| application/rls-services+xml
|
RustLexer
Georges Racinet
|
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
|
r41220 | //! Bindings for the `hg::ancestors` module provided by the | ||
Georges Racinet
|
r41001 | //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor` | ||
Georges Racinet
|
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
|
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
|
r41220 | //! | ||
Georges Racinet
|
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
|
r41220 | //! - [`AncestorsIterator`] is the Rust counterpart of the | ||
Georges Racinet
|
r41279 | //! `ancestor._lazyancestorsiter` Python generator. From Python, instances of | ||
//! this should be mainly obtained by calling `iter()` on a [`LazyAncestors`] | ||||
//! instance. | ||||
Georges Racinet
|
r41220 | //! | ||
//! [`LazyAncestors`]: struct.LazyAncestors.html | ||||
Georges Racinet
|
r41224 | //! [`MissingAncestors`]: struct.MissingAncestors.html | ||
Georges Racinet
|
r41220 | //! [`AncestorsIterator`]: struct.AncestorsIterator.html | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | use crate::revlog::py_rust_index_to_graph; | ||
Raphaël Gomès
|
r51872 | use crate::PyRevision; | ||
Raphaël Gomès
|
r42828 | use crate::{ | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | conversion::rev_pyiter_collect, exceptions::GraphError, | ||
Georges Racinet
|
r52132 | revlog::PySharedIndex, | ||
Raphaël Gomès
|
r42828 | }; | ||
Georges Racinet
|
r41083 | use cpython::{ | ||
Georges Racinet
|
r52132 | ObjectProtocol, PyClone, PyDict, PyErr, PyList, PyModule, PyObject, | ||
PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked, | ||||
Georges Racinet
|
r41083 | }; | ||
Georges Racinet
|
r52132 | |||
pacien
|
r49350 | use hg::MissingAncestors as CoreMissing; | ||
Georges Racinet
|
r41083 | use hg::Revision; | ||
use std::cell::RefCell; | ||||
Georges Racinet
|
r41224 | use std::collections::HashSet; | ||
pacien
|
r49350 | use vcsgraph::lazy_ancestors::{ | ||
AncestorsIterator as VCGAncestorsIterator, | ||||
LazyAncestors as VCGLazyAncestors, | ||||
}; | ||||
Georges Racinet
|
r41083 | |||
Georges Racinet
|
r52132 | // Error propagation for an [`UnsafePyLeaked`] wrapping a [`Result`] | ||
// | ||||
// It would be nice for UnsharedPyLeaked to provide this directly as a variant | ||||
// of the `map` method with a signature such as: | ||||
// | ||||
// ``` | ||||
Raphaël Gomès
|
r52148 | // unsafe fn map_or_err(py: Python, | ||
// f: impl FnOnce(T) -> Result(U, E), | ||||
// convert_err: impl FnOnce(Python, E) -> PyErr) | ||||
Georges Racinet
|
r52132 | // ``` | ||
// | ||||
// This would spare users of the `cpython` crate the additional `unsafe` deref | ||||
// to inspect the error and return it outside `UnsafePyLeaked`, and the | ||||
// subsequent unwrapping that this function performs. | ||||
fn pyleaked_or_map_err<T, E: std::fmt::Debug + Copy>( | ||||
py: Python, | ||||
leaked: UnsafePyLeaked<Result<T, E>>, | ||||
convert_err: impl FnOnce(Python, E) -> PyErr, | ||||
) -> PyResult<UnsafePyLeaked<T>> { | ||||
// Result.inspect_err is unstable in Rust 1.61 | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | if let Err(e) = *unsafe { leaked.try_borrow(py)? } { | ||
return Err(convert_err(py, e)); | ||||
} | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | Ok(unsafe { | ||
leaked.map(py, |res| { | ||||
res.expect("Error case should have already be treated") | ||||
}) | ||||
}) | ||||
} | ||||
Georges Racinet
|
r41220 | py_class!(pub class AncestorsIterator |py| { | ||
Georges Racinet
|
r52132 | data inner: RefCell<UnsafePyLeaked<VCGAncestorsIterator<PySharedIndex>>>; | ||
Georges Racinet
|
r41083 | |||
Raphaël Gomès
|
r51872 | def __next__(&self) -> PyResult<Option<PyRevision>> { | ||
Georges Racinet
|
r52132 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
match inner.next() { | ||||
pacien
|
r49350 | Some(Err(e)) => Err(GraphError::pynew_from_vcsgraph(py, e)), | ||
Georges Racinet
|
r41083 | None => Ok(None), | ||
Raphaël Gomès
|
r51872 | Some(Ok(r)) => Ok(Some(PyRevision(r))), | ||
Georges Racinet
|
r41083 | } | ||
} | ||||
Raphaël Gomès
|
r51872 | def __contains__(&self, rev: PyRevision) -> PyResult<bool> { | ||
Georges Racinet
|
r52132 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let mut inner = unsafe { leaked.try_borrow_mut(py)? }; | ||
inner.contains(rev.0) | ||||
pacien
|
r49350 | .map_err(|e| GraphError::pynew_from_vcsgraph(py, e)) | ||
Georges Racinet
|
r41083 | } | ||
def __iter__(&self) -> PyResult<Self> { | ||||
Ok(self.clone_ref(py)) | ||||
} | ||||
Raphaël Gomès
|
r51872 | def __new__( | ||
_cls, | ||||
index: PyObject, | ||||
initrevs: PyObject, | ||||
stoprev: PyRevision, | ||||
inclusive: bool | ||||
) -> PyResult<AncestorsIterator> { | ||||
Georges Racinet
|
r52132 | Self::inner_new(py, index, initrevs, stoprev, inclusive) | ||
Georges Racinet
|
r41083 | } | ||
}); | ||||
impl AncestorsIterator { | ||||
pacien
|
r49350 | pub fn from_inner( | ||
py: Python, | ||||
Georges Racinet
|
r52132 | ait: UnsafePyLeaked<VCGAncestorsIterator<PySharedIndex>>, | ||
pacien
|
r49350 | ) -> PyResult<Self> { | ||
Georges Racinet
|
r52132 | Self::create_instance(py, RefCell::new(ait)) | ||
} | ||||
pub fn inner_new( | ||||
py: Python, | ||||
index: PyObject, | ||||
initrevs: PyObject, | ||||
stoprev: PyRevision, | ||||
inclusive: bool, | ||||
) -> PyResult<AncestorsIterator> { | ||||
let index = py_rust_index_to_graph(py, index)?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let initvec: Vec<_> = { | ||
let borrowed_idx = unsafe { index.try_borrow(py)? }; | ||||
rev_pyiter_collect(py, &initrevs, &*borrowed_idx)? | ||||
}; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let res_ait = unsafe { | ||
index.map(py, |idx| { | ||||
VCGAncestorsIterator::new( | ||||
idx, | ||||
initvec.into_iter().map(|r| r.0), | ||||
stoprev.0, | ||||
inclusive, | ||||
) | ||||
}) | ||||
}; | ||||
let ait = | ||||
pyleaked_or_map_err(py, res_ait, GraphError::pynew_from_vcsgraph)?; | ||||
AncestorsIterator::from_inner(py, ait) | ||||
Georges Racinet
|
r41083 | } | ||
} | ||||
Georges Racinet
|
r41001 | |||
Georges Racinet
|
r41220 | py_class!(pub class LazyAncestors |py| { | ||
Georges Racinet
|
r52132 | data inner: RefCell<UnsafePyLeaked< | ||
RefCell<VCGLazyAncestors<PySharedIndex>> | ||||
>>; | ||||
data index: PyObject; | ||||
data initrevs: PyObject; | ||||
data stoprev: PyRevision; | ||||
data inclusive: bool; | ||||
Georges Racinet
|
r41149 | |||
Raphaël Gomès
|
r51872 | def __contains__(&self, rev: PyRevision) -> PyResult<bool> { | ||
Georges Racinet
|
r52132 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let inner: &RefCell<VCGLazyAncestors<PySharedIndex>> = | ||
&*unsafe { leaked.try_borrow(py)? }; | ||||
let inner_mut: &mut VCGLazyAncestors<PySharedIndex> = | ||||
Raphaël Gomès
|
r52165 | &mut inner.borrow_mut(); | ||
Georges Racinet
|
r52132 | inner_mut.contains(rev.0) | ||
pacien
|
r49350 | .map_err(|e| GraphError::pynew_from_vcsgraph(py, e)) | ||
Georges Racinet
|
r41149 | } | ||
def __iter__(&self) -> PyResult<AncestorsIterator> { | ||||
Georges Racinet
|
r52132 | let index = self.index(py).clone_ref(py); | ||
let initrevs = self.initrevs(py).clone_ref(py); | ||||
AncestorsIterator::inner_new(py, index, initrevs, | ||||
*self.stoprev(py), | ||||
*self.inclusive(py)) | ||||
Georges Racinet
|
r41149 | } | ||
def __bool__(&self) -> PyResult<bool> { | ||||
Georges Racinet
|
r52132 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let inner = unsafe { leaked.try_borrow(py)? }; | ||
let empty = inner.borrow().is_empty(); | ||||
Ok(!empty) | ||||
Georges Racinet
|
r41149 | } | ||
Raphaël Gomès
|
r51872 | def __new__( | ||
_cls, | ||||
index: PyObject, | ||||
initrevs: PyObject, | ||||
stoprev: PyRevision, | ||||
inclusive: bool | ||||
) -> PyResult<Self> { | ||||
Georges Racinet
|
r52132 | let cloned_index = index.clone_ref(py); | ||
let index = py_rust_index_to_graph(py, index)?; | ||||
let initvec: Vec<_> = { | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let borrowed_idx = unsafe {index.try_borrow(py)?}; | ||
rev_pyiter_collect(py, &initrevs, &*borrowed_idx)? | ||||
}; | ||||
Georges Racinet
|
r41149 | |||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let res_lazy = | ||
unsafe { index.map(py, |idx| VCGLazyAncestors::new( | ||||
idx, | ||||
Raphaël Gomès
|
r51872 | initvec.into_iter().map(|r| r.0), | ||
stoprev.0, | ||||
inclusive | ||||
Georges Racinet
|
r52132 | ))}; | ||
let lazy = pyleaked_or_map_err(py, res_lazy, | ||||
GraphError::pynew_from_vcsgraph)?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet
|
r52132 | let lazy_cell = unsafe { lazy.map(py, RefCell::new)}; | ||
let res = Self::create_instance( | ||||
py, RefCell::new(lazy_cell), | ||||
cloned_index, initrevs, stoprev, inclusive)?; | ||||
Ok(res) | ||||
Georges Racinet
|
r41149 | } | ||
}); | ||||
Georges Racinet
|
r41224 | py_class!(pub class MissingAncestors |py| { | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | data inner: RefCell<UnsafePyLeaked< | ||
CoreMissing<PySharedIndex> | ||||
>>; | ||||
data index: PyObject; | ||||
Georges Racinet
|
r41224 | |||
Raphaël Gomès
|
r44927 | def __new__( | ||
_cls, | ||||
index: PyObject, | ||||
bases: PyObject | ||||
) | ||||
-> PyResult<MissingAncestors> { | ||||
Georges Racinet on incendie.racinet.fr
|
r52133 | let cloned_index = index.clone_ref(py); | ||
let inner_index = py_rust_index_to_graph(py, index)?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let bases_vec: Vec<_> = { | ||
let borrowed_idx = unsafe { inner_index.try_borrow(py)? }; | ||||
rev_pyiter_collect(py, &bases, &*borrowed_idx)? | ||||
}; | ||||
Raphaël Gomès
|
r51872 | |||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner = unsafe { | ||
inner_index.map(py, |idx| CoreMissing::new(idx, bases_vec)) | ||||
}; | ||||
Raphaël Gomès
|
r51872 | MissingAncestors::create_instance( | ||
py, | ||||
Georges Racinet on incendie.racinet.fr
|
r52133 | RefCell::new(inner), | ||
cloned_index, | ||||
Raphaël Gomès
|
r51872 | ) | ||
Georges Racinet
|
r41224 | } | ||
def hasbases(&self) -> PyResult<bool> { | ||||
Georges Racinet on incendie.racinet.fr
|
r52133 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &CoreMissing<PySharedIndex> = | ||
&*unsafe { leaked.try_borrow(py)? }; | ||||
Ok(inner.has_bases()) | ||||
Georges Racinet
|
r41224 | } | ||
def addbases(&self, bases: PyObject) -> PyResult<PyObject> { | ||||
Georges Racinet on incendie.racinet.fr
|
r52133 | let bases_vec: Vec<_> = { | ||
let leaked = py_rust_index_to_graph(py, | ||||
self.index(py).clone_ref(py))?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let index = &*unsafe { leaked.try_borrow(py)? }; | ||
rev_pyiter_collect(py, &bases, index)? | ||||
}; | ||||
let mut leaked = self.inner(py).borrow_mut(); | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &mut CoreMissing<PySharedIndex> = | ||
&mut *unsafe { leaked.try_borrow_mut(py)? }; | ||||
Georges Racinet
|
r41224 | 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
|
r51872 | def bases(&self) -> PyResult<HashSet<PyRevision>> { | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &CoreMissing<PySharedIndex> = | ||
&*unsafe { leaked.try_borrow(py)? }; | ||||
Ok(inner.get_bases() | ||||
.iter() | ||||
.map(|r| PyRevision(r.0)) | ||||
.collect() | ||||
Raphaël Gomès
|
r51872 | ) | ||
Georges Racinet
|
r41224 | } | ||
Raphaël Gomès
|
r51872 | def basesheads(&self) -> PyResult<HashSet<PyRevision>> { | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let leaked = self.inner(py).borrow(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &CoreMissing<PySharedIndex> = | ||
&*unsafe { leaked.try_borrow(py)? }; | ||||
Raphaël Gomès
|
r51872 | Ok( | ||
inner | ||||
.bases_heads() | ||||
.map_err(|e| GraphError::pynew(py, e))? | ||||
.into_iter() | ||||
.map(|r| PyRevision(r.0)) | ||||
.collect() | ||||
) | ||||
Georges Racinet
|
r41282 | } | ||
Georges Racinet
|
r41224 | def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> { | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let mut revs_pyset: HashSet<Revision> = { | ||
// 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 | ||||
let leaked = py_rust_index_to_graph(py, | ||||
self.index(py).clone_ref(py))?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let index = &*unsafe { leaked.try_borrow(py)? }; | ||
Raphaël Gomès
|
r52165 | rev_pyiter_collect(py, &revs, index)? | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | }; | ||
let mut leaked = self.inner(py).borrow_mut(); | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &mut CoreMissing<PySharedIndex> = | ||
&mut *unsafe { leaked.try_borrow_mut(py)? }; | ||||
Georges Racinet
|
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
|
r51872 | remaining_pyint_vec.push( | ||
PyRevision(rev.0).to_py_object(py).into_object() | ||||
); | ||||
Georges Racinet
|
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> { | ||||
Georges Racinet on incendie.racinet.fr
|
r52133 | let revs_vec: Vec<Revision> = { | ||
let leaked = py_rust_index_to_graph(py, | ||||
self.index(py).clone_ref(py))?; | ||||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let index = &*unsafe { leaked.try_borrow(py)? }; | ||
rev_pyiter_collect(py, &revs, index)? | ||||
}; | ||||
Raphaël Gomès
|
r51872 | |||
Georges Racinet on incendie.racinet.fr
|
r52133 | let mut leaked = self.inner(py).borrow_mut(); | ||
Raphaël Gomès
|
r52148 | // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | ||
Georges Racinet on incendie.racinet.fr
|
r52133 | let inner: &mut CoreMissing<PySharedIndex> = | ||
&mut *unsafe { leaked.try_borrow_mut(py)? }; | ||||
Georges Racinet
|
r41224 | 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
|
r51872 | missing_pyint_vec.push( | ||
PyRevision(rev.0).to_py_object(py).into_object() | ||||
); | ||||
Georges Racinet
|
r41224 | } | ||
Ok(PyList::new(py, missing_pyint_vec.as_slice())) | ||||
} | ||||
}); | ||||
/// Create the module, with __package__ given from parent | ||||
Georges Racinet
|
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
|
r41083 | m.add_class::<AncestorsIterator>(py)?; | ||
Georges Racinet
|
r41149 | m.add_class::<LazyAncestors>(py)?; | ||
Georges Racinet
|
r41224 | m.add_class::<MissingAncestors>(py)?; | ||
Georges Racinet
|
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) | ||||
} | ||||