2019-04-17 09:35:29 +00:00
# Python Modules
2017-06-18 02:02:02 +00:00
2020-06-06 22:26:17 +00:00
You can create a module as follows:
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
2020-03-13 13:53:49 +00:00
// add bindings to the generated Python module
// N.B: "rust2py" must be the name of the `.so` or `.pyd` file.
2017-06-18 15:15:10 +00:00
/// This module is implemented in Rust.
2018-11-12 21:28:45 +00:00
#[pymodule]
2018-07-09 22:13:02 +00:00
fn rust2py(py: Python, m: & PyModule) -> PyResult< ()> {
2020-03-13 13:53:49 +00:00
// PyO3 aware function. All of our Python interfaces could be declared in a separate module.
2017-06-18 05:07:05 +00:00
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
2020-03-13 13:53:49 +00:00
// Python objects to Rust values, and the Rust return value back into a Python object.
// The `_py` argument represents that we're holding the GIL.
2017-06-18 05:07:05 +00:00
#[pyfn(m, "sum_as_string")]
2020-03-13 13:53:49 +00:00
fn sum_as_string_py(_py: Python, a: i64, b: i64) -> PyResult< String > {
let out = sum_as_string(a, b);
Ok(out)
2017-06-18 05:07:05 +00:00
}
Ok(())
}
2020-03-13 13:53:49 +00:00
// logic implemented as a normal Rust function
fn sum_as_string(a: i64, b: i64) -> String {
2020-02-10 02:20:18 +00:00
format!("{}", a + b)
2017-06-18 05:07:05 +00:00
}
# fn main() {}
```
2019-04-17 09:35:29 +00:00
The `#[pymodule]` procedural macro attribute takes care of exporting the initialization function of your module to Python. It can take as an argument the name of your module, which must be the name of the `.so` or `.pyd` file; the default is the Rust function's name.
2018-11-12 21:25:45 +00:00
2020-06-15 08:58:37 +00:00
If the name of the module (the default being the function name) does not match the name of the `.so` or `.pyd` file, 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)`
2020-06-06 22:26:17 +00:00
To import the module, either copy the shared library as described in [the README ](https://github.com/PyO3/pyo3 ) 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
2017-06-18 05:07:05 +00:00
2020-03-13 13:53:49 +00:00
The [Rust doc comments ](https://doc.rust-lang.org/stable/book/first-edition/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
```python
import rust2py
print(rust2py.__doc__)
```
Which means that the above Python code will print `This module is implemented in Rust.` .
2018-11-12 21:25:45 +00:00
## Modules as objects
2019-04-17 09:35:29 +00:00
In Python, modules are first class objects. This means that you can store them as values or add them to dicts or other modules:
2017-06-18 05:07:05 +00:00
2018-11-12 21:25:45 +00:00
```rust
2019-02-14 09:41:52 +00:00
use pyo3::prelude::*;
use pyo3::{wrap_pyfunction, wrap_pymodule};
2019-03-22 13:07:33 +00:00
use pyo3::types::IntoPyDict;
2019-02-14 09:41:52 +00:00
2018-11-12 21:25:45 +00:00
#[pyfunction]
fn subfunction() -> String {
"Subfunction".to_string()
}
#[pymodule]
fn submodule(_py: Python, module: & PyModule) -> PyResult< ()> {
2019-02-01 13:01:18 +00:00
module.add_wrapped(wrap_pyfunction!(subfunction))?;
2018-11-12 21:25:45 +00:00
Ok(())
}
#[pymodule]
fn supermodule(_py: Python, module: & PyModule) -> PyResult< ()> {
2019-02-01 13:01:18 +00:00
module.add_wrapped(wrap_pymodule!(submodule))?;
2018-11-12 21:25:45 +00:00
Ok(())
}
fn nested_call() {
let gil = GILGuard::acquire();
let py = gil.python();
2019-02-01 13:01:18 +00:00
let supermodule = wrap_pymodule!(supermodule)(py);
2019-03-20 18:37:27 +00:00
let ctx = [("supermodule", supermodule)].into_py_dict(py);
2018-11-12 21:25:45 +00:00
2019-03-22 13:07:33 +00:00
py.run("assert supermodule.submodule.subfunction() == 'Subfunction'", None, Some(&ctx)).unwrap();
2018-11-12 21:25:45 +00:00
}
```
2019-04-17 09:35:29 +00:00
This way, you can create a module hierarchy within a single extension module.