rewrite Pycell documentation

This commit is contained in:
mejrs 2021-09-22 03:13:26 +02:00
parent 4a3b049706
commit e805c998f9
1 changed files with 186 additions and 71 deletions

View File

@ -1,6 +1,144 @@
//! PyO3's interior mutability primitive.
//!
//! For more information see the [guide](https://pyo3.rs/latest/class.html?highlight=pycell#pycell-and-interior-mutability "PyCell and interior mutability")
//! Rust has strict aliasing rules - you can either have any number of immutable (shared) references or one mutable
//! reference. Python's ownership model is the complete opposite of that - any Python object
//! can be referenced any number of times, and mutation is allowed from any reference.
//!
//! PyO3 deals with these differences by employing the [Interior Mutability]
//! pattern. This requires that PyO3 enforces the borrowing rules and it has two mechanisms for
//! doing so:
//! - Statically it can enforce threadsafe access with the [`Python<'py>`](crate::Python) token.
//! All Rust code holding that token, or anything derived from it, can assume that they have
//! safe access to the Python interpreter's state. For this reason all the native Python objects
//! can be mutated through shared references.
//! - However, methods and functions in Rust usually *do* need `&mut` references. While PyO3 can
//! use the [`Python<'py>`](crate::Python) token to guarantee thread-safe access to them, it cannot
//! statically guarantee uniqueness of `&mut` references. As such those references have to be tracked
//! dynamically at runtime, using [`PyCell`] and the other types defined in this module. This works
//! similar to std's [`RefCell`](std::cell::RefCell) type.
//!
//! # When *not* to use PyCell
//!
//! Usually you can use `&mut` references as method and function receivers and arguments, and you
//! won't need to use [`PyCell`] directly:
//!
//! ```rust
//! use pyo3::prelude::*;
//!
//! #[pyclass]
//! struct Number {
//! inner: u32,
//! }
//!
//! #[pymethods]
//! impl Number {
//! fn increment(&mut self) {
//! self.inner += 1;
//! }
//! }
//! ```
//!
//! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more),
//! using [`PyCell`] under the hood:
//!
//! ```ignore
//! // This function is exported to Python.
//! unsafe extern "C" fn __wrap(slf: *mut PyObject, _args: *mut PyObject) -> *mut PyObject {
//! pyo3::callback::handle_panic(|py| {
//! let cell: &PyCell<Number> = py.from_borrowed_ptr(slf);
//! let mut _ref: PyRefMut<Number> = cell.try_borrow_mut()?;
//! let slf: &mut Number = &mut _ref;
//! pyo3::callback::convert(py, Number::increment(slf))
//! })
//! }
//! ```
//!
//! # When to use PyCell
//! ## Using pyclasses from Rust
//!
//! However, we *do* need [`PyCell`] if we want to call its methods from Rust:
//! ```rust
//! # use pyo3::prelude::*;
//! #
//! # #[pyclass]
//! # struct Number {
//! # inner: u32,
//! # }
//! #
//! # #[pymethods]
//! # impl Number {
//! # fn increment(&mut self) {
//! # self.inner += 1;
//! # }
//! # }
//! # fn main() -> PyResult<()> {
//! Python::with_gil(|py| {
//! let n = PyCell::new(py, Number { inner: 0 })?;
//!
//! // We borrow the guard and then dereference
//! // it to get a mutable reference to Number
//! let mut guard: PyRefMut<'_, Number> = n.borrow_mut();
//! let n_mutable: &mut Number = &mut *guard;
//!
//! n_mutable.increment();
//!
//! // To avoid panics we must dispose of the
//! // `PyRefMut` before borrowing again.
//! drop(guard);
//!
//! let n_immutable : &Number = &n.borrow();
//! assert_eq!(n_immutable.inner, 1);
//!
//! Ok(())
//! })
//! # }
//! ```
//! ## Dealing with possibly overlapping mutable references
//!
//! It is also necessary to use [`PyCell`] if you can receive mutable arguments that may overlap.
//! Suppose the following function that swaps the values of two `Number`s:
//! ```
//! # use pyo3::prelude::*;
//! # #[pyclass]
//! # pub struct Number {
//! # inner: u32,
//! # }
//! #[pyfunction]
//! fn swap_numbers(a: &mut Number, b: &mut Number){
//! std::mem::swap(&mut a.inner, &mut b.inner);
//! }
//! ```
//! When users pass in the same `Number` as both arguments, one of the mutable borrows will
//! fail and raise a `RuntimeError`:
//! ```text
//! >>> a = Number()
//! >>> swap_numbers(a,a)
//! Traceback (most recent call last):
//! File "<stdin>", line 1, in <module>
//! RuntimeError: Already borrowed
//! ```
//!
//! It is better to write that function like this:
//! ```
//! # use pyo3::prelude::*;
//! # #[pyclass]
//! # pub struct Number {
//! # inner: u32,
//! # }
//! #[pyfunction]
//! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) {
//! // Check that the pointers are unequal
//! if a.as_ref() != b.as_ref() {
//! std::mem::swap(&mut a.borrow_mut().inner, &mut b.borrow_mut().inner);
//! } else {
//! // Do nothing - they are the same object, so don't need swapping.
//! }
//! }
//! ```
//! See the [guide] for more information.
//!
//! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability"
//! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language"
use crate::exceptions::PyRuntimeError;
use crate::pyclass::PyClass;
@ -32,71 +170,41 @@ pub struct PyCellBase<T> {
unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {}
/// `PyCell` is the container type for [`PyClass`](../pyclass/trait.PyClass.html) values.
/// A container type for (mutably) accessing [`PyClass`] values
///
/// From the Python side, `PyCell<T>` is the concrete layout of `T: PyClass` in the Python heap,
/// which means we can convert `*const PyClass<T>` to `*mut ffi::PyObject`.
///
/// From the Rust side, `PyCell<T>` is the mutable container of `T`.
/// Since `PyCell<T: PyClass>` is always on the Python heap, we don't have the ownership of it.
/// Thus, to mutate the data behind `&PyCell<T>` safely, we employ the
/// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
/// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html).
///
/// `PyCell` implements `Deref<Target = PyAny>`, so you can also call methods from `PyAny`
/// when you have a `PyCell<T>`.
/// `PyCell` autodereferences to [`PyAny`], so you can call `PyAny`'s methods on a `PyCell<T>`.
///
/// # Examples
///
/// In most cases, `PyCell` is hidden behind `#[pymethods]`.
/// However, you can construct a `&PyCell` directly to test your pyclass in Rust code.
/// This example demonstrates getting a mutable reference of the contained `PyClass`.
/// ```rust
/// use pyo3::prelude::*;
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Book {
/// #[pyo3(get)]
/// name: &'static str,
/// author: &'static str,
/// }
/// let book = Book {
/// name: "The Man in the High Castle",
/// author: "Philip Kindred Dick",
/// };
/// Python::with_gil(|py| {
/// let book_cell = PyCell::new(py, book).unwrap();
/// // `&PyCell` implements `ToPyObject`, so you can use it in a Python snippet
/// pyo3::py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'");
/// });
/// ```
/// You can use `slf: &PyCell<Self>` as an alternative `self` receiver of `#[pymethod]`,
/// though you'll rarely need it.
/// ```
/// # use pyo3::prelude::*;
/// use std::collections::HashMap;
/// #[pyclass]
/// #[derive(Default)]
/// struct Counter {
/// counter: HashMap<String, usize>
/// struct Number {
/// inner: u32,
/// }
///
/// #[pymethods]
/// impl Counter {
/// // You can use &mut self here, but now we use &PyCell for demonstration
/// fn increment(slf: &PyCell<Self>, name: String) -> PyResult<usize> {
/// let mut slf_mut = slf.try_borrow_mut()?;
/// // Now a mutable reference exists so we cannot get another one
/// assert!(slf.try_borrow().is_err());
/// assert!(slf.try_borrow_mut().is_err());
/// let counter = slf_mut.counter.entry(name).or_insert(0);
/// *counter += 1;
/// Ok(*counter)
/// impl Number {
/// fn increment(&mut self) {
/// self.inner += 1;
/// }
/// }
/// # Python::with_gil(|py| {
/// # let counter = PyCell::new(py, Counter::default()).unwrap();
/// # pyo3::py_run!(py, counter, "assert counter.increment('cat') == 1");
/// # });
///
/// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| {
/// let n = PyCell::new(py, Number { inner: 0 })?;
///
/// let n_mutable: &mut Number = &mut n.borrow_mut();
/// n_mutable.increment();
///
/// Ok(())
/// })
/// # }
/// ```
/// For more information on how, when and why (not) to use `PyCell` please see the
/// [module-level documentation](self).
#[repr(C)]
pub struct PyCell<T: PyClass> {
ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
@ -195,8 +303,7 @@ impl<T: PyClass> PyCell<T> {
/// Makes a new `PyCell` on the Python heap and return the reference to it.
///
/// In cases where the value in the cell does not need to be accessed immediately after
/// creation, consider [`Py::new`](../instance/struct.Py.html#method.new) as a more efficient
/// alternative.
/// creation, consider [`Py::new`](crate::Py::new) as a more efficient alternative.
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self> {
unsafe {
let initializer = value.into();
@ -219,7 +326,7 @@ impl<T: PyClass> PyCell<T> {
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// Panics if the value is currently borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T> {
self.try_borrow_mut().expect("Already borrowed")
@ -236,6 +343,7 @@ impl<T: PyClass> PyCell<T> {
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
///
/// Python::with_gil(|py| {
/// let c = PyCell::new(py, Class {}).unwrap();
/// {
@ -410,9 +518,9 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
}
}
/// Wraps a borrowed reference to a value in a `PyCell<T>`.
/// A wrapper type for an immutably borrowed value from a [`PyCell`]`<T>`.
///
/// See the [`PyCell`](struct.PyCell.html) documentation for more.
/// See the [`PyCell`] documentation for more information.
///
/// # Examples
///
@ -425,16 +533,19 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
/// struct Parent {
/// basename: &'static str,
/// }
///
/// #[pyclass(extends=Parent)]
/// struct Child {
/// name: &'static str,
/// }
///
/// #[pymethods]
/// impl Child {
/// #[new]
/// fn new() -> (Self, Parent) {
/// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" })
/// }
///
/// fn format(slf: PyRef<Self>) -> String {
/// // We can get *mut ffi::PyObject from PyRef
/// use pyo3::AsPyPointer;
@ -449,13 +560,14 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 3)'");
/// # });
/// ```
///
/// See the [module-level documentation](self) for more information.
pub struct PyRef<'p, T: PyClass> {
inner: &'p PyCell<T>,
}
impl<'p, T: PyClass> PyRef<'p, T> {
/// Returns a `Python` token.
/// This function is safe since `PyRef` has the same lifetime as a `GILGuard`.
/// Returns a `Python` token that is bound to the lifetime of the `PyRef`.
pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
}
@ -491,14 +603,17 @@ where
/// struct Base1 {
/// name1: &'static str,
/// }
///
/// #[pyclass(extends=Base1, subclass)]
/// struct Base2 {
/// name2: &'static str,
/// }
///
/// #[pyclass(extends=Base2)]
/// struct Sub {
/// name3: &'static str,
/// }
///
/// #[pymethods]
/// impl Sub {
/// #[new]
@ -568,16 +683,15 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
}
}
/// Wraps a mutable borrowed reference to a value in a `PyCell<T>`.
/// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`.
///
/// See the [`PyCell`](struct.PyCell.html) and [`PyRef`](struct.PyRef.html) documentation for more.
/// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: PyClass> {
inner: &'p PyCell<T>,
}
impl<'p, T: PyClass> PyRefMut<'p, T> {
/// Returns a `Python` token.
/// This function is safe since `PyRefMut` has the same lifetime as a `GILGuard`.
/// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`.
pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
}
@ -609,7 +723,8 @@ where
U: PyClass,
{
/// Gets a `PyRef<T::BaseType>`.
/// See [`PyRef::into_super`](struct.PyRef.html#method.into_super) for more.
///
/// See [`PyRef::into_super`] for more.
pub fn into_super(self) -> PyRefMut<'p, U> {
let PyRefMut { inner } = self;
std::mem::forget(self);
@ -681,9 +796,9 @@ impl BorrowFlag {
}
}
/// An error returned by [`PyCell::try_borrow`](struct.PyCell.html#method.try_borrow).
/// An error type returned by [`PyCell::try_borrow`].
///
/// In Python, you can catch this error using `except RuntimeError`.
/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowError {
_private: (),
}
@ -706,9 +821,9 @@ impl From<PyBorrowError> for PyErr {
}
}
/// An error returned by [`PyCell::try_borrow_mut`](struct.PyCell.html#method.try_borrow_mut).
/// An error type returned by [`PyCell::try_borrow_mut`].
///
/// In Python, you can catch this error using `except RuntimeError`.
/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
pub struct PyBorrowMutError {
_private: (),
}