Merge pull request #1141 from davidhewitt/guide-subchapters
Start splitting long guide pages into subchapters
This commit is contained in:
commit
3c24d27a66
|
@ -7,7 +7,10 @@
|
||||||
- [Python Modules](module.md)
|
- [Python Modules](module.md)
|
||||||
- [Python Functions](function.md)
|
- [Python Functions](function.md)
|
||||||
- [Python Classes](class.md)
|
- [Python Classes](class.md)
|
||||||
|
- [Class customizations](class/protocols.md)
|
||||||
- [Type Conversions](conversions.md)
|
- [Type Conversions](conversions.md)
|
||||||
|
- [Mapping of Rust types to Python types](conversions/tables.md)]
|
||||||
|
- [Conversion traits](conversions/traits.md)]
|
||||||
- [Python Exceptions](exception.md)
|
- [Python Exceptions](exception.md)
|
||||||
- [Calling Python from Rust](python_from_rust.md)
|
- [Calling Python from Rust](python_from_rust.md)
|
||||||
- [GIL, mutability and object types](types.md)
|
- [GIL, mutability and object types](types.md)
|
||||||
|
|
|
@ -15,7 +15,7 @@ For ease of discovery, below is a list of all custom attributes with links to th
|
||||||
- [`#[call]`](#callable-objects)
|
- [`#[call]`](#callable-objects)
|
||||||
- [`#[classattr]`](#class-attributes)
|
- [`#[classattr]`](#class-attributes)
|
||||||
- [`#[args]`](#method-arguments)
|
- [`#[args]`](#method-arguments)
|
||||||
- [`#[pyproto]`](#class-customizations)
|
- [`#[pyproto]`](class/protocols.html)
|
||||||
|
|
||||||
## Defining a new class
|
## Defining a new class
|
||||||
|
|
||||||
|
@ -695,307 +695,6 @@ num=44, debug=false
|
||||||
num=-1, debug=false
|
num=-1, debug=false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class customizations
|
|
||||||
|
|
||||||
Python's object model defines several protocols for different object behavior, like sequence,
|
|
||||||
mapping or number protocols. PyO3 defines separate traits for each of them. To provide specific
|
|
||||||
Python object behavior, you need to implement the specific trait for your struct. Important note,
|
|
||||||
each protocol implementation block has to be annotated with the `#[pyproto]` attribute.
|
|
||||||
|
|
||||||
All `#[pyproto]` methods which can be defined below can return `T` instead of `PyResult<T>` if the
|
|
||||||
method implementation is infallible. In addition, if the return type is `()`, it can be omitted altogether.
|
|
||||||
|
|
||||||
### Basic object customization
|
|
||||||
|
|
||||||
The [`PyObjectProtocol`] trait provides several basic customizations.
|
|
||||||
|
|
||||||
#### Attribute access
|
|
||||||
|
|
||||||
To customize object attribute access, define the following methods:
|
|
||||||
|
|
||||||
* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPy<PyObject>>`
|
|
||||||
* `fn __setattr__(&mut self, name: FromPyObject, value: FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __delattr__(&mut self, name: FromPyObject) -> PyResult<()>`
|
|
||||||
|
|
||||||
Each method corresponds to Python's `self.attr`, `self.attr = value` and `del self.attr` code.
|
|
||||||
|
|
||||||
#### String Conversions
|
|
||||||
|
|
||||||
* `fn __repr__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
|
||||||
* `fn __str__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
|
||||||
|
|
||||||
Possible return types for `__str__` and `__repr__` are `PyResult<String>` or `PyResult<PyString>`.
|
|
||||||
|
|
||||||
* `fn __bytes__(&self) -> PyResult<PyBytes>`
|
|
||||||
|
|
||||||
Provides the conversion to `bytes`.
|
|
||||||
|
|
||||||
* `fn __format__(&self, format_spec: &str) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
|
||||||
|
|
||||||
Special method that is used by the `format()` builtin and the `str.format()` method.
|
|
||||||
Possible return types are `PyResult<String>` or `PyResult<PyString>`.
|
|
||||||
|
|
||||||
#### Comparison operators
|
|
||||||
|
|
||||||
* `fn __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
Overloads Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`).
|
|
||||||
The `op` argument indicates the comparison operation being performed.
|
|
||||||
The return type will normally be `PyResult<bool>`, but any Python object can be returned.
|
|
||||||
If `other` is not of the type specified in the signature, the generated code will
|
|
||||||
automatically `return NotImplemented`.
|
|
||||||
|
|
||||||
* `fn __hash__(&self) -> PyResult<impl PrimInt>`
|
|
||||||
|
|
||||||
Objects that compare equal must have the same hash value.
|
|
||||||
The return type must be `PyResult<T>` where `T` is one of Rust's primitive integer types.
|
|
||||||
|
|
||||||
#### Other methods
|
|
||||||
|
|
||||||
* `fn __bool__(&self) -> PyResult<bool>`
|
|
||||||
|
|
||||||
Determines the "truthyness" of the object.
|
|
||||||
|
|
||||||
### Emulating numeric types
|
|
||||||
|
|
||||||
The [`PyNumberProtocol`] trait allows [emulate numeric types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types).
|
|
||||||
|
|
||||||
* `fn __add__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __sub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __mul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __matmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __truediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __floordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __mod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __divmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __pow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __lshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __and__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __or__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __xor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
These methods are called to implement the binary arithmetic operations
|
|
||||||
(`+`, `-`, `*`, `@`, `/`, `//`, `%`, `divmod()`, `pow()` and `**`, `<<`, `>>`, `&`, `^`, and `|`).
|
|
||||||
|
|
||||||
If `rhs` is not of the type specified in the signature, the generated code
|
|
||||||
will automatically `return NotImplemented`. This is not the case for `lhs`
|
|
||||||
which must match signature or else raise a TypeError.
|
|
||||||
|
|
||||||
|
|
||||||
The reflected operations are also available:
|
|
||||||
|
|
||||||
* `fn __radd__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rsub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rmatmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rtruediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rfloordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rdivmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rpow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rlshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rrshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rand__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __ror__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __rxor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
The code generated for these methods expect that all arguments match the
|
|
||||||
signature, or raise a TypeError.
|
|
||||||
|
|
||||||
*Note*: Currently implementing the method for a binary arithmetic operations
|
|
||||||
(e.g, `__add__`) shadows the reflected operation (e.g, `__radd__`). This is
|
|
||||||
being addressed in [#844](https://github.com/PyO3/pyo3/issues/844). to make
|
|
||||||
these methods
|
|
||||||
|
|
||||||
|
|
||||||
This trait also has support the augmented arithmetic assignments (`+=`, `-=`,
|
|
||||||
`*=`, `@=`, `/=`, `//=`, `%=`, `**=`, `<<=`, `>>=`, `&=`, `^=`, `|=`):
|
|
||||||
|
|
||||||
* `fn __iadd__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __isub__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __imul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __imatmul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __itruediv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __ifloordiv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __imod__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __ipow__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __ilshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __irshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __iand__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __ior__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
* `fn __ixor__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
|
||||||
|
|
||||||
The following methods implement the unary arithmetic operations (`-`, `+`, `abs()` and `~`):
|
|
||||||
|
|
||||||
* `fn __neg__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __pos__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __abs__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __invert__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
Support for coercions:
|
|
||||||
|
|
||||||
* `fn __complex__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __int__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __float__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
Other:
|
|
||||||
|
|
||||||
* `fn __index__(&'p self) -> PyResult<impl ToPyObject>`
|
|
||||||
* `fn __round__(&'p self, ndigits: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
|
||||||
|
|
||||||
### Garbage Collector Integration
|
|
||||||
|
|
||||||
If your type owns references to other Python objects, you will need to
|
|
||||||
integrate with Python's garbage collector so that the GC is aware of
|
|
||||||
those references.
|
|
||||||
To do this, implement the [`PyGCProtocol`] trait for your struct.
|
|
||||||
It includes two methods `__traverse__` and `__clear__`.
|
|
||||||
These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API.
|
|
||||||
`__traverse__` must call `visit.call()` for each reference to another Python object.
|
|
||||||
`__clear__` must clear out any mutable references to other Python objects
|
|
||||||
(thus breaking reference cycles). Immutable references do not have to be cleared,
|
|
||||||
as every cycle must contain at least one mutable reference.
|
|
||||||
Example:
|
|
||||||
```rust
|
|
||||||
extern crate pyo3;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::PyTraverseError;
|
|
||||||
use pyo3::gc::{PyGCProtocol, PyVisit};
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct ClassWithGCSupport {
|
|
||||||
obj: Option<PyObject>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyproto]
|
|
||||||
impl PyGCProtocol for ClassWithGCSupport {
|
|
||||||
fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> {
|
|
||||||
if let Some(ref obj) = self.obj {
|
|
||||||
visit.call(obj)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __clear__(&mut self) {
|
|
||||||
// Clear reference, this decrements ref counter.
|
|
||||||
self.obj = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Special protocol trait implementations have to be annotated with the `#[pyproto]` attribute.
|
|
||||||
|
|
||||||
It is also possible to enable GC for custom classes using the `gc` parameter of the `pyclass` attribute.
|
|
||||||
i.e. `#[pyclass(gc)]`. In that case instances of custom class participate in Python garbage
|
|
||||||
collection, and it is possible to track them with `gc` module methods. When using the `gc` parameter,
|
|
||||||
it is *required* to implement the `PyGCProtocol` trait, failure to do so will result in an error
|
|
||||||
at compile time:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
#[pyclass(gc)]
|
|
||||||
struct GCTracked {} // Fails because it does not implement PyGCProtocol
|
|
||||||
```
|
|
||||||
|
|
||||||
### Iterator Types
|
|
||||||
|
|
||||||
Iterators can be defined using the
|
|
||||||
[`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait.
|
|
||||||
It includes two methods `__iter__` and `__next__`:
|
|
||||||
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<PyObject>>`
|
|
||||||
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
|
|
||||||
|
|
||||||
Returning `None` from `__next__` indicates that that there are no further items.
|
|
||||||
These two methods can be take either `PyRef<Self>` or `PyRefMut<Self>` as their
|
|
||||||
first argument, so that mutable borrow can be avoided if needed.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::PyIterProtocol;
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct MyIterator {
|
|
||||||
iter: Box<Iterator<Item = PyObject> + Send>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyproto]
|
|
||||||
impl PyIterProtocol for MyIterator {
|
|
||||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
|
||||||
slf
|
|
||||||
}
|
|
||||||
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
|
||||||
slf.iter.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In many cases you'll have a distinction between the type being iterated over (i.e. the *iterable*) and the iterator it
|
|
||||||
provides. In this case, you should implement `PyIterProtocol` for both the iterable and the iterator, but the iterable
|
|
||||||
only needs to support `__iter__()` while the iterator must support both `__iter__()` and `__next__()`. The default
|
|
||||||
implementations in `PyIterProtocol` will ensure that the objects behave correctly in Python. For example:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# use pyo3::prelude::*;
|
|
||||||
# use pyo3::PyIterProtocol;
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct Iter {
|
|
||||||
inner: std::vec::IntoIter<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyproto]
|
|
||||||
impl PyIterProtocol for Iter {
|
|
||||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
|
||||||
slf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __next__(mut slf: PyRefMut<Self>) -> Option<usize> {
|
|
||||||
slf.inner.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct Container {
|
|
||||||
iter: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyproto]
|
|
||||||
impl PyIterProtocol for Container {
|
|
||||||
fn __iter__(slf: PyRef<Self>) -> PyResult<Py<Iter>> {
|
|
||||||
let iter = Iter {
|
|
||||||
inner: slf.iter.clone().into_iter(),
|
|
||||||
};
|
|
||||||
Py::new(slf.py(), iter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# let gil = Python::acquire_gil();
|
|
||||||
# let py = gil.python();
|
|
||||||
# let inst = pyo3::PyCell::new(
|
|
||||||
# py,
|
|
||||||
# Container {
|
|
||||||
# iter: vec![1, 2, 3, 4],
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
# .unwrap();
|
|
||||||
# pyo3::py_run!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
|
|
||||||
# pyo3::py_run!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
|
|
||||||
```
|
|
||||||
|
|
||||||
For more details on Python's iteration protocols, check out [the "Iterator Types" section of the library
|
|
||||||
documentation](https://docs.python.org/3/library/stdtypes.html#iterator-types).
|
|
||||||
|
|
||||||
#### Returning a value from iteration
|
|
||||||
|
|
||||||
This guide has so far shown how to use `Option<T>` to implement yielding values during iteration.
|
|
||||||
In Python a generator can also return a value. To express this in Rust, PyO3 provides the
|
|
||||||
[`IterNextOutput`](https://docs.rs/pyo3/latest/pyo3/class/iter/enum.IterNextOutput.html) enum to
|
|
||||||
both `Yield` values and `Return` a final value - see its docs for further details and an example.
|
|
||||||
|
|
||||||
|
|
||||||
## How methods are implemented
|
## How methods are implemented
|
||||||
|
|
||||||
Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a
|
Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a
|
||||||
|
@ -1089,8 +788,6 @@ impl pyo3::pyclass::PyClassSend for MyClass {
|
||||||
|
|
||||||
|
|
||||||
[`GILGuard`]: https://docs.rs/pyo3/latest/pyo3/struct.GILGuard.html
|
[`GILGuard`]: https://docs.rs/pyo3/latest/pyo3/struct.GILGuard.html
|
||||||
[`PyGCProtocol`]: https://docs.rs/pyo3/latest/pyo3/class/gc/trait.PyGCProtocol.html
|
|
||||||
[`PyObjectProtocol`]: https://docs.rs/pyo3/latest/pyo3/class/basic/trait.PyObjectProtocol.html
|
|
||||||
[`PyTypeInfo`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeInfo.html
|
[`PyTypeInfo`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeInfo.html
|
||||||
[`PyTypeObject`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeObject.html
|
[`PyTypeObject`]: https://docs.rs/pyo3/latest/pyo3/type_object/trait.PyTypeObject.html
|
||||||
|
|
||||||
|
|
303
guide/src/class/protocols.md
Normal file
303
guide/src/class/protocols.md
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
## Class customizations
|
||||||
|
|
||||||
|
Python's object model defines several protocols for different object behavior, like sequence,
|
||||||
|
mapping or number protocols. PyO3 defines separate traits for each of them. To provide specific
|
||||||
|
Python object behavior, you need to implement the specific trait for your struct. Important note,
|
||||||
|
each protocol implementation block has to be annotated with the `#[pyproto]` attribute.
|
||||||
|
|
||||||
|
All `#[pyproto]` methods which can be defined below can return `T` instead of `PyResult<T>` if the
|
||||||
|
method implementation is infallible. In addition, if the return type is `()`, it can be omitted altogether.
|
||||||
|
|
||||||
|
### Basic object customization
|
||||||
|
|
||||||
|
The [`PyObjectProtocol`] trait provides several basic customizations.
|
||||||
|
|
||||||
|
#### Attribute access
|
||||||
|
|
||||||
|
To customize object attribute access, define the following methods:
|
||||||
|
|
||||||
|
* `fn __getattr__(&self, name: FromPyObject) -> PyResult<impl IntoPy<PyObject>>`
|
||||||
|
* `fn __setattr__(&mut self, name: FromPyObject, value: FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __delattr__(&mut self, name: FromPyObject) -> PyResult<()>`
|
||||||
|
|
||||||
|
Each method corresponds to Python's `self.attr`, `self.attr = value` and `del self.attr` code.
|
||||||
|
|
||||||
|
#### String Conversions
|
||||||
|
|
||||||
|
* `fn __repr__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||||
|
* `fn __str__(&self) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||||
|
|
||||||
|
Possible return types for `__str__` and `__repr__` are `PyResult<String>` or `PyResult<PyString>`.
|
||||||
|
|
||||||
|
* `fn __bytes__(&self) -> PyResult<PyBytes>`
|
||||||
|
|
||||||
|
Provides the conversion to `bytes`.
|
||||||
|
|
||||||
|
* `fn __format__(&self, format_spec: &str) -> PyResult<impl ToPyObject<ObjectType=PyString>>`
|
||||||
|
|
||||||
|
Special method that is used by the `format()` builtin and the `str.format()` method.
|
||||||
|
Possible return types are `PyResult<String>` or `PyResult<PyString>`.
|
||||||
|
|
||||||
|
#### Comparison operators
|
||||||
|
|
||||||
|
* `fn __richcmp__(&self, other: impl FromPyObject, op: CompareOp) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
Overloads Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`).
|
||||||
|
The `op` argument indicates the comparison operation being performed.
|
||||||
|
The return type will normally be `PyResult<bool>`, but any Python object can be returned.
|
||||||
|
If `other` is not of the type specified in the signature, the generated code will
|
||||||
|
automatically `return NotImplemented`.
|
||||||
|
|
||||||
|
* `fn __hash__(&self) -> PyResult<impl PrimInt>`
|
||||||
|
|
||||||
|
Objects that compare equal must have the same hash value.
|
||||||
|
The return type must be `PyResult<T>` where `T` is one of Rust's primitive integer types.
|
||||||
|
|
||||||
|
#### Other methods
|
||||||
|
|
||||||
|
* `fn __bool__(&self) -> PyResult<bool>`
|
||||||
|
|
||||||
|
Determines the "truthyness" of the object.
|
||||||
|
|
||||||
|
### Emulating numeric types
|
||||||
|
|
||||||
|
The [`PyNumberProtocol`] trait allows [emulate numeric types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types).
|
||||||
|
|
||||||
|
* `fn __add__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __sub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __mul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __matmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __truediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __floordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __mod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __divmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __pow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __lshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __and__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __or__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __xor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
These methods are called to implement the binary arithmetic operations
|
||||||
|
(`+`, `-`, `*`, `@`, `/`, `//`, `%`, `divmod()`, `pow()` and `**`, `<<`, `>>`, `&`, `^`, and `|`).
|
||||||
|
|
||||||
|
If `rhs` is not of the type specified in the signature, the generated code
|
||||||
|
will automatically `return NotImplemented`. This is not the case for `lhs`
|
||||||
|
which must match signature or else raise a TypeError.
|
||||||
|
|
||||||
|
|
||||||
|
The reflected operations are also available:
|
||||||
|
|
||||||
|
* `fn __radd__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rsub__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rmatmul__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rtruediv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rfloordiv__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rdivmod__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rpow__(lhs: impl FromPyObject, rhs: impl FromPyObject, modulo: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rlshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rrshift__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rand__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __ror__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __rxor__(lhs: impl FromPyObject, rhs: impl FromPyObject) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
The code generated for these methods expect that all arguments match the
|
||||||
|
signature, or raise a TypeError.
|
||||||
|
|
||||||
|
*Note*: Currently implementing the method for a binary arithmetic operations
|
||||||
|
(e.g, `__add__`) shadows the reflected operation (e.g, `__radd__`). This is
|
||||||
|
being addressed in [#844](https://github.com/PyO3/pyo3/issues/844). to make
|
||||||
|
these methods
|
||||||
|
|
||||||
|
|
||||||
|
This trait also has support the augmented arithmetic assignments (`+=`, `-=`,
|
||||||
|
`*=`, `@=`, `/=`, `//=`, `%=`, `**=`, `<<=`, `>>=`, `&=`, `^=`, `|=`):
|
||||||
|
|
||||||
|
* `fn __iadd__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __isub__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __imul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __imatmul__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __itruediv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __ifloordiv__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __imod__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __ipow__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __ilshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __irshift__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __iand__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __ior__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
* `fn __ixor__(&'p mut self, other: impl FromPyObject) -> PyResult<()>`
|
||||||
|
|
||||||
|
The following methods implement the unary arithmetic operations (`-`, `+`, `abs()` and `~`):
|
||||||
|
|
||||||
|
* `fn __neg__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __pos__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __abs__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __invert__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
Support for coercions:
|
||||||
|
|
||||||
|
* `fn __complex__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __int__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __float__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
Other:
|
||||||
|
|
||||||
|
* `fn __index__(&'p self) -> PyResult<impl ToPyObject>`
|
||||||
|
* `fn __round__(&'p self, ndigits: Option<impl FromPyObject>) -> PyResult<impl ToPyObject>`
|
||||||
|
|
||||||
|
### Garbage Collector Integration
|
||||||
|
|
||||||
|
If your type owns references to other Python objects, you will need to
|
||||||
|
integrate with Python's garbage collector so that the GC is aware of
|
||||||
|
those references.
|
||||||
|
To do this, implement the [`PyGCProtocol`] trait for your struct.
|
||||||
|
It includes two methods `__traverse__` and `__clear__`.
|
||||||
|
These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API.
|
||||||
|
`__traverse__` must call `visit.call()` for each reference to another Python object.
|
||||||
|
`__clear__` must clear out any mutable references to other Python objects
|
||||||
|
(thus breaking reference cycles). Immutable references do not have to be cleared,
|
||||||
|
as every cycle must contain at least one mutable reference.
|
||||||
|
Example:
|
||||||
|
```rust
|
||||||
|
extern crate pyo3;
|
||||||
|
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::PyTraverseError;
|
||||||
|
use pyo3::gc::{PyGCProtocol, PyVisit};
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct ClassWithGCSupport {
|
||||||
|
obj: Option<PyObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyGCProtocol for ClassWithGCSupport {
|
||||||
|
fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> {
|
||||||
|
if let Some(ref obj) = self.obj {
|
||||||
|
visit.call(obj)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __clear__(&mut self) {
|
||||||
|
// Clear reference, this decrements ref counter.
|
||||||
|
self.obj = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Special protocol trait implementations have to be annotated with the `#[pyproto]` attribute.
|
||||||
|
|
||||||
|
It is also possible to enable GC for custom classes using the `gc` parameter of the `pyclass` attribute.
|
||||||
|
i.e. `#[pyclass(gc)]`. In that case instances of custom class participate in Python garbage
|
||||||
|
collection, and it is possible to track them with `gc` module methods. When using the `gc` parameter,
|
||||||
|
it is *required* to implement the `PyGCProtocol` trait, failure to do so will result in an error
|
||||||
|
at compile time:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
#[pyclass(gc)]
|
||||||
|
struct GCTracked {} // Fails because it does not implement PyGCProtocol
|
||||||
|
```
|
||||||
|
|
||||||
|
### Iterator Types
|
||||||
|
|
||||||
|
Iterators can be defined using the
|
||||||
|
[`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait.
|
||||||
|
It includes two methods `__iter__` and `__next__`:
|
||||||
|
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<PyObject>>`
|
||||||
|
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
|
||||||
|
|
||||||
|
Returning `None` from `__next__` indicates that that there are no further items.
|
||||||
|
These two methods can be take either `PyRef<Self>` or `PyRefMut<Self>` as their
|
||||||
|
first argument, so that mutable borrow can be avoided if needed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::PyIterProtocol;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct MyIterator {
|
||||||
|
iter: Box<Iterator<Item = PyObject> + Send>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyIterProtocol for MyIterator {
|
||||||
|
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
||||||
|
slf.iter.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In many cases you'll have a distinction between the type being iterated over (i.e. the *iterable*) and the iterator it
|
||||||
|
provides. In this case, you should implement `PyIterProtocol` for both the iterable and the iterator, but the iterable
|
||||||
|
only needs to support `__iter__()` while the iterator must support both `__iter__()` and `__next__()`. The default
|
||||||
|
implementations in `PyIterProtocol` will ensure that the objects behave correctly in Python. For example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use pyo3::prelude::*;
|
||||||
|
# use pyo3::PyIterProtocol;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct Iter {
|
||||||
|
inner: std::vec::IntoIter<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyIterProtocol for Iter {
|
||||||
|
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __next__(mut slf: PyRefMut<Self>) -> Option<usize> {
|
||||||
|
slf.inner.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
struct Container {
|
||||||
|
iter: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyproto]
|
||||||
|
impl PyIterProtocol for Container {
|
||||||
|
fn __iter__(slf: PyRef<Self>) -> PyResult<Py<Iter>> {
|
||||||
|
let iter = Iter {
|
||||||
|
inner: slf.iter.clone().into_iter(),
|
||||||
|
};
|
||||||
|
Py::new(slf.py(), iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# let gil = Python::acquire_gil();
|
||||||
|
# let py = gil.python();
|
||||||
|
# let inst = pyo3::PyCell::new(
|
||||||
|
# py,
|
||||||
|
# Container {
|
||||||
|
# iter: vec![1, 2, 3, 4],
|
||||||
|
# },
|
||||||
|
# )
|
||||||
|
# .unwrap();
|
||||||
|
# pyo3::py_run!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
|
||||||
|
# pyo3::py_run!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details on Python's iteration protocols, check out [the "Iterator Types" section of the library
|
||||||
|
documentation](https://docs.python.org/3/library/stdtypes.html#iterator-types).
|
||||||
|
|
||||||
|
#### Returning a value from iteration
|
||||||
|
|
||||||
|
This guide has so far shown how to use `Option<T>` to implement yielding values during iteration.
|
||||||
|
In Python a generator can also return a value. To express this in Rust, PyO3 provides the
|
||||||
|
[`IterNextOutput`](https://docs.rs/pyo3/latest/pyo3/class/iter/enum.IterNextOutput.html) enum to
|
||||||
|
both `Yield` values and `Return` a final value - see its docs for further details and an example.
|
||||||
|
|
||||||
|
[`PyGCProtocol`]: https://docs.rs/pyo3/latest/pyo3/class/gc/trait.PyGCProtocol.html
|
||||||
|
[`PyGCProtocol`]: https://docs.rs/pyo3/latest/pyo3/class/number/trait.PyNumberProtocol.html
|
||||||
|
[`PyObjectProtocol`]: https://docs.rs/pyo3/latest/pyo3/class/basic/trait.PyObjectProtocol.html
|
|
@ -1,424 +1,3 @@
|
||||||
# Type Conversions
|
# Type Conversions
|
||||||
|
|
||||||
In this portion of the guide we'll talk about the mapping of Python types to Rust types offered by PyO3, as well as the traits available to perform conversions between them.
|
In this portion of the guide we'll talk about the mapping of Python types to Rust types offered by PyO3, as well as the traits available to perform conversions between them.
|
||||||
|
|
||||||
## Mapping of Rust types to Python types
|
|
||||||
|
|
||||||
When writing functions callable from Python (such as a `#[pyfunction]` or in a `#[pymethods]` block), the trait `FromPyObject` is required for function arguments, and `IntoPy<PyObject>` is required for function return values.
|
|
||||||
|
|
||||||
Consult the tables in the following section to find the Rust types provided by PyO3 which implement these traits.
|
|
||||||
|
|
||||||
### Argument Types
|
|
||||||
|
|
||||||
When accepting a function argument, it is possible to either use Rust library types or PyO3's Python-native types. (See the next section for discussion on when to use each.)
|
|
||||||
|
|
||||||
The table below contains the Python type and the corresponding function argument types that will accept them:
|
|
||||||
|
|
||||||
| Python | Rust | Rust (Python-native) |
|
|
||||||
| ------------- |:-------------------------------:|:--------------------:|
|
|
||||||
| `object` | - | `&PyAny` |
|
|
||||||
| `str` | `String`, `Cow<str>`, `&str` | `&PyUnicode` |
|
|
||||||
| `bytes` | `Vec<u8>`, `&[u8]` | `&PyBytes` |
|
|
||||||
| `bool` | `bool` | `&PyBool` |
|
|
||||||
| `int` | Any integer type (`i32`, `u32`, `usize`, etc) | `&PyLong` |
|
|
||||||
| `float` | `f32`, `f64` | `&PyFloat` |
|
|
||||||
| `complex` | `num_complex::Complex`[^1] | `&PyComplex` |
|
|
||||||
| `list[T]` | `Vec<T>` | `&PyList` |
|
|
||||||
| `dict[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^2] | `&PyDict` |
|
|
||||||
| `tuple[T, U]` | `(T, U)`, `Vec<T>` | `&PyTuple` |
|
|
||||||
| `set[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^2] | `&PySet` |
|
|
||||||
| `frozenset[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^2] | `&PyFrozenSet` |
|
|
||||||
| `bytearray` | `Vec<u8>` | `&PyByteArray` |
|
|
||||||
| `slice` | - | `&PySlice` |
|
|
||||||
| `type` | - | `&PyType` |
|
|
||||||
| `module` | - | `&PyModule` |
|
|
||||||
| `datetime.datetime` | - | `&PyDateTime` |
|
|
||||||
| `datetime.date` | - | `&PyDate` |
|
|
||||||
| `datetime.time` | - | `&PyTime` |
|
|
||||||
| `datetime.tzinfo` | - | `&PyTzInfo` |
|
|
||||||
| `datetime.timedelta` | - | `&PyDelta` |
|
|
||||||
| `typing.Optional[T]` | `Option<T>` | - |
|
|
||||||
| `typing.Sequence[T]` | `Vec<T>` | `&PySequence` |
|
|
||||||
| `typing.Iterator[Any]` | - | `&PyIterator` |
|
|
||||||
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](#deriving-a-hrefhttpsdocsrspyo3latestpyo3conversiontraitfrompyobjecthtmlfrompyobjecta-for-enums) | - |
|
|
||||||
|
|
||||||
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:
|
|
||||||
|
|
||||||
| What | Description |
|
|
||||||
| ------------- | ------------------------------------- |
|
|
||||||
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
|
|
||||||
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. |
|
|
||||||
| `PyObject` | An alias for `Py<PyAny>` |
|
|
||||||
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
|
|
||||||
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
|
|
||||||
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
|
|
||||||
|
|
||||||
For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](class.md).
|
|
||||||
|
|
||||||
#### Using Rust library types vs Python-native types
|
|
||||||
|
|
||||||
Using Rust library types as function arguments will incur a conversion cost compared to using the Python-native types. Using the Python-native types is almost zero-cost (they just require a type check similar to the Python builtin function `isinstance()`).
|
|
||||||
|
|
||||||
However, once that conversion cost has been paid, the Rust standard library types offer a number of benefits:
|
|
||||||
- You can write functionality in native-speed Rust code (free of Python's runtime costs).
|
|
||||||
- You get better interoperability with the rest of the Rust ecosystem.
|
|
||||||
- You can use `Python::allow_threads` to release the Python GIL and let other Python threads make progress while your Rust code is executing.
|
|
||||||
- You also benefit from stricter type checking. For example you can specify `Vec<i32>`, which will only accept a Python `list` containing integers. The Python-native equivalent, `&PyList`, would accept a Python `list` containing Python objects of any type.
|
|
||||||
|
|
||||||
For most PyO3 usage the conversion cost is worth paying to get these benefits. As always, if you're not sure it's worth it in your case, benchmark it!
|
|
||||||
|
|
||||||
### Returning Rust values to Python
|
|
||||||
|
|
||||||
When returning values from functions callable from Python, Python-native types (`&PyAny`, `&PyDict` etc.) can be used with zero cost.
|
|
||||||
|
|
||||||
Because these types are references, in some situations the Rust compiler may ask for lifetime annotations. If this is the case, you should use `Py<PyAny>`, `Py<PyDict>` etc. instead - which are also zero-cost. For all of these Python-native types `T`, `Py<T>` can be created from `T` with an `.into()` conversion.
|
|
||||||
|
|
||||||
If your function is fallible, it should return `PyResult<T>` or `Result<T, E>` where `E` implements `From<E> for PyErr`. This will raise a `Python` exception if the `Err` variant is returned.
|
|
||||||
|
|
||||||
Finally, the following Rust types are also able to convert to Python as return values:
|
|
||||||
|
|
||||||
| Rust type | Resulting Python Type |
|
|
||||||
| ------------- |:-------------------------------:|
|
|
||||||
| `String` | `str` |
|
|
||||||
| `&str` | `str` |
|
|
||||||
| `bool` | `bool` |
|
|
||||||
| Any integer type (`i32`, `u32`, `usize`, etc) | `int` |
|
|
||||||
| `f32`, `f64` | `float` |
|
|
||||||
| `Option<T>` | `Optional[T]` |
|
|
||||||
| `(T, U)` | `Tuple[T, U]` |
|
|
||||||
| `Vec<T>` | `List[T]` |
|
|
||||||
| `HashMap<K, V>` | `Dict[K, V]` |
|
|
||||||
| `BTreeMap<K, V>` | `Dict[K, V]` |
|
|
||||||
| `HashSet<T>` | `Set[T]` |
|
|
||||||
| `BTreeSet<T>` | `Set[T]` |
|
|
||||||
| `&PyCell<T: PyClass>` | `T` |
|
|
||||||
| `PyRef<T: PyClass>` | `T` |
|
|
||||||
| `PyRefMut<T: PyClass>` | `T` |
|
|
||||||
|
|
||||||
## Traits
|
|
||||||
|
|
||||||
PyO3 provides some handy traits to convert between Python types and Rust types.
|
|
||||||
|
|
||||||
### `.extract()` and the `FromPyObject` trait
|
|
||||||
|
|
||||||
The easiest way to convert a Python object to a Rust value is using
|
|
||||||
`.extract()`. It returns a `PyResult` with a type error if the conversion
|
|
||||||
fails, so usually you will use something like
|
|
||||||
|
|
||||||
```ignore
|
|
||||||
let v: Vec<i32> = obj.extract()?;
|
|
||||||
```
|
|
||||||
|
|
||||||
This method is available for many Python object types, and can produce a wide
|
|
||||||
variety of Rust types, which you can check out in the implementor list of
|
|
||||||
[`FromPyObject`].
|
|
||||||
|
|
||||||
[`FromPyObject`] is also implemented for your own Rust types wrapped as Python
|
|
||||||
objects (see [the chapter about classes](class.md)). There, in order to both be
|
|
||||||
able to operate on mutable references *and* satisfy Rust's rules of non-aliasing
|
|
||||||
mutable references, you have to extract the PyO3 reference wrappers [`PyRef`]
|
|
||||||
and [`PyRefMut`]. They work like the reference wrappers of
|
|
||||||
`std::cell::RefCell` and ensure (at runtime) that Rust borrows are allowed.
|
|
||||||
|
|
||||||
#### Deriving [`FromPyObject`]
|
|
||||||
|
|
||||||
[`FromPyObject`] can be automatically derived for many kinds of structs and enums
|
|
||||||
if the member types themselves implement `FromPyObject`. This even includes members
|
|
||||||
with a generic type `T: FromPyObject`. Derivation for empty enums, enum variants and
|
|
||||||
structs is not supported.
|
|
||||||
|
|
||||||
#### Deriving [`FromPyObject`] for structs
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyStruct {
|
|
||||||
my_string: String,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The derivation generates code that will per default access the attribute `my_string` on
|
|
||||||
the Python object, i.e. `obj.getattr("my_string")`, and call `extract()` on the attribute.
|
|
||||||
It is also possible to access the value on the Python object through `obj.get_item("my_string")`
|
|
||||||
by setting the attribute `pyo3(item)` on the field:
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyStruct {
|
|
||||||
#[pyo3(item)]
|
|
||||||
my_string: String,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The argument passed to `getattr` and `get_item` can also be configured:
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyStruct {
|
|
||||||
#[pyo3(item("key"))]
|
|
||||||
string_in_mapping: String,
|
|
||||||
#[pyo3(attribute("name"))]
|
|
||||||
string_attr: String,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This tries to extract `string_attr` from the attribute `name` and `string_in_mapping`
|
|
||||||
from a mapping with the key `"key"`. The arguments for `attribute` are restricted to
|
|
||||||
non-empty string literals while `item` can take any valid literal that implements
|
|
||||||
`ToBorrowedObject`.
|
|
||||||
|
|
||||||
#### Deriving [`FromPyObject`] for tuple structs
|
|
||||||
|
|
||||||
Tuple structs are also supported but do not allow customizing the extraction. The input is
|
|
||||||
always assumed to be a Python tuple with the same length as the Rust type, the `n`th field
|
|
||||||
is extracted from the `n`th item in the Python tuple.
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyTuple(String, String);
|
|
||||||
```
|
|
||||||
|
|
||||||
Tuple structs with a single field are treated as wrapper types which are described in the
|
|
||||||
following section. To override this behaviour and ensure that the input is in fact a tuple,
|
|
||||||
specify the struct as
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyTuple((String,));
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Deriving [`FromPyObject`] for wrapper types
|
|
||||||
|
|
||||||
The `pyo3(transparent)` attribute can be used on structs with exactly one field. This results
|
|
||||||
in extracting directly from the input object, i.e. `obj.extract()`, rather than trying to access
|
|
||||||
an item or attribute. This behaviour is enabled per default for newtype structs and tuple-variants
|
|
||||||
with a single field.
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
struct RustyTransparentTupleStruct(String);
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
#[pyo3(transparent)]
|
|
||||||
struct RustyTransparentStruct {
|
|
||||||
inner: String,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Deriving [`FromPyObject`] for enums
|
|
||||||
|
|
||||||
The `FromPyObject` derivation for enums generates code that tries to extract the variants in the
|
|
||||||
order of the fields. As soon as a variant can be extracted succesfully, that variant is returned.
|
|
||||||
This makes it possible to extract Python types like `Union[str, int]`.
|
|
||||||
|
|
||||||
The same customizations and restrictions described for struct derivations apply to enum variants,
|
|
||||||
i.e. a tuple variant assumes that the input is a Python tuple, and a struct variant defaults to
|
|
||||||
extracting fields as attributes but can be configured in the same manner. The `transparent`
|
|
||||||
attribute can be applied to single-field-variants.
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
enum RustyEnum<'a> {
|
|
||||||
Int(usize), // input is a positive int
|
|
||||||
String(String), // input is a string
|
|
||||||
IntTuple(usize, usize), // input is a 2-tuple with positive ints
|
|
||||||
StringIntTuple(String, usize), // input is a 2-tuple with String and int
|
|
||||||
Coordinates3d { // needs to be in front of 2d
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
z: usize,
|
|
||||||
},
|
|
||||||
Coordinates2d { // only gets checked if the input did not have `z`
|
|
||||||
#[pyo3(attribute("x"))]
|
|
||||||
a: usize,
|
|
||||||
#[pyo3(attribute("y"))]
|
|
||||||
b: usize,
|
|
||||||
},
|
|
||||||
#[pyo3(transparent)]
|
|
||||||
CatchAll(&'a PyAny), // This extraction never fails
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If none of the enum variants match, a `PyValueError` containing the names of the
|
|
||||||
tested variants is returned. The names reported in the error message can be customized
|
|
||||||
through the `pyo3(annotation = "name")` attribute, e.g. to use conventional Python type
|
|
||||||
names:
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
|
||||||
enum RustyEnum {
|
|
||||||
#[pyo3(transparent, annotation = "str")]
|
|
||||||
String(String),
|
|
||||||
#[pyo3(transparent, annotation = "int")]
|
|
||||||
Int(isize),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If the input is neither a string nor an integer, the error message will be:
|
|
||||||
`"Can't convert <INPUT> to Union[str, int]"`, where `<INPUT>` is replaced by the type name and
|
|
||||||
`repr()` of the input object.
|
|
||||||
|
|
||||||
#### `#[derive(FromPyObject)]` Container Attributes
|
|
||||||
- `pyo3(transparent)`
|
|
||||||
- extract the field directly from the object as `obj.extract()` instead of `get_item()` or
|
|
||||||
`getattr()`
|
|
||||||
- Newtype structs and tuple-variants are treated as transparent per default.
|
|
||||||
- only supported for single-field structs and enum variants
|
|
||||||
- `pyo3(annotation = "name")`
|
|
||||||
- changes the name of the failed variant in the generated error message in case of failure.
|
|
||||||
- e.g. `pyo3("int")` reports the variant's type as `int`.
|
|
||||||
- only supported for enum variants
|
|
||||||
|
|
||||||
#### `#[derive(FromPyObject)]` Field Attributes
|
|
||||||
- `pyo3(attribute)`, `pyo3(attribute("name"))`
|
|
||||||
- retrieve the field from an attribute, possibly with a custom name specified as an argument
|
|
||||||
- argument must be a string-literal.
|
|
||||||
- `pyo3(item)`, `pyo3(item("key"))`
|
|
||||||
- retrieve the field from a mapping, possibly with the custom key specified as an argument.
|
|
||||||
- can be any literal that implements `ToBorrowedObject`
|
|
||||||
|
|
||||||
### `IntoPy<T>`
|
|
||||||
|
|
||||||
This trait defines the to-python conversion for a Rust type. It is usually implemented as
|
|
||||||
`IntoPy<PyObject>`, which is the trait needed for returning a value from `#[pyfunction]` and
|
|
||||||
`#[pymethods]`.
|
|
||||||
|
|
||||||
All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`.
|
|
||||||
|
|
||||||
Occasionally you may choose to implement this for custom types which are mapped to Python types
|
|
||||||
_without_ having a unique python type.
|
|
||||||
|
|
||||||
```
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
struct MyPyObjectWrapper(PyObject);
|
|
||||||
|
|
||||||
impl IntoPy<PyObject> for MyPyObjectWrapper {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### The `ToPyObject` trait
|
|
||||||
|
|
||||||
[`ToPyObject`] is a conversion trait that allows various objects to be
|
|
||||||
converted into [`PyObject`]. `IntoPy<PyObject>` serves the
|
|
||||||
same purpose, except that it consumes `self`.
|
|
||||||
|
|
||||||
|
|
||||||
### `*args` and `**kwargs` for Python object calls
|
|
||||||
|
|
||||||
There are several ways how to pass positional and keyword arguments to a Python object call.
|
|
||||||
[`PyAny`] provides two methods:
|
|
||||||
|
|
||||||
* `call` - call any callable Python object.
|
|
||||||
* `call_method` - call a specific method on the object, shorthand for `get_attr` then `call`.
|
|
||||||
|
|
||||||
Both methods need `args` and `kwargs` arguments, but there are variants for less
|
|
||||||
complex calls, such as `call1` for only `args` and `call0` for no arguments at all.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::types::{PyDict, PyTuple};
|
|
||||||
|
|
||||||
struct SomeObject;
|
|
||||||
impl SomeObject {
|
|
||||||
fn new(py: Python) -> PyObject {
|
|
||||||
PyDict::new(py).to_object(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let arg1 = "arg1";
|
|
||||||
let arg2 = "arg2";
|
|
||||||
let arg3 = "arg3";
|
|
||||||
|
|
||||||
let gil = Python::acquire_gil();
|
|
||||||
let py = gil.python();
|
|
||||||
|
|
||||||
let obj = SomeObject::new(py);
|
|
||||||
|
|
||||||
// call object without empty arguments
|
|
||||||
obj.call0(py);
|
|
||||||
|
|
||||||
// call object with PyTuple
|
|
||||||
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
|
|
||||||
obj.call1(py, args);
|
|
||||||
|
|
||||||
// pass arguments as rust tuple
|
|
||||||
let args = (arg1, arg2, arg3);
|
|
||||||
obj.call1(py, args);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`kwargs` can be `None` or `Some(&PyDict)`. You can use the
|
|
||||||
[`IntoPyDict`] trait to convert other dict-like containers,
|
|
||||||
e.g. `HashMap` or `BTreeMap`, as well as tuples with up to 10 elements and
|
|
||||||
`Vec`s where each element is a two-element tuple.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::types::{IntoPyDict, PyDict};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
struct SomeObject;
|
|
||||||
|
|
||||||
impl SomeObject {
|
|
||||||
fn new(py: Python) -> PyObject {
|
|
||||||
PyDict::new(py).to_object(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let key1 = "key1";
|
|
||||||
let val1 = 1;
|
|
||||||
let key2 = "key2";
|
|
||||||
let val2 = 2;
|
|
||||||
|
|
||||||
let gil = Python::acquire_gil();
|
|
||||||
let py = gil.python();
|
|
||||||
|
|
||||||
let obj = SomeObject::new(py);
|
|
||||||
|
|
||||||
// call object with PyDict
|
|
||||||
let kwargs = [(key1, val1)].into_py_dict(py);
|
|
||||||
obj.call(py, (), Some(kwargs));
|
|
||||||
|
|
||||||
// pass arguments as Vec
|
|
||||||
let kwargs = vec![(key1, val1), (key2, val2)];
|
|
||||||
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
|
||||||
|
|
||||||
// pass arguments as HashMap
|
|
||||||
let mut kwargs = HashMap::<&str, i32>::new();
|
|
||||||
kwargs.insert(key1, 1);
|
|
||||||
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[`IntoPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPy.html
|
|
||||||
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPyObject.html
|
|
||||||
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html
|
|
||||||
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
|
|
||||||
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
|
|
||||||
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
|
|
||||||
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
|
|
||||||
|
|
||||||
[`PyRef`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRef.html
|
|
||||||
[`PyRefMut`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRefMut.html
|
|
||||||
|
|
||||||
[^1]: Requires the `num-complex` optional feature.
|
|
||||||
[^2]: Requires the `hashbrown` optional feature.
|
|
||||||
|
|
96
guide/src/conversions/tables.md
Normal file
96
guide/src/conversions/tables.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
## Mapping of Rust types to Python types
|
||||||
|
|
||||||
|
When writing functions callable from Python (such as a `#[pyfunction]` or in a `#[pymethods]` block), the trait `FromPyObject` is required for function arguments, and `IntoPy<PyObject>` is required for function return values.
|
||||||
|
|
||||||
|
Consult the tables in the following section to find the Rust types provided by PyO3 which implement these traits.
|
||||||
|
|
||||||
|
### Argument Types
|
||||||
|
|
||||||
|
When accepting a function argument, it is possible to either use Rust library types or PyO3's Python-native types. (See the next section for discussion on when to use each.)
|
||||||
|
|
||||||
|
The table below contains the Python type and the corresponding function argument types that will accept them:
|
||||||
|
|
||||||
|
| Python | Rust | Rust (Python-native) |
|
||||||
|
| ------------- |:-------------------------------:|:--------------------:|
|
||||||
|
| `object` | - | `&PyAny` |
|
||||||
|
| `str` | `String`, `Cow<str>`, `&str` | `&PyUnicode` |
|
||||||
|
| `bytes` | `Vec<u8>`, `&[u8]` | `&PyBytes` |
|
||||||
|
| `bool` | `bool` | `&PyBool` |
|
||||||
|
| `int` | Any integer type (`i32`, `u32`, `usize`, etc) | `&PyLong` |
|
||||||
|
| `float` | `f32`, `f64` | `&PyFloat` |
|
||||||
|
| `complex` | `num_complex::Complex`[^1] | `&PyComplex` |
|
||||||
|
| `list[T]` | `Vec<T>` | `&PyList` |
|
||||||
|
| `dict[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^2] | `&PyDict` |
|
||||||
|
| `tuple[T, U]` | `(T, U)`, `Vec<T>` | `&PyTuple` |
|
||||||
|
| `set[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^2] | `&PySet` |
|
||||||
|
| `frozenset[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^2] | `&PyFrozenSet` |
|
||||||
|
| `bytearray` | `Vec<u8>` | `&PyByteArray` |
|
||||||
|
| `slice` | - | `&PySlice` |
|
||||||
|
| `type` | - | `&PyType` |
|
||||||
|
| `module` | - | `&PyModule` |
|
||||||
|
| `datetime.datetime` | - | `&PyDateTime` |
|
||||||
|
| `datetime.date` | - | `&PyDate` |
|
||||||
|
| `datetime.time` | - | `&PyTime` |
|
||||||
|
| `datetime.tzinfo` | - | `&PyTzInfo` |
|
||||||
|
| `datetime.timedelta` | - | `&PyDelta` |
|
||||||
|
| `typing.Optional[T]` | `Option<T>` | - |
|
||||||
|
| `typing.Sequence[T]` | `Vec<T>` | `&PySequence` |
|
||||||
|
| `typing.Iterator[Any]` | - | `&PyIterator` |
|
||||||
|
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](traits.html#deriving-a-hrefhttpsdocsrspyo3latestpyo3conversiontraitfrompyobjecthtmlfrompyobjecta-for-enums) | - |
|
||||||
|
|
||||||
|
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:
|
||||||
|
|
||||||
|
| What | Description |
|
||||||
|
| ------------- | ------------------------------------- |
|
||||||
|
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
|
||||||
|
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. |
|
||||||
|
| `PyObject` | An alias for `Py<PyAny>` |
|
||||||
|
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
|
||||||
|
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
|
||||||
|
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
|
||||||
|
|
||||||
|
For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](class.md).
|
||||||
|
|
||||||
|
#### Using Rust library types vs Python-native types
|
||||||
|
|
||||||
|
Using Rust library types as function arguments will incur a conversion cost compared to using the Python-native types. Using the Python-native types is almost zero-cost (they just require a type check similar to the Python builtin function `isinstance()`).
|
||||||
|
|
||||||
|
However, once that conversion cost has been paid, the Rust standard library types offer a number of benefits:
|
||||||
|
- You can write functionality in native-speed Rust code (free of Python's runtime costs).
|
||||||
|
- You get better interoperability with the rest of the Rust ecosystem.
|
||||||
|
- You can use `Python::allow_threads` to release the Python GIL and let other Python threads make progress while your Rust code is executing.
|
||||||
|
- You also benefit from stricter type checking. For example you can specify `Vec<i32>`, which will only accept a Python `list` containing integers. The Python-native equivalent, `&PyList`, would accept a Python `list` containing Python objects of any type.
|
||||||
|
|
||||||
|
For most PyO3 usage the conversion cost is worth paying to get these benefits. As always, if you're not sure it's worth it in your case, benchmark it!
|
||||||
|
|
||||||
|
### Returning Rust values to Python
|
||||||
|
|
||||||
|
When returning values from functions callable from Python, Python-native types (`&PyAny`, `&PyDict` etc.) can be used with zero cost.
|
||||||
|
|
||||||
|
Because these types are references, in some situations the Rust compiler may ask for lifetime annotations. If this is the case, you should use `Py<PyAny>`, `Py<PyDict>` etc. instead - which are also zero-cost. For all of these Python-native types `T`, `Py<T>` can be created from `T` with an `.into()` conversion.
|
||||||
|
|
||||||
|
If your function is fallible, it should return `PyResult<T>` or `Result<T, E>` where `E` implements `From<E> for PyErr`. This will raise a `Python` exception if the `Err` variant is returned.
|
||||||
|
|
||||||
|
Finally, the following Rust types are also able to convert to Python as return values:
|
||||||
|
|
||||||
|
| Rust type | Resulting Python Type |
|
||||||
|
| ------------- |:-------------------------------:|
|
||||||
|
| `String` | `str` |
|
||||||
|
| `&str` | `str` |
|
||||||
|
| `bool` | `bool` |
|
||||||
|
| Any integer type (`i32`, `u32`, `usize`, etc) | `int` |
|
||||||
|
| `f32`, `f64` | `float` |
|
||||||
|
| `Option<T>` | `Optional[T]` |
|
||||||
|
| `(T, U)` | `Tuple[T, U]` |
|
||||||
|
| `Vec<T>` | `List[T]` |
|
||||||
|
| `HashMap<K, V>` | `Dict[K, V]` |
|
||||||
|
| `BTreeMap<K, V>` | `Dict[K, V]` |
|
||||||
|
| `HashSet<T>` | `Set[T]` |
|
||||||
|
| `BTreeSet<T>` | `Set[T]` |
|
||||||
|
| `&PyCell<T: PyClass>` | `T` |
|
||||||
|
| `PyRef<T: PyClass>` | `T` |
|
||||||
|
| `PyRefMut<T: PyClass>` | `T` |
|
||||||
|
|
||||||
|
[^1]: Requires the `num-complex` optional feature.
|
||||||
|
|
||||||
|
[^2]: Requires the `hashbrown` optional feature.
|
324
guide/src/conversions/traits.md
Normal file
324
guide/src/conversions/traits.md
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
## Conversion traits
|
||||||
|
|
||||||
|
PyO3 provides some handy traits to convert between Python types and Rust types.
|
||||||
|
|
||||||
|
### `.extract()` and the `FromPyObject` trait
|
||||||
|
|
||||||
|
The easiest way to convert a Python object to a Rust value is using
|
||||||
|
`.extract()`. It returns a `PyResult` with a type error if the conversion
|
||||||
|
fails, so usually you will use something like
|
||||||
|
|
||||||
|
```ignore
|
||||||
|
let v: Vec<i32> = obj.extract()?;
|
||||||
|
```
|
||||||
|
|
||||||
|
This method is available for many Python object types, and can produce a wide
|
||||||
|
variety of Rust types, which you can check out in the implementor list of
|
||||||
|
[`FromPyObject`].
|
||||||
|
|
||||||
|
[`FromPyObject`] is also implemented for your own Rust types wrapped as Python
|
||||||
|
objects (see [the chapter about classes](class.md)). There, in order to both be
|
||||||
|
able to operate on mutable references *and* satisfy Rust's rules of non-aliasing
|
||||||
|
mutable references, you have to extract the PyO3 reference wrappers [`PyRef`]
|
||||||
|
and [`PyRefMut`]. They work like the reference wrappers of
|
||||||
|
`std::cell::RefCell` and ensure (at runtime) that Rust borrows are allowed.
|
||||||
|
|
||||||
|
#### Deriving [`FromPyObject`]
|
||||||
|
|
||||||
|
[`FromPyObject`] can be automatically derived for many kinds of structs and enums
|
||||||
|
if the member types themselves implement `FromPyObject`. This even includes members
|
||||||
|
with a generic type `T: FromPyObject`. Derivation for empty enums, enum variants and
|
||||||
|
structs is not supported.
|
||||||
|
|
||||||
|
#### Deriving [`FromPyObject`] for structs
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyStruct {
|
||||||
|
my_string: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The derivation generates code that will per default access the attribute `my_string` on
|
||||||
|
the Python object, i.e. `obj.getattr("my_string")`, and call `extract()` on the attribute.
|
||||||
|
It is also possible to access the value on the Python object through `obj.get_item("my_string")`
|
||||||
|
by setting the attribute `pyo3(item)` on the field:
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyStruct {
|
||||||
|
#[pyo3(item)]
|
||||||
|
my_string: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The argument passed to `getattr` and `get_item` can also be configured:
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyStruct {
|
||||||
|
#[pyo3(item("key"))]
|
||||||
|
string_in_mapping: String,
|
||||||
|
#[pyo3(attribute("name"))]
|
||||||
|
string_attr: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This tries to extract `string_attr` from the attribute `name` and `string_in_mapping`
|
||||||
|
from a mapping with the key `"key"`. The arguments for `attribute` are restricted to
|
||||||
|
non-empty string literals while `item` can take any valid literal that implements
|
||||||
|
`ToBorrowedObject`.
|
||||||
|
|
||||||
|
#### Deriving [`FromPyObject`] for tuple structs
|
||||||
|
|
||||||
|
Tuple structs are also supported but do not allow customizing the extraction. The input is
|
||||||
|
always assumed to be a Python tuple with the same length as the Rust type, the `n`th field
|
||||||
|
is extracted from the `n`th item in the Python tuple.
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyTuple(String, String);
|
||||||
|
```
|
||||||
|
|
||||||
|
Tuple structs with a single field are treated as wrapper types which are described in the
|
||||||
|
following section. To override this behaviour and ensure that the input is in fact a tuple,
|
||||||
|
specify the struct as
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyTuple((String,));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deriving [`FromPyObject`] for wrapper types
|
||||||
|
|
||||||
|
The `pyo3(transparent)` attribute can be used on structs with exactly one field. This results
|
||||||
|
in extracting directly from the input object, i.e. `obj.extract()`, rather than trying to access
|
||||||
|
an item or attribute. This behaviour is enabled per default for newtype structs and tuple-variants
|
||||||
|
with a single field.
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct RustyTransparentTupleStruct(String);
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
#[pyo3(transparent)]
|
||||||
|
struct RustyTransparentStruct {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deriving [`FromPyObject`] for enums
|
||||||
|
|
||||||
|
The `FromPyObject` derivation for enums generates code that tries to extract the variants in the
|
||||||
|
order of the fields. As soon as a variant can be extracted succesfully, that variant is returned.
|
||||||
|
This makes it possible to extract Python types like `Union[str, int]`.
|
||||||
|
|
||||||
|
The same customizations and restrictions described for struct derivations apply to enum variants,
|
||||||
|
i.e. a tuple variant assumes that the input is a Python tuple, and a struct variant defaults to
|
||||||
|
extracting fields as attributes but can be configured in the same manner. The `transparent`
|
||||||
|
attribute can be applied to single-field-variants.
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
enum RustyEnum<'a> {
|
||||||
|
Int(usize), // input is a positive int
|
||||||
|
String(String), // input is a string
|
||||||
|
IntTuple(usize, usize), // input is a 2-tuple with positive ints
|
||||||
|
StringIntTuple(String, usize), // input is a 2-tuple with String and int
|
||||||
|
Coordinates3d { // needs to be in front of 2d
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
z: usize,
|
||||||
|
},
|
||||||
|
Coordinates2d { // only gets checked if the input did not have `z`
|
||||||
|
#[pyo3(attribute("x"))]
|
||||||
|
a: usize,
|
||||||
|
#[pyo3(attribute("y"))]
|
||||||
|
b: usize,
|
||||||
|
},
|
||||||
|
#[pyo3(transparent)]
|
||||||
|
CatchAll(&'a PyAny), // This extraction never fails
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If none of the enum variants match, a `PyValueError` containing the names of the
|
||||||
|
tested variants is returned. The names reported in the error message can be customized
|
||||||
|
through the `pyo3(annotation = "name")` attribute, e.g. to use conventional Python type
|
||||||
|
names:
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
enum RustyEnum {
|
||||||
|
#[pyo3(transparent, annotation = "str")]
|
||||||
|
String(String),
|
||||||
|
#[pyo3(transparent, annotation = "int")]
|
||||||
|
Int(isize),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the input is neither a string nor an integer, the error message will be:
|
||||||
|
`"Can't convert <INPUT> to Union[str, int]"`, where `<INPUT>` is replaced by the type name and
|
||||||
|
`repr()` of the input object.
|
||||||
|
|
||||||
|
#### `#[derive(FromPyObject)]` Container Attributes
|
||||||
|
- `pyo3(transparent)`
|
||||||
|
- extract the field directly from the object as `obj.extract()` instead of `get_item()` or
|
||||||
|
`getattr()`
|
||||||
|
- Newtype structs and tuple-variants are treated as transparent per default.
|
||||||
|
- only supported for single-field structs and enum variants
|
||||||
|
- `pyo3(annotation = "name")`
|
||||||
|
- changes the name of the failed variant in the generated error message in case of failure.
|
||||||
|
- e.g. `pyo3("int")` reports the variant's type as `int`.
|
||||||
|
- only supported for enum variants
|
||||||
|
|
||||||
|
#### `#[derive(FromPyObject)]` Field Attributes
|
||||||
|
- `pyo3(attribute)`, `pyo3(attribute("name"))`
|
||||||
|
- retrieve the field from an attribute, possibly with a custom name specified as an argument
|
||||||
|
- argument must be a string-literal.
|
||||||
|
- `pyo3(item)`, `pyo3(item("key"))`
|
||||||
|
- retrieve the field from a mapping, possibly with the custom key specified as an argument.
|
||||||
|
- can be any literal that implements `ToBorrowedObject`
|
||||||
|
|
||||||
|
### `IntoPy<T>`
|
||||||
|
|
||||||
|
This trait defines the to-python conversion for a Rust type. It is usually implemented as
|
||||||
|
`IntoPy<PyObject>`, which is the trait needed for returning a value from `#[pyfunction]` and
|
||||||
|
`#[pymethods]`.
|
||||||
|
|
||||||
|
All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`.
|
||||||
|
|
||||||
|
Occasionally you may choose to implement this for custom types which are mapped to Python types
|
||||||
|
_without_ having a unique python type.
|
||||||
|
|
||||||
|
```
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
struct MyPyObjectWrapper(PyObject);
|
||||||
|
|
||||||
|
impl IntoPy<PyObject> for MyPyObjectWrapper {
|
||||||
|
fn into_py(self, py: Python) -> PyObject {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The `ToPyObject` trait
|
||||||
|
|
||||||
|
[`ToPyObject`] is a conversion trait that allows various objects to be
|
||||||
|
converted into [`PyObject`]. `IntoPy<PyObject>` serves the
|
||||||
|
same purpose, except that it consumes `self`.
|
||||||
|
|
||||||
|
|
||||||
|
### `*args` and `**kwargs` for Python object calls
|
||||||
|
|
||||||
|
There are several ways how to pass positional and keyword arguments to a Python object call.
|
||||||
|
[`PyAny`] provides two methods:
|
||||||
|
|
||||||
|
* `call` - call any callable Python object.
|
||||||
|
* `call_method` - call a specific method on the object, shorthand for `get_attr` then `call`.
|
||||||
|
|
||||||
|
Both methods need `args` and `kwargs` arguments, but there are variants for less
|
||||||
|
complex calls, such as `call1` for only `args` and `call0` for no arguments at all.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::types::{PyDict, PyTuple};
|
||||||
|
|
||||||
|
struct SomeObject;
|
||||||
|
impl SomeObject {
|
||||||
|
fn new(py: Python) -> PyObject {
|
||||||
|
PyDict::new(py).to_object(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arg1 = "arg1";
|
||||||
|
let arg2 = "arg2";
|
||||||
|
let arg3 = "arg3";
|
||||||
|
|
||||||
|
let gil = Python::acquire_gil();
|
||||||
|
let py = gil.python();
|
||||||
|
|
||||||
|
let obj = SomeObject::new(py);
|
||||||
|
|
||||||
|
// call object without empty arguments
|
||||||
|
obj.call0(py);
|
||||||
|
|
||||||
|
// call object with PyTuple
|
||||||
|
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
|
||||||
|
obj.call1(py, args);
|
||||||
|
|
||||||
|
// pass arguments as rust tuple
|
||||||
|
let args = (arg1, arg2, arg3);
|
||||||
|
obj.call1(py, args);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`kwargs` can be `None` or `Some(&PyDict)`. You can use the
|
||||||
|
[`IntoPyDict`] trait to convert other dict-like containers,
|
||||||
|
e.g. `HashMap` or `BTreeMap`, as well as tuples with up to 10 elements and
|
||||||
|
`Vec`s where each element is a two-element tuple.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::types::{IntoPyDict, PyDict};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
struct SomeObject;
|
||||||
|
|
||||||
|
impl SomeObject {
|
||||||
|
fn new(py: Python) -> PyObject {
|
||||||
|
PyDict::new(py).to_object(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let key1 = "key1";
|
||||||
|
let val1 = 1;
|
||||||
|
let key2 = "key2";
|
||||||
|
let val2 = 2;
|
||||||
|
|
||||||
|
let gil = Python::acquire_gil();
|
||||||
|
let py = gil.python();
|
||||||
|
|
||||||
|
let obj = SomeObject::new(py);
|
||||||
|
|
||||||
|
// call object with PyDict
|
||||||
|
let kwargs = [(key1, val1)].into_py_dict(py);
|
||||||
|
obj.call(py, (), Some(kwargs));
|
||||||
|
|
||||||
|
// pass arguments as Vec
|
||||||
|
let kwargs = vec![(key1, val1), (key2, val2)];
|
||||||
|
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
||||||
|
|
||||||
|
// pass arguments as HashMap
|
||||||
|
let mut kwargs = HashMap::<&str, i32>::new();
|
||||||
|
kwargs.insert(key1, 1);
|
||||||
|
obj.call(py, (), Some(kwargs.into_py_dict(py)));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[`IntoPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPy.html
|
||||||
|
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPyObject.html
|
||||||
|
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html
|
||||||
|
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
|
||||||
|
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
|
||||||
|
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
|
||||||
|
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
|
||||||
|
|
||||||
|
[`PyRef`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRef.html
|
||||||
|
[`PyRefMut`]: https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyRefMut.html
|
|
@ -1,4 +1,4 @@
|
||||||
# The PyO3 Guide
|
# The PyO3 user guide
|
||||||
|
|
||||||
Welcome to the PyO3 user guide! This book is a companion to [PyO3's API docs](https://docs.rs/pyo3). It contains examples and documentation to explain all of PyO3's use cases in detail.
|
Welcome to the PyO3 user guide! This book is a companion to [PyO3's API docs](https://docs.rs/pyo3). It contains examples and documentation to explain all of PyO3's use cases in detail.
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ impl MyClass {
|
||||||
```
|
```
|
||||||
|
|
||||||
Basically you can return `Self` or `Result<Self>` directly.
|
Basically you can return `Self` or `Result<Self>` directly.
|
||||||
For more, see [the constructor section](https://pyo3.rs/master/class.html#constructor) of this guide.
|
For more, see [the constructor section](class.html#constructor) of this guide.
|
||||||
|
|
||||||
### PyCell
|
### PyCell
|
||||||
PyO3 0.9 introduces [`PyCell`], which is a [`RefCell`]-like object wrapper
|
PyO3 0.9 introduces [`PyCell`], which is a [`RefCell`]-like object wrapper
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Using in Python a Rust function with trait bounds
|
# Using in Python a Rust function with trait bounds
|
||||||
|
|
||||||
PyO3 allows for easy conversion from Rust to Python for certain functions and classes (see the [conversion table](https://pyo3.rs/master/conversions.html)).
|
PyO3 allows for easy conversion from Rust to Python for certain functions and classes (see the [conversion table](conversions/tables.html).
|
||||||
However, it is not always straightforward to convert Rust code that requires a given trait implementation as an argument.
|
However, it is not always straightforward to convert Rust code that requires a given trait implementation as an argument.
|
||||||
|
|
||||||
This tutorial explains how to convert a Rust function that takes a trait as argument for use in Python with classes implementing the same methods as the trait.
|
This tutorial explains how to convert a Rust function that takes a trait as argument for use in Python with classes implementing the same methods as the trait.
|
||||||
|
|
Loading…
Reference in a new issue