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 17, 2022
1 parent 23a3069 commit 0b2e831
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 CPython interpreter

When you run your Rust binary with CPython (not PyPy) interpreter, your newly created module won't be initialized unless the function defined with macro `#[pymodule]` is added to a table called `PyImport_Inittab`. This means Python statements like `import your_new_module` run by your Rust binary will fail. You can use macro `append_to_inittab` before function `prepare_freethreaded_python` being called to add the module function into that table. Also [`auto-initialize`](features.md#auto-initialize) needs to be turned off in such scenario.

## 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 {
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,
Option::Some($module::init),
);
}
};
}
32 changes: 32 additions & 0 deletions tests/test_append_to_inittab.rs
@@ -0,0 +1,32 @@
#![cfg(feature = "macros")]
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 0b2e831

Please sign in to comment.