#![cfg(feature = "macros")] use pyo3::impl_::pyclass::{PyClassBaseType, PyClassImpl}; use pyo3::prelude::*; use pyo3::pycell::{ BorrowChecker, ExtendsMutableAncestor, ImmutableClass, MutableClass, PyClassMutability, }; use pyo3::pyclass::boolean_struct::{False, True}; use pyo3::PyClass; #[pyclass(subclass)] struct MutableBase; #[pyclass(extends = MutableBase, subclass)] struct MutableChildOfMutableBase; #[pyclass(extends = MutableBase, frozen, subclass)] struct ImmutableChildOfMutableBase; #[pyclass(extends = MutableChildOfMutableBase)] struct MutableChildOfMutableChildOfMutableBase; #[pyclass(extends = ImmutableChildOfMutableBase)] struct MutableChildOfImmutableChildOfMutableBase; #[pyclass(extends = MutableChildOfMutableBase, frozen)] struct ImmutableChildOfMutableChildOfMutableBase; #[pyclass(extends = ImmutableChildOfMutableBase, frozen)] struct ImmutableChildOfImmutableChildOfMutableBase; #[pyclass(frozen, subclass)] struct ImmutableBase; #[pyclass(extends = ImmutableBase, subclass)] struct MutableChildOfImmutableBase; #[pyclass(extends = ImmutableBase, frozen, subclass)] struct ImmutableChildOfImmutableBase; #[pyclass(extends = MutableChildOfImmutableBase)] struct MutableChildOfMutableChildOfImmutableBase; #[pyclass(extends = ImmutableChildOfImmutableBase)] struct MutableChildOfImmutableChildOfImmutableBase; #[pyclass(extends = MutableChildOfImmutableBase, frozen)] struct ImmutableChildOfMutableChildOfImmutableBase; #[pyclass(extends = ImmutableChildOfImmutableBase, frozen)] struct ImmutableChildOfImmutableChildOfImmutableBase; fn assert_mutable>() {} fn assert_immutable>() {} fn assert_mutable_with_mutable_ancestor< T: PyClass>, >() // These horrible bounds are necessary for Rust 1.48 but not newer versions where ::BaseType: PyClassImpl>, <::BaseType as PyClassImpl>::PyClassMutability: PyClassMutability, ::BaseType: PyClassBaseType>, { } fn assert_immutable_with_mutable_ancestor< T: PyClass>, >() // These horrible bounds are necessary for Rust 1.48 but not newer versions where ::BaseType: PyClassImpl>, <::BaseType as PyClassImpl>::PyClassMutability: PyClassMutability, ::BaseType: PyClassBaseType>, { } #[test] fn test_inherited_mutability() { // mutable base assert_mutable::(); // children of mutable base have a mutable ancestor assert_mutable_with_mutable_ancestor::(); assert_immutable_with_mutable_ancestor::(); // grandchildren of mutable base have a mutable ancestor assert_mutable_with_mutable_ancestor::(); assert_mutable_with_mutable_ancestor::(); assert_immutable_with_mutable_ancestor::(); assert_immutable_with_mutable_ancestor::(); // immutable base and children assert_immutable::(); assert_immutable::(); assert_immutable::(); // mutable children of immutable at any level are simply mutable assert_mutable::(); assert_mutable::(); // children of the mutable child display this property assert_mutable_with_mutable_ancestor::(); assert_immutable_with_mutable_ancestor::(); } #[test] fn test_mutable_borrow_prevents_further_borrows() { Python::with_gil(|py| { let mmm = Py::new( py, PyClassInitializer::from(MutableBase) .add_subclass(MutableChildOfMutableBase) .add_subclass(MutableChildOfMutableChildOfMutableBase), ) .unwrap(); let mmm_cell: &PyCell = mmm.as_ref(py); let mmm_refmut = mmm_cell.borrow_mut(); // Cannot take any other mutable or immutable borrows whilst the object is borrowed mutably assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell.extract::>().is_err()); assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell.extract::>().is_err()); // With the borrow dropped, all other borrow attempts will succeed drop(mmm_refmut); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell.extract::>().is_ok()); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell.extract::>().is_ok()); }) } #[test] fn test_immutable_borrows_prevent_mutable_borrows() { Python::with_gil(|py| { let mmm = Py::new( py, PyClassInitializer::from(MutableBase) .add_subclass(MutableChildOfMutableBase) .add_subclass(MutableChildOfMutableChildOfMutableBase), ) .unwrap(); let mmm_cell: &PyCell = mmm.as_ref(py); let mmm_refmut = mmm_cell.borrow(); // Further immutable borrows are ok assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell.extract::>().is_ok()); // Further mutable borrows are not ok assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell .extract::>() .is_err()); assert!(mmm_cell.extract::>().is_err()); // With the borrow dropped, all mutable borrow attempts will succeed drop(mmm_refmut); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell .extract::>() .is_ok()); assert!(mmm_cell.extract::>().is_ok()); }) }