diff --git a/rust/hg-pyo3/src/dagops.rs b/rust/hg-pyo3/src/dagops.rs new file mode 100644 --- /dev/null +++ b/rust/hg-pyo3/src/dagops.rs @@ -0,0 +1,22 @@ +// dagops.rs +// +// Copyright 2024 Georges Racinet +// +// 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::dagops` module provided by the +//! `hg-core` package. +//! +//! From Python, this will be seen as `mercurial.pyo3-rustext.dagop` +use pyo3::prelude::*; + +use crate::util::new_submodule; + +pub fn init_module<'py>( + py: Python<'py>, + package: &str, +) -> PyResult> { + let m = new_submodule(py, package, "dagop")?; + Ok(m) +} diff --git a/rust/hg-pyo3/src/lib.rs b/rust/hg-pyo3/src/lib.rs --- a/rust/hg-pyo3/src/lib.rs +++ b/rust/hg-pyo3/src/lib.rs @@ -1,6 +1,19 @@ use pyo3::prelude::*; +mod dagops; +mod util; + #[pymodule] -fn pyo3_rustext(_py: Python<'_>, _m: &Bound<'_, PyModule>) -> PyResult<()> { +fn pyo3_rustext(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add( + "__doc__", + "Mercurial core concepts - Rust implementation exposed via PyO3", + )?; + // the module's __name__ is pyo3_rustext, not mercurial.pyo3_rustext + // (at least at this point). + let name: String = m.getattr("__name__")?.extract()?; + let dotted_name = format!("mercurial.{}", name); + + m.add_submodule(&dagops::init_module(py, &dotted_name)?)?; Ok(()) } diff --git a/rust/hg-pyo3/src/util.rs b/rust/hg-pyo3/src/util.rs new file mode 100644 --- /dev/null +++ b/rust/hg-pyo3/src/util.rs @@ -0,0 +1,28 @@ +use pyo3::prelude::*; +use pyo3::types::PyDict; +/// Create the module, with `__package__` given from parent +/// +/// According to PyO3 documentation, which links to +/// , the same convoluted +/// write to sys.modules has to be made as with the `cpython` crate. +pub(crate) fn new_submodule<'py>( + py: Python<'py>, + package_name: &str, + name: &str, +) -> PyResult> { + let dotted_name = &format!("{}.{}", package_name, name); + let m = PyModule::new(py, name)?; + m.add("__package__", package_name)?; + m.add("__doc__", "DAG operations - Rust implementation")?; + + let sys = PyModule::import(py, "sys")?; + // according to the doc, we could make a static PyString out of + // "modules" with the `intern!` macro, but this is used only at + // registration so it may not be worth the effort. + let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.extract()?; + sys_modules.set_item(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) +}