Skip to content

Commit

Permalink
reintroduce PyModule constructors (#4404)
Browse files Browse the repository at this point in the history
* reintroduce `PyModule` constructors

* Update links

* Use PyModule_NewObject

* module::from_code takes CStr instead str

* Add changelog

* fix PyPy link_name for `PyModule_NewObject`

---------

Co-authored-by: Icxolu <[email protected]>
  • Loading branch information
bschoenmaeckers and Icxolu authored Aug 13, 2024
1 parent 7760d6b commit f869d16
Show file tree
Hide file tree
Showing 31 changed files with 264 additions and 183 deletions.
2 changes: 1 addition & 1 deletion examples/maturin-starter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn maturin_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. from maturin_starter.submodule import SubmoduleClass

let sys = PyModule::import_bound(py, "sys")?;
let sys = PyModule::import(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?;
sys_modules.set_item("maturin_starter.submodule", m.getattr("submodule")?)?;

Expand Down
2 changes: 1 addition & 1 deletion examples/plugin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

// Now we can load our python_plugin/gadget_init_plugin.py file.
// It can in turn import other stuff as it deems appropriate
let plugin = PyModule::import_bound(py, "gadget_init_plugin")?;
let plugin = PyModule::import(py, "gadget_init_plugin")?;
// and call start function there, which will return a python reference to Gadget.
// Gadget here is a "pyclass" object reference
let gadget = plugin.getattr("start")?.call0()?;
Expand Down
2 changes: 1 addition & 1 deletion examples/setuptools-rust-starter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn _setuptools_rust_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult
// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. from setuptools_rust_starter.submodule import SubmoduleClass

let sys = PyModule::import_bound(py, "sys")?;
let sys = PyModule::import(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?;
sys_modules.set_item("setuptools_rust_starter.submodule", m.getattr("submodule")?)?;

Expand Down
4 changes: 2 additions & 2 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,8 @@ impl MyClass {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let module = PyModule::new_bound(py, "my_module")?;
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let module = PyModule::new(py, "my_module")?;
# module.add_class::<MyClass>()?;
# let class = module.getattr("MyClass")?;
#
Expand Down
2 changes: 1 addition & 1 deletion guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import_bound(py, "__main__")?.dict();
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object_bound(py))?;
#
# py.run_bound(SCRIPT, Some(&globals), None)?;
Expand Down
43 changes: 23 additions & 20 deletions guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ the Python object, i.e. `obj.getattr("my_string")`, and call `extract()` on the

```rust
use pyo3::prelude::*;
use pyo3_ffi::c_str;

#[derive(FromPyObject)]
struct RustyStruct {
Expand All @@ -54,13 +55,13 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let module = PyModule::from_code_bound(
# let module = PyModule::from_code(
# py,
# "class Foo:
# c_str!("class Foo:
# def __init__(self):
# self.my_string = 'test'",
# "",
# "",
# self.my_string = 'test'"),
# c_str!(""),
# c_str!(""),
# )?;
#
# let class = module.getattr("Foo")?;
Expand Down Expand Up @@ -100,6 +101,7 @@ The argument passed to `getattr` and `get_item` can also be configured:

```rust
use pyo3::prelude::*;
use pyo3_ffi::c_str;

#[derive(FromPyObject)]
struct RustyStruct {
Expand All @@ -111,14 +113,14 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let module = PyModule::from_code_bound(
# let module = PyModule::from_code(
# py,
# "class Foo(dict):
# c_str!("class Foo(dict):
# def __init__(self):
# self.name = 'test'
# self['key'] = 'test2'",
# "",
# "",
# self['key'] = 'test2'"),
# c_str!(""),
# c_str!(""),
# )?;
#
# let class = module.getattr("Foo")?;
Expand Down Expand Up @@ -262,6 +264,7 @@ attribute can be applied to single-field-variants.

```rust
use pyo3::prelude::*;
use pyo3_ffi::c_str;

#[derive(FromPyObject)]
# #[derive(Debug)]
Expand Down Expand Up @@ -339,15 +342,15 @@ enum RustyEnum<'py> {
# );
# }
# {
# let module = PyModule::from_code_bound(
# let module = PyModule::from_code(
# py,
# "class Foo(dict):
# c_str!("class Foo(dict):
# def __init__(self):
# self.x = 0
# self.y = 1
# self.z = 2",
# "",
# "",
# self.z = 2"),
# c_str!(""),
# c_str!(""),
# )?;
#
# let class = module.getattr("Foo")?;
Expand All @@ -364,14 +367,14 @@ enum RustyEnum<'py> {
# }
#
# {
# let module = PyModule::from_code_bound(
# let module = PyModule::from_code(
# py,
# "class Foo(dict):
# c_str!("class Foo(dict):
# def __init__(self):
# self.x = 3
# self.y = 4",
# "",
# "",
# self.y = 4"),
# c_str!(""),
# c_str!(""),
# )?;
#
# let class = module.getattr("Foo")?;
Expand Down
8 changes: 4 additions & 4 deletions guide/src/function/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(increment, py)?;
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
# .call1((fun,))?
# .call_method0("__str__")?
Expand Down Expand Up @@ -178,7 +178,7 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {
# Python::with_gil(|py| {
# let fun = pyo3::wrap_pyfunction!(increment, py)?;
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
# .call1((fun,))?
# .call_method0("__str__")?
Expand Down Expand Up @@ -221,7 +221,7 @@ fn add(a: u64, b: u64) -> u64 {
# let doc: String = fun.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
# .call1((fun,))?
# .call_method0("__str__")?
Expand Down Expand Up @@ -269,7 +269,7 @@ fn add(a: u64, b: u64) -> u64 {
# let doc: String = fun.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
#
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let sig: String = inspect
# .call1((fun,))?
# .call_method0("__str__")?
Expand Down
2 changes: 1 addition & 1 deletion guide/src/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn parent_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
}

fn register_child_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let child_module = PyModule::new_bound(parent_module.py(), "child_module")?;
let child_module = PyModule::new(parent_module.py(), "child_module")?;
child_module.add_function(wrap_pyfunction!(func, &child_module)?)?;
parent_module.add_submodule(&child_module)
}
Expand Down
57 changes: 31 additions & 26 deletions guide/src/python-from-rust/calling-existing-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

If you already have some existing Python code that you need to execute from Rust, the following FAQs can help you select the right PyO3 functionality for your situation:

## Want to access Python APIs? Then use `PyModule::import_bound`.
## Want to access Python APIs? Then use `PyModule::import`.

[`PyModule::import_bound`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.import_bound) can
[`PyModule::import`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.import) can
be used to get handle to a Python module from Rust. You can use this to import and use any Python
module available in your environment.

Expand All @@ -13,7 +13,7 @@ use pyo3::prelude::*;

fn main() -> PyResult<()> {
Python::with_gil(|py| {
let builtins = PyModule::import_bound(py, "builtins")?;
let builtins = PyModule::import(py, "builtins")?;
let total: i32 = builtins
.getattr("sum")?
.call1((vec![1, 2, 3],))?
Expand Down Expand Up @@ -95,9 +95,9 @@ assert userdata.as_tuple() == userdata_as_tuple
# }
```

## You have a Python file or code snippet? Then use `PyModule::from_code_bound`.
## You have a Python file or code snippet? Then use `PyModule::from_code`.

[`PyModule::from_code_bound`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.from_code_bound)
[`PyModule::from_code`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.from_code)
can be used to generate a Python module which can then be used just as if it was imported with
`PyModule::import`.

Expand All @@ -106,21 +106,22 @@ to this function!

```rust
use pyo3::{prelude::*, types::IntoPyDict};
use pyo3_ffi::c_str;

# fn main() -> PyResult<()> {
Python::with_gil(|py| {
let activators = PyModule::from_code_bound(
let activators = PyModule::from_code(
py,
r#"
c_str!(r#"
def relu(x):
"""see https://en.wikipedia.org/wiki/Rectifier_(neural_networks)"""
return max(0.0, x)
def leaky_relu(x, slope=0.01):
return x if x >= 0 else x * slope
"#,
"activators.py",
"activators",
"#),
c_str!("activators.py"),
c_str!("activators"),
)?;

let relu_result: f64 = activators.getattr("relu")?.call1((-1.0,))?.extract()?;
Expand Down Expand Up @@ -171,7 +172,7 @@ fn main() -> PyResult<()> {
```

If `append_to_inittab` cannot be used due to constraints in the program,
an alternative is to create a module using [`PyModule::new_bound`]
an alternative is to create a module using [`PyModule::new`]
and insert it manually into `sys.modules`:

```rust
Expand All @@ -186,11 +187,11 @@ pub fn add_one(x: i64) -> i64 {
fn main() -> PyResult<()> {
Python::with_gil(|py| {
// Create new module
let foo_module = PyModule::new_bound(py, "foo")?;
let foo_module = PyModule::new(py, "foo")?;
foo_module.add_function(wrap_pyfunction!(add_one, &foo_module)?)?;

// Import and get sys.modules
let sys = PyModule::import_bound(py, "sys")?;
let sys = PyModule::import(py, "sys")?;
let py_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?;

// Insert foo into sys.modules
Expand Down Expand Up @@ -249,16 +250,17 @@ The example below shows:
`src/main.rs`:
```rust,ignore
use pyo3::prelude::*;
use pyo3_ffi::c_str;
fn main() -> PyResult<()> {
let py_foo = include_str!(concat!(
let py_foo = c_str!(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/python_app/utils/foo.py"
));
let py_app = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/python_app/app.py"));
)));
let py_app = c_str!(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/python_app/app.py")));
let from_python = Python::with_gil(|py| -> PyResult<Py<PyAny>> {
PyModule::from_code_bound(py, py_foo, "utils.foo", "utils.foo")?;
let app: Py<PyAny> = PyModule::from_code_bound(py, py_app, "", "")?
PyModule::from_code(py, py_foo, c_str!("utils.foo"), c_str!("utils.foo"))?;
let app: Py<PyAny> = PyModule::from_code(py, py_app, c_str!(""), c_str!(""))?
.getattr("run")?
.into();
app.call0(py)
Expand All @@ -283,19 +285,21 @@ that directory is `/usr/share/python_app`).
```rust,no_run
use pyo3::prelude::*;
use pyo3::types::PyList;
use pyo3_ffi::c_str;
use std::fs;
use std::path::Path;
use std::ffi::CString;
fn main() -> PyResult<()> {
let path = Path::new("/usr/share/python_app");
let py_app = fs::read_to_string(path.join("app.py"))?;
let py_app = CString::new(fs::read_to_string(path.join("app.py"))?)?;
let from_python = Python::with_gil(|py| -> PyResult<Py<PyAny>> {
let syspath = py
.import("sys")?
.getattr("path")?
.downcast_into::<PyList>()?;
syspath.insert(0, &path)?;
let app: Py<PyAny> = PyModule::from_code_bound(py, &py_app, "", "")?
let app: Py<PyAny> = PyModule::from_code(py, py_app.as_c_str(), c_str!(""), c_str!(""))?
.getattr("run")?
.into();
app.call0(py)
Expand All @@ -316,12 +320,13 @@ Use context managers by directly invoking `__enter__` and `__exit__`.

```rust
use pyo3::prelude::*;
use pyo3_ffi::c_str;

fn main() {
Python::with_gil(|py| {
let custom_manager = PyModule::from_code_bound(
let custom_manager = PyModule::from_code(
py,
r#"
c_str!(r#"
class House(object):
def __init__(self, address):
self.address = address
Expand All @@ -333,9 +338,9 @@ class House(object):
else:
print(f"Thank you for visiting {self.address}, come again soon!")
"#,
"house.py",
"house",
"#),
c_str!("house.py"),
c_str!("house"),
)
.unwrap();

Expand Down Expand Up @@ -394,4 +399,4 @@ Python::with_gil(|py| -> PyResult<()> {
```


[`PyModule::new_bound`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.new_bound
[`PyModule::new`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PyModule.html#method.new
Loading

0 comments on commit f869d16

Please sign in to comment.