pyo3/guide/src/module.md

182 lines
5 KiB
Markdown
Raw Normal View History

# Python Modules
You can create a module using `#[pymodule]`:
```rust
2019-02-01 13:01:18 +00:00
use pyo3::prelude::*;
#[pyfunction]
fn double(x: usize) -> usize {
x * 2
}
2017-06-18 15:15:10 +00:00
/// This module is implemented in Rust.
2018-11-12 21:28:45 +00:00
#[pymodule]
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
}
```
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::*;
#[pyfunction]
fn double(x: usize) -> usize {
x * 2
}
#[pymodule]
#[pyo3(name = "custom_name")]
fn my_extension(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
}
```
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:
`ImportError: dynamic module does not define module export function (PyInit_name_of_your_module)`
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
`python setup.py develop` with [setuptools-rust](https://github.com/PyO3/setuptools-rust).
2018-11-12 21:25:45 +00:00
## Documentation
The [Rust doc comments](https://doc.rust-lang.org/stable/book/ch03-04-comments.html) of the module
initialization function will be applied automatically as the Python docstring of your module.
2017-06-18 15:15:10 +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
import my_extension
2017-06-18 15:15:10 +00:00
print(my_extension.__doc__)
2017-06-18 15:15:10 +00:00
```
## Organizing your module registration code
2017-06-18 15:15:10 +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
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:
2018-11-12 21:25:45 +00:00
```rust
// 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]
struct SomeClass {/* ... */}
# }
// src/osutil.rs
# mod osutil {
2019-02-14 09:41:52 +00:00
use pyo3::prelude::*;
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]
fn determine_current_os() -> String {
"linux".to_owned()
2018-11-12 21:25:45 +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
#[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(())
}
// 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]
fn parent_module(py: Python, m: &PyModule) -> PyResult<()> {
register_child_module(py, m)?;
2018-11-12 21:25:45 +00:00
Ok(())
}
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()
}
# Python::with_gil(|py| {
# 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);
#
# py.run("assert parent_module.child_module.func() == 'func'", None, Some(&ctx)).unwrap();
# })
2018-11-12 21:25:45 +00:00
```
Note that this does not define a package, so this wont 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).
It is not necessary to add `#[pymodule]` on nested modules, which is only required on the top-level module.