Skip to content

Commit

Permalink
Merge #2678
Browse files Browse the repository at this point in the history
2678: github pages example for sys.dict modules r=davidhewitt a=flickpp

Add an example into `python_from_rust.md` for creating a module and inserting it into the `sys.modules` dictionary.

As discussed here:
GH-2649 example for inserting moulde in sys.dict

Co-authored-by: James Welchman <jamesw@plantpot.ai>
Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 6, 2023
2 parents c09d24e + 8651d3b commit 94c568d
Showing 1 changed file with 71 additions and 3 deletions.
74 changes: 71 additions & 3 deletions guide/src/python_from_rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,71 @@ def leaky_relu(x, slope=0.01):
# }
```

### Want to embed Python in Rust with additional modules?

Python maintains the `sys.modules` dict as a cache of all imported modules.
An import in Python will first attempt to lookup the module from this dict,
and if not present will use various strategies to attempt to locate and load
the module.

The [`append_to_inittab`]({{*PYO3_DOCS_URL}}/pyo3/macro.append_to_inittab.html)
macro can be used to add additional `#[pymodule]` modules to an embedded
Python interpreter. The macro **must** be invoked _before_ initializing Python.

As an example, the below adds the module `foo` to the embedded interpreter:

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

#[pyfunction]
fn add_one(x: i64) -> i64 {
x + 1
}

#[pymodule]
fn foo(_py: Python<'_>, foo_module: &PyModule) -> PyResult<()> {
foo_module.add_function(wrap_pyfunction!(add_one, foo_module)?)?;
Ok(())
}

fn main() -> PyResult<()> {
pyo3::append_to_inittab!(foo);
Python::with_gil(|py| Python::run(py, "import foo; foo.add_one(6)", None, None))
}
```

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

```rust
use pyo3::prelude::*;
use pyo3::types::PyDict;

#[pyfunction]
pub fn add_one(x: i64) -> i64 {
x + 1
}

fn main() -> PyResult<()> {
Python::with_gil(|py| {
// Create new module
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(py, "sys")?;
let py_modules: &PyDict = sys.getattr("modules")?.downcast()?;

// Insert foo into sys.modules
py_modules.set_item("foo", foo_module)?;

// Now we can import + run our python code
Python::run(py, "import foo; foo.add_one(6)", None, None)
})
}
```

### Include multiple Python files

You can include a file at compile time by using
Expand All @@ -262,9 +327,9 @@ Example directory structure:
├── Cargo.lock
├── Cargo.toml
├── python_app
   ├── app.py
   └── utils
   └── foo.py
├── app.py
└── utils
└── foo.py
└── src
└── main.rs
```
Expand Down Expand Up @@ -395,3 +460,6 @@ class House(object):
})
}
```


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

0 comments on commit 94c568d

Please sign in to comment.