Skip to content

Commit

Permalink
Add macro append_to_inittab
Browse files Browse the repository at this point in the history
Sometimes we need to debug in a real environment with our module installed. `append_to_inittab` will be a wrapper for PyImport_AppendInittab (https://docs.python.org/3/c-api/import.html#c.PyImport_AppendInittab) and help us to do this
  • Loading branch information
herquan committed May 18, 2022
1 parent 23a3069 commit af5a462
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implement `ToPyObject` for `[T; N]`. [#2313](https://github.com/PyO3/pyo3/pull/2313)
- Added the internal `IntoPyResult` trait to give better error messages when function return types do not implement `IntoPy`. [#2326](https://github.com/PyO3/pyo3/pull/2326)
- Add `PyDictKeys`, `PyDictValues` and `PyDictItems` Rust types to represent `dict_keys`, `dict_values` and `dict_items` types. [#2358](https://github.com/PyO3/pyo3/pull/2358)
- Add macro `append_to_inittab`. [#2377](https://github.com/PyO3/pyo3/pull/2377)

### Changed

Expand Down
4 changes: 4 additions & 0 deletions guide/src/building_and_distribution.md
Expand Up @@ -225,6 +225,10 @@ The known complications are:

If you encounter these or other complications when linking the interpreter statically, discuss them on [issue 416 on PyO3's Github](https://github.com/PyO3/pyo3/issues/416). It is hoped that eventually that discussion will contain enough information and solutions that PyO3 can offer first-class support for static embedding.

### Import your module when embedding the Python interpreter

When you run your Rust binary with an embedded interpreter, any `#[pymodule]` created modules won't be accessible to import unless added to a table called `PyImport_Inittab` before the embedded interpreter is initialized. This will cause Python statements in your embedded interpreter such as `import your_new_module` to fail. You can call the macro [`append_to_inittab`]({{#PYO3_DOCS_URL}}/pyo3/macro.append_to_inittab.html) with your module before initializing the Python interpreter to add the module function into that table. (The Python interpreter will be initialized by calling `prepare_freethreaded_python`, `with_embedded_interpreter`, or `Python::with_gil` with the [`auto-initialize`](features.md#auto-initialize) feature enabled.)

## Cross Compiling

Thanks to Rust's great cross-compilation support, cross-compiling using PyO3 is relatively straightforward. To get started, you'll need a few pieces of software:
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/src/import.rs
Expand Up @@ -76,6 +76,6 @@ extern "C" {

pub fn PyImport_AppendInittab(
name: *const c_char,
initfunc: Option<extern "C" fn() -> *mut PyObject>,
initfunc: Option<unsafe extern "C" fn() -> *mut PyObject>,
) -> c_int;
}
23 changes: 23 additions & 0 deletions src/macros.rs
Expand Up @@ -150,3 +150,26 @@ macro_rules! wrap_pymodule {
}
};
}

#[cfg(not(PyPy))]
/// Add the module to the initialization table in order to make embedded Python code to use it.
/// Module name is the argument.
///
/// Use it before [`prepare_freethreaded_python`](crate::prepare_freethreaded_python) and
/// leave feature `auto-initialize` off
#[macro_export]
macro_rules! append_to_inittab {
($module:ident) => {
unsafe {
::std::assert_eq!(
$crate::ffi::Py_IsInitialized(),
0,
"called `append_to_inittab_impl` but a Python interpreter is already running."
);
$crate::ffi::PyImport_AppendInittab(
concat!(stringify!($module), "\0").as_ptr() as *const ::std::os::raw::c_char,
::std::option::Option::Some($module::init),
);
}
};
}
32 changes: 32 additions & 0 deletions tests/test_append_to_inittab.rs
@@ -0,0 +1,32 @@
#![cfg(all(feature = "macros", not(PyPy)))]
use pyo3::prelude::*;

#[pyfunction]
fn foo() -> usize {
123
}

#[pymodule]
fn module_with_functions(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(foo, m)?).unwrap();
Ok(())
}

#[cfg(not(PyPy))]
#[test]
fn test_module_append_to_inittab() {
use pyo3::append_to_inittab;
append_to_inittab!(module_with_functions);
Python::with_gil(|py| {
py.run(
r#"
import module_with_functions
assert module_with_functions.foo() == 123
"#,
None,
None,
)
.map_err(|e| e.print(py))
.unwrap();
})
}

0 comments on commit af5a462

Please sign in to comment.