Fix various typos/omissions in guide.
This commit is contained in:
parent
5f7d67615c
commit
c91ed70bbc
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs.
|
PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs.
|
||||||
|
|
||||||
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or a fieldless `enum` (a.k.a. C-like enum) to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
|
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or a fieldless `enum` (a.k.a. C-like enum) to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
|
||||||
|
|
||||||
This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each:
|
This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each:
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ When you need to share ownership of data between Python and Rust, instead of usi
|
||||||
|
|
||||||
A Rust `struct Foo<T>` with a generic parameter `T` generates new compiled implementations each time it is used with a different concrete type for `T`. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping `Foo` in Python, where there needs to be a single compiled implementation of `Foo` which is integrated with the Python interpreter.
|
A Rust `struct Foo<T>` with a generic parameter `T` generates new compiled implementations each time it is used with a different concrete type for `T`. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping `Foo` in Python, where there needs to be a single compiled implementation of `Foo` which is integrated with the Python interpreter.
|
||||||
|
|
||||||
#### Must be send
|
#### Must be Send
|
||||||
|
|
||||||
Because Python objects are freely shared between threads by the Python interpreter, there is no guarantee which thread will eventually drop the object. Therefore all types annotated with `#[pyclass]` must implement `Send` (unless annotated with [`#[pyclass(unsendable)]`](#customizing-the-class)).
|
Because Python objects are freely shared between threads by the Python interpreter, there is no guarantee which thread will eventually drop the object. Therefore all types annotated with `#[pyclass]` must implement `Send` (unless annotated with [`#[pyclass(unsendable)]`](#customizing-the-class)).
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ For arguments, see the [`Method arguments`](#method-arguments) section below.
|
||||||
|
|
||||||
## Adding the class to a module
|
## Adding the class to a module
|
||||||
|
|
||||||
The next step is to create the module initializer and add our class to it
|
The next step is to create the module initializer and add our class to it:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# use pyo3::prelude::*;
|
# use pyo3::prelude::*;
|
||||||
|
@ -219,7 +219,7 @@ These parameters are covered in various sections of this guide.
|
||||||
|
|
||||||
### Return type
|
### Return type
|
||||||
|
|
||||||
Generally, `#[new]` method have to return `T: Into<PyClassInitializer<Self>>` or
|
Generally, `#[new]` methods have to return `T: Into<PyClassInitializer<Self>>` or
|
||||||
`PyResult<T> where T: Into<PyClassInitializer<Self>>`.
|
`PyResult<T> where T: Into<PyClassInitializer<Self>>`.
|
||||||
|
|
||||||
For constructors that may fail, you should wrap the return type in a PyResult as well.
|
For constructors that may fail, you should wrap the return type in a PyResult as well.
|
||||||
|
|
|
@ -88,7 +88,8 @@ Traceback (most recent call last):
|
||||||
ValueError: invalid digit found in string
|
ValueError: invalid digit found in string
|
||||||
```
|
```
|
||||||
|
|
||||||
As a more complete example, the following snippet defines a Rust error named `CustomIOError`. It then defines a `From<CustomIOError> for PyErr`, which returns a `PyErr` representing Python's `OSError`. Finally, it
|
As a more complete example, the following snippet defines a Rust error named `CustomIOError`. It then defines a `From<CustomIOError> for PyErr`, which returns a `PyErr` representing Python's `OSError`.
|
||||||
|
Therefore, it can use this error in the result of a `#[pyfunction]` directly, relying on the conversion if it has to be propagated into a Python exception.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use pyo3::exceptions::PyOSError;
|
use pyo3::exceptions::PyOSError;
|
||||||
|
@ -125,6 +126,7 @@ fn bind(addr: String) -> Result<Connection, CustomIOError> {
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn connect(s: String) -> Result<(), CustomIOError> {
|
fn connect(s: String) -> Result<(), CustomIOError> {
|
||||||
bind(s)?;
|
bind(s)?;
|
||||||
|
// etc.
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ num=44
|
||||||
num=-1
|
num=-1
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: for keywords like `struct`, to use it as a function argument, use "raw ident" syntax `r#struct` in both the signature and the function definition:
|
> Note: to use keywords like `struct` as a function argument, use "raw identifier" syntax `r#struct` in both the signature and the function definition:
|
||||||
>
|
>
|
||||||
> ```rust
|
> ```rust
|
||||||
> # #![allow(dead_code)]
|
> # #![allow(dead_code)]
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
To get started using PyO3 you will need three things: a rust toolchain, a python environment, and a way to build. We'll cover each of these below.
|
To get started using PyO3 you will need three things: a Rust toolchain, a Python environment, and a way to build. We'll cover each of these below.
|
||||||
|
|
||||||
## Rust
|
## Rust
|
||||||
|
|
||||||
First, make sure you have rust installed on your system. If you haven't already done so you can do so by following the instructions [here](https://www.rust-lang.org/tools/install). PyO3 runs on both the `stable` and `nightly` versions so you can choose whichever one fits you best. The minimum required rust version is Rust 1.48.
|
First, make sure you have Rust installed on your system. If you haven't already done so, try following the instructions [here](https://www.rust-lang.org/tools/install). PyO3 runs on both the `stable` and `nightly` versions so you can choose whichever one fits you best. The minimum required Rust version is 1.48.
|
||||||
|
|
||||||
if you can run `rustc --version` and the version is high enough you're good to go!
|
If you can run `rustc --version` and the version is new enough you're good to go!
|
||||||
|
|
||||||
## Python
|
## Python
|
||||||
|
|
||||||
To use PyO3 you need at least Python 3.7. While you can simply use the default Python version on your system, it is recommended to use a virtual environment.
|
To use PyO3, you need at least Python 3.7. While you can simply use the default Python interpreter on your system, it is recommended to use a virtual environment.
|
||||||
|
|
||||||
## Virtualenvs
|
## Virtualenvs
|
||||||
|
|
||||||
While you can use any virtualenv manager you like, we recommend the use of `pyenv` especially if you want to develop or test for multiple different python versions, so that is what the examples in this book will use. The installation instructions for `pyenv` can be found [here](https://github.com/pyenv/pyenv#getting-pyenv).
|
While you can use any virtualenv manager you like, we recommend the use of `pyenv` in particular if you want to develop or test for multiple different Python versions, so that is what the examples in this book will use. The installation instructions for `pyenv` can be found [here](https://github.com/pyenv/pyenv#getting-pyenv).
|
||||||
|
|
||||||
Note that when using `pyenv` you should also set the following environment variable
|
Note that when using `pyenv`, you should also set the following environment variable:
|
||||||
```bash
|
```bash
|
||||||
PYTHON_CONFIGURE_OPTS="--enable-shared"
|
PYTHON_CONFIGURE_OPTS="--enable-shared"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
There are a number of build and python package management systems such as [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) or [manually](https://pyo3.rs/latest/building_and_distribution.html#manual-builds) we recommend the use of `maturin` which you can install [here](https://maturin.rs/installation.html). It is developed to work with PyO3 and is the most "batteries included" experience. `maturin` is just a python package so you can add it in any way that you install python packages.
|
There are a number of build and Python package management systems such as [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) or [manually](https://pyo3.rs/latest/building_and_distribution.html#manual-builds). We recommend the use of `maturin`, which you can install [here](https://maturin.rs/installation.html). It is developed to work with PyO3 and provides the most "batteries included" experience. `maturin` is just a Python package, so you can add it in the same you already install Python packages.
|
||||||
|
|
||||||
System Python:
|
System Python:
|
||||||
```bash
|
```bash
|
||||||
|
@ -47,11 +46,11 @@ poetry:
|
||||||
poetry add -D maturin
|
poetry add -D maturin
|
||||||
```
|
```
|
||||||
|
|
||||||
after installation, you can run `maturin --version` to check that you have correctly installed it.
|
After installation, you can run `maturin --version` to check that you have correctly installed it.
|
||||||
|
|
||||||
# Starting a new project
|
# Starting a new project
|
||||||
|
|
||||||
Firstly you should create the folder and virtual environment that are going to contain your new project. Here we will use the recommended `pyenv`:
|
First you should create the folder and virtual environment that are going to contain your new project. Here we will use the recommended `pyenv`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir pyo3-example
|
mkdir pyo3-example
|
||||||
|
@ -59,19 +58,20 @@ cd pyo3-example
|
||||||
pyenv virtualenv pyo3
|
pyenv virtualenv pyo3
|
||||||
pyenv local pyo3
|
pyenv local pyo3
|
||||||
```
|
```
|
||||||
after this, you should install your build manager. In this example, we will use `maturin`. After you've activated your virtualenv add `maturin` to it:
|
|
||||||
|
After this, you should install your build manager. In this example, we will use `maturin`. After you've activated your virtualenv, add `maturin` to it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install maturin
|
pip install maturin
|
||||||
```
|
```
|
||||||
|
|
||||||
After this, you can initialise the new project
|
Now you can initialise the new project:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
maturin init
|
maturin init
|
||||||
```
|
```
|
||||||
|
|
||||||
If `maturin` is already installed you can create a new project using that directly as well:
|
If `maturin` is already installed, you can create a new project using that directly as well:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
maturin new -b pyo3 pyo3-example
|
maturin new -b pyo3 pyo3-example
|
||||||
|
@ -82,16 +82,16 @@ pyenv local pyo3
|
||||||
|
|
||||||
# Adding to an existing project
|
# Adding to an existing project
|
||||||
|
|
||||||
Sadly currently `maturin` cannot be run in existing projects, so if you want to use python in an existing project you basically have two options:
|
Sadly, `maturin` cannot currently be run in existing projects, so if you want to use Python in an existing project you basically have two options:
|
||||||
|
|
||||||
1. create a new project as above and move your existing code into that project
|
1. Create a new project as above and move your existing code into that project
|
||||||
2. Manually edit your project configuration as necessary.
|
2. Manually edit your project configuration as necessary
|
||||||
|
|
||||||
If you are opting for the second option, here are the things you need to pay attention to:
|
If you opt for the second option, here are the things you need to pay attention to:
|
||||||
|
|
||||||
## Cargo.toml
|
## Cargo.toml
|
||||||
|
|
||||||
Make sure that the rust you want to be able to access from Python is compiled into a library. You can have a binary output as well, but the code you want to access from python has to be in the library. Also, make sure that the crate type is `cdylib` and add PyO3 as a dependency as so:
|
Make sure that the Rust crate you want to be able to access from Python is compiled into a library. You can have a binary output as well, but the code you want to access from Python has to be in the library part. Also, make sure that the crate type is `cdylib` and add PyO3 as a dependency as so:
|
||||||
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
@ -138,7 +138,7 @@ classifiers = [
|
||||||
|
|
||||||
## Running code
|
## Running code
|
||||||
|
|
||||||
After this you can setup rust code to be available in python as below; for example, you can place this code in `src/lib.rs`:
|
After this you can setup Rust code to be available in Python as below; for example, you can place this code in `src/lib.rs`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
@ -159,7 +159,7 @@ fn pyo3_example(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
After this you can run `maturin develop` to prepare the python package after which you can use it like so:
|
Now you can run `maturin develop` to prepare the Python package, after which you can use it like so:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ maturin develop
|
$ maturin develop
|
||||||
|
@ -170,4 +170,4 @@ $ python
|
||||||
'25'
|
'25'
|
||||||
```
|
```
|
||||||
|
|
||||||
For more instructions on how to use python code from rust see the [Python from Rust](python_from_rust.md) page.
|
For more instructions on how to use Python code from Rust, see the [Python from Rust](python_from_rust.md) page.
|
||||||
|
|
|
@ -122,11 +122,12 @@ for more information on safety.
|
||||||
## GIL-independent memory
|
## GIL-independent memory
|
||||||
|
|
||||||
Sometimes we need a reference to memory on Python's heap that can outlive the
|
Sometimes we need a reference to memory on Python's heap that can outlive the
|
||||||
GIL. Python's `Py<PyAny>` is analogous to `Rc<T>`, but for variables whose
|
GIL. Python's `Py<PyAny>` is analogous to `Arc<T>`, but for variables whose
|
||||||
memory is allocated on Python's heap. Cloning a `Py<PyAny>` increases its
|
memory is allocated on Python's heap. Cloning a `Py<PyAny>` increases its
|
||||||
internal reference count just like cloning `Rc<T>`. The smart pointer can
|
internal reference count just like cloning `Arc<T>`. The smart pointer can
|
||||||
outlive the GIL from which it was created. It isn't magic, though. We need to
|
outlive the "GIL is held" period in which it was created. It isn't magic,
|
||||||
reacquire the GIL to access the memory pointed to by the `Py<PyAny>`.
|
though. We need to reacquire the GIL to access the memory pointed to by the
|
||||||
|
`Py<PyAny>`.
|
||||||
|
|
||||||
What happens to the memory when the last `Py<PyAny>` is dropped and its
|
What happens to the memory when the last `Py<PyAny>` is dropped and its
|
||||||
reference count reaches zero? It depends whether or not we are holding the GIL.
|
reference count reaches zero? It depends whether or not we are holding the GIL.
|
||||||
|
|
|
@ -4,7 +4,7 @@ PyO3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython)
|
||||||
|
|
||||||
## Macros
|
## Macros
|
||||||
|
|
||||||
While rust-cpython has a `macro_rules!` based dsl for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions.
|
While rust-cpython has a `macro_rules!` based DSL for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions.
|
||||||
|
|
||||||
**rust-cpython**
|
**rust-cpython**
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue