2019-04-17 09:35:29 +00:00
|
|
|
|
# Python Modules
|
2017-06-18 02:02:02 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
You can create a module using `#[pymodule]`:
|
2017-06-18 05:07:05 +00:00
|
|
|
|
|
|
|
|
|
```rust
|
2019-02-01 13:01:18 +00:00
|
|
|
|
use pyo3::prelude::*;
|
2017-06-18 05:07:05 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
#[pyfunction]
|
|
|
|
|
fn double(x: usize) -> usize {
|
|
|
|
|
x * 2
|
|
|
|
|
}
|
2020-03-13 13:53:49 +00:00
|
|
|
|
|
2017-06-18 15:15:10 +00:00
|
|
|
|
/// This module is implemented in Rust.
|
2018-11-12 21:28:45 +00:00
|
|
|
|
#[pymodule]
|
2021-07-22 07:10:32 +00:00
|
|
|
|
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
m.add_function(wrap_pyfunction!(double, m)?)?;
|
2017-06-18 05:07:05 +00:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2021-07-22 07:10:32 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `#[pymodule]` procedural macro takes care of exporting the initialization function of your
|
|
|
|
|
module to Python.
|
|
|
|
|
|
|
|
|
|
The module's name defaults to the name of the Rust function. You can override the module name by
|
|
|
|
|
using `#[pyo3(name = "custom_name")]`:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use pyo3::prelude::*;
|
2017-06-18 05:07:05 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
#[pyfunction]
|
|
|
|
|
fn double(x: usize) -> usize {
|
|
|
|
|
x * 2
|
2017-06-18 05:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
#[pymodule]
|
|
|
|
|
#[pyo3(name = "custom_name")]
|
|
|
|
|
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
m.add_function(wrap_pyfunction!(double, m)?)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2017-06-18 05:07:05 +00:00
|
|
|
|
```
|
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
The name of the module must match the name of the `.so` or `.pyd`
|
|
|
|
|
file. Otherwise, you will get an import error in Python with the following message:
|
2020-06-15 08:58:37 +00:00
|
|
|
|
`ImportError: dynamic module does not define module export function (PyInit_name_of_your_module)`
|
|
|
|
|
|
2021-05-24 06:48:22 +00:00
|
|
|
|
To import the module, either:
|
|
|
|
|
- copy the shared library as described in [Manual builds](building_and_distribution.html#manual-builds), or
|
|
|
|
|
- use a tool, e.g. `maturin develop` with [maturin](https://github.com/PyO3/maturin) or
|
2020-09-05 13:54:03 +00:00
|
|
|
|
`python setup.py develop` with [setuptools-rust](https://github.com/PyO3/setuptools-rust).
|
2018-11-12 21:25:45 +00:00
|
|
|
|
|
|
|
|
|
## Documentation
|
2017-06-18 05:07:05 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
The [Rust doc comments](https://doc.rust-lang.org/stable/book/ch03-04-comments.html) of the module
|
2020-09-05 13:54:03 +00:00
|
|
|
|
initialization function will be applied automatically as the Python docstring of your module.
|
2017-06-18 15:15:10 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
For example, building off of the above code, this will print `This module is implemented in Rust.`:
|
|
|
|
|
|
2017-06-18 15:15:10 +00:00
|
|
|
|
```python
|
2021-07-22 07:10:32 +00:00
|
|
|
|
import my_extension
|
2017-06-18 15:15:10 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
print(my_extension.__doc__)
|
2017-06-18 15:15:10 +00:00
|
|
|
|
```
|
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
## Organizing your module registration code
|
2017-06-18 15:15:10 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
For most projects, it's adequate to centralize all your FFI code into a single Rust module.
|
2018-11-12 21:25:45 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
However, for larger projects, it can be helpful to split your Rust code into several Rust modules to keep your code
|
|
|
|
|
readable. Unfortunately, though, some of the macros like `wrap_pyfunction!` do not yet work when used on code defined
|
|
|
|
|
in other modules ([#1709](https://github.com/PyO3/pyo3/issues/1709)). One way to work around this is to pass
|
|
|
|
|
references to the `PyModule` so that each module registers its own FFI code. For example:
|
2017-06-18 05:07:05 +00:00
|
|
|
|
|
2018-11-12 21:25:45 +00:00
|
|
|
|
```rust
|
2021-07-22 07:10:32 +00:00
|
|
|
|
// src/lib.rs
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
|
|
|
|
|
#[pymodule]
|
|
|
|
|
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
dirutil::register(py, m)?;
|
|
|
|
|
osutil::register(py, m)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// src/dirutil.rs
|
|
|
|
|
# mod dirutil {
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
|
|
|
|
|
pub(crate) fn register(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
m.add_class::<SomeClass>()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pyclass]
|
2021-10-14 21:15:25 +00:00
|
|
|
|
struct SomeClass {/* ... */}
|
2021-07-22 07:10:32 +00:00
|
|
|
|
# }
|
|
|
|
|
|
|
|
|
|
// src/osutil.rs
|
|
|
|
|
# mod osutil {
|
2019-02-14 09:41:52 +00:00
|
|
|
|
use pyo3::prelude::*;
|
2021-07-22 07:10:32 +00:00
|
|
|
|
|
|
|
|
|
pub(crate) fn register(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
m.add_function(wrap_pyfunction!(determine_current_os, m)?)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2019-02-14 09:41:52 +00:00
|
|
|
|
|
2018-11-12 21:25:45 +00:00
|
|
|
|
#[pyfunction]
|
2021-07-22 07:10:32 +00:00
|
|
|
|
fn determine_current_os() -> String {
|
|
|
|
|
"linux".to_owned()
|
2018-11-12 21:25:45 +00:00
|
|
|
|
}
|
2021-07-22 07:10:32 +00:00
|
|
|
|
# }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Another workaround for splitting FFI code across multiple modules ([#1709](https://github.com/PyO3/pyo3/issues/1709))
|
|
|
|
|
is to add `use module::*`, like this:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// src/lib.rs
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
use osutil::*;
|
2018-11-12 21:25:45 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
#[pymodule]
|
|
|
|
|
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
m.add_function(wrap_pyfunction!(determine_current_os, m)?)?;
|
2018-11-12 21:25:45 +00:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
// src/osutil.rs
|
|
|
|
|
# mod osutil {
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
|
|
|
|
|
#[pyfunction]
|
|
|
|
|
pub(crate) fn determine_current_os() -> String {
|
|
|
|
|
"linux".to_owned()
|
|
|
|
|
}
|
|
|
|
|
# }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Python submodules
|
|
|
|
|
|
|
|
|
|
You can create a module hierarchy within a single extension module by using
|
|
|
|
|
[`PyModule.add_submodule()`]({{#PYO3_DOCS_URL}}/pyo3/prelude/struct.PyModule.html#method.add_submodule).
|
|
|
|
|
For example, you could define the modules `parent_module` and `parent_module.child_module`.
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
|
2018-11-12 21:25:45 +00:00
|
|
|
|
#[pymodule]
|
2021-07-22 07:10:32 +00:00
|
|
|
|
fn parent_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
|
|
|
register_child_module(py, m)?;
|
2018-11-12 21:25:45 +00:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
fn register_child_module(py: Python, parent_module: &PyModule) -> PyResult<()> {
|
|
|
|
|
let child_module = PyModule::new(py, "child_module")?;
|
|
|
|
|
child_module.add_function(wrap_pyfunction!(func, child_module)?)?;
|
|
|
|
|
parent_module.add_submodule(child_module)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pyfunction]
|
|
|
|
|
fn func() -> String {
|
|
|
|
|
"func".to_string()
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 21:38:02 +00:00
|
|
|
|
# Python::with_gil(|py| {
|
2021-07-22 07:10:32 +00:00
|
|
|
|
# use pyo3::wrap_pymodule;
|
|
|
|
|
# use pyo3::types::IntoPyDict;
|
|
|
|
|
# let parent_module = wrap_pymodule!(parent_module)(py);
|
|
|
|
|
# let ctx = [("parent_module", parent_module)].into_py_dict(py);
|
2020-07-13 21:38:02 +00:00
|
|
|
|
#
|
2021-07-22 07:10:32 +00:00
|
|
|
|
# py.run("assert parent_module.child_module.func() == 'func'", None, Some(&ctx)).unwrap();
|
2020-07-13 21:38:02 +00:00
|
|
|
|
# })
|
2018-11-12 21:25:45 +00:00
|
|
|
|
```
|
2019-04-17 09:35:29 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
Note that this does not define a package, so this won’t allow Python code to directly import
|
|
|
|
|
submodules by using `from parent_module import child_module`. For more information, see
|
|
|
|
|
[#759](https://github.com/PyO3/pyo3/issues/759) and
|
|
|
|
|
[#1517](https://github.com/PyO3/pyo3/issues/1517#issuecomment-808664021).
|
2020-09-05 13:54:03 +00:00
|
|
|
|
|
2021-07-22 07:10:32 +00:00
|
|
|
|
It is not necessary to add `#[pymodule]` on nested modules, which is only required on the top-level module.
|