Mercurial > public > mercurial-scm > hg
view rust/hg-cpython/src/lib.rs @ 52411:c2480ac4c5e2
rust-pyo3: retrieving the InnerRevlog of hg-cpython
This allows PyO3-based code to use the InnerRevlog, access its shared data
(core InnerRevlog), which will then allow, e.g., to retrieve references on
the core Index.
On the `hg-cpython` (`rusthg` crate, `rustext` Python extension module),
we had to also build as a Rust library, and open up some accesses (see
notably the public accessor for `inner`, the core `InnerRevlog`).
Retrieving the Rust struct underlying a Python object defined by another
extension module written in Rust is tricky because the Python type objects
are duplicated in the extension modules, leading to failure of the normal
type checking. See the doc-comment of `convert_cpython::extract_inner_revlog`
for a complete explanation.
To solve this, we import the Python type object of `rustext` (defined
by `hg-cpython`) and perform a manual check. Checking the Python type is
necessary, as PyO3 documentation clearly state that downcasting an object
that has not the proper type is Undefined Behaviour.
At this point, we do not have conversion facilities for exceptions (`PyErr`
on both sides), hence the remaining unwraps).
author | Georges Racinet <georges.racinet@cloudcrane.io> |
---|---|
date | Sat, 30 Nov 2024 20:57:02 +0100 |
parents | 4361d787e6cf |
children |
line wrap: on
line source
// lib.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. //! Python bindings of `hg-core` objects using the `cpython` crate. //! Once compiled, the resulting single shared library object can be placed in //! the `mercurial` package directly as `rustext.so` or `rustext.dll`. //! It holds several modules, so that from the point of view of Python, //! it behaves as the `cext` package. //! //! Example: //! //! ```text //! >>> from mercurial.rustext import ancestor //! >>> ancestor.__doc__ //! 'Generic DAG ancestor algorithms - Rust implementation' //! ``` #![allow(clippy::too_many_arguments)] // rust-cpython macros #![allow(clippy::zero_ptr)] // rust-cpython macros #![allow(clippy::needless_update)] // rust-cpython macros #![allow(clippy::manual_strip)] // rust-cpython macros #![allow(clippy::type_complexity)] // rust-cpython macros use cpython::{FromPyObject, PyInt, Python, ToPyObject}; use hg::{BaseRevision, Revision}; /// This crate uses nested private macros, `extern crate` is still needed in /// 2018 edition. #[macro_use] extern crate cpython; pub mod ancestors; mod cindex; mod conversion; #[macro_use] pub mod ref_sharing; pub mod copy_tracing; pub mod dagops; pub mod debug; pub mod dirstate; pub mod discovery; pub mod exceptions; mod pybytes_deref; pub mod revlog; pub mod update; pub mod utils; pub mod vfs; /// Revision as exposed to/from the Python layer. /// /// We need this indirection because of the orphan rule, meaning we can't /// implement a foreign trait (like [`cpython::ToPyObject`]) /// for a foreign type (like [`hg::UncheckedRevision`]). /// /// This also acts as a deterrent against blindly trusting Python to send /// us valid revision numbers. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PyRevision(BaseRevision); impl From<Revision> for PyRevision { fn from(r: Revision) -> Self { PyRevision(r.0) } } impl<'s> FromPyObject<'s> for PyRevision { fn extract( py: Python, obj: &'s cpython::PyObject, ) -> cpython::PyResult<Self> { Ok(Self(obj.extract::<BaseRevision>(py)?)) } } impl ToPyObject for PyRevision { type ObjectType = PyInt; fn to_py_object(&self, py: Python) -> Self::ObjectType { self.0.to_py_object(py) } } py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { m.add( py, "__doc__", "Mercurial core concepts - Rust implementation", )?; let dotted_name: String = m.get(py, "__name__")?.extract(py)?; m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?; m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?; m.add(py, "debug", debug::init_module(py, &dotted_name)?)?; m.add( py, "copy_tracing", copy_tracing::init_module(py, &dotted_name)?, )?; m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?; m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?; m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?; m.add(py, "update", update::init_module(py, &dotted_name)?)?; m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?; Ok(()) });