For #[pymethods] or #[pyfunction]s, your existing code should continue to work without any change.
Python exceptions will automatically be raised when your functions are used in a way which breaks Rust's
rules of references.
Here is an example.
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
#[pyclass]structNames {
names: Vec<String>
}
#[pymethods]impl Names {
#[new]fnnew() -> Self {
Names { names: vec![] }
}
fnmerge(&mutself, other: &mut Names) {
self.names.append(&mut other.names)
}
}
let gil = Python::acquire_gil();
let py = gil.python();
let names = PyCell::new(py, Names::new()).unwrap();
let borrow_mut_err = py.get_type::<pyo3::pycell::PyBorrowMutError>();
pyo3::py_run!(py, names borrow_mut_err, r"
try:
names.merge(names)
assert False, 'Unreachable'
except RuntimeError as e:
isinstance(e, borrow_mut_err)
");
}
Names has a merge method, which takes &mut self and another argument of type &mut Self.
Given this #[pyclass], calling names.merge(names) in Python raises
a PyBorrowMutError exception, since it requires two mutable borrows of names.
However, for #[pyproto] and some functions, you need to manually fix the code.
In 0.8 object creation was done with PyRef::new and PyRefMut::new.
In 0.9 these have both been removed.
To upgrade code, please use
PyCell::new instead.
If you need PyRef or PyRefMut, just call .borrow() or .borrow_mut()
on the newly-created PyCell.
Before:
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
#[pyclass]structMyClass {}
let gil = Python::acquire_gil();
let py = gil.python();
let obj_ref = PyRef::new(py, MyClass {}).unwrap();
}
After:
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
#[pyclass]structMyClass {}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyCell::new(py, MyClass {}).unwrap();
let obj_ref = obj.borrow();
}
For PyClass types T, &T and &mut T no longer have FromPyObject implementations.
Instead you should extract PyRef<T> or PyRefMut<T>, respectively.
If T implements Clone, you can extract T itself.
In addition, you can also extract &PyCell<T>, though you rarely need it.
Before:
let obj: &PyAny = create_obj();
let obj_ref: &MyClass = obj.extract().unwrap();
let obj_ref_mut: &mut MyClass = obj.extract().unwrap();
After:
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
#[pyclass]#[derive(Clone)]structMyClass {}
#[pymethods]impl MyClass { #[new]fnnew() -> Self { MyClass {} }}
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<MyClass>();
let d = [("c", typeobj)].into_py_dict(py);
let create_obj = || py.eval("c()", None, Some(d)).unwrap();
let obj: &PyAny = create_obj();
let obj_cell: &PyCell<MyClass> = obj.extract().unwrap();
let obj_cloned: MyClass = obj.extract().unwrap(); // extracted by cloning the object
{
let obj_ref: PyRef<MyClass> = obj.extract().unwrap();
// we need to drop obj_ref before we can extract a PyRefMut due to Rust's rules of references
}
let obj_ref_mut: PyRefMut<MyClass> = obj.extract().unwrap();
}
Most of the arguments to methods in #[pyproto] impls require a
FromPyObject implementation.
So if your protocol methods take &T or &mut T (where T: PyClass),
please use PyRef or PyRefMut instead.
Before:
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
use pyo3::class::PySequenceProtocol;
#[pyclass]structByteSequence {
elements: Vec<u8>,
}
#[pyproto]impl PySequenceProtocol for ByteSequence {
fn__concat__(&self, other: &Self) -> PyResult<Self> {
letmut elements = self.elements.clone();
elements.extend_from_slice(&other.elements);
Ok(Self { elements })
}
}
}
After:
#![allow(unused_variables)]fnmain() {
use pyo3::prelude::*;
use pyo3::class::PySequenceProtocol;
#[pyclass]structByteSequence {
elements: Vec<u8>,
}
#[pyproto]impl PySequenceProtocol for ByteSequence {
fn__concat__(&self, other: PyRef<'p, Self>) -> PyResult<Self> {
letmut elements = self.elements.clone();
elements.extend_from_slice(&other.elements);
Ok(Self { elements })
}
}
}