Merge pull request #2448 from davidhewitt/frozen

pyclass: switch from immutable to frozen
This commit is contained in:
David Hewitt 2022-06-20 07:27:22 +01:00 committed by GitHub
commit 920fa93fa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 316 additions and 161 deletions

View File

@ -951,7 +951,9 @@ unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
}
}
impl ::pyo3::PyClass for MyClass { }
impl ::pyo3::PyClass for MyClass {
type Frozen = pyo3::pyclass::boolean_struct::False;
}
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut MyClass {
type Target = ::pyo3::PyRefMut<'a, MyClass>;
@ -974,7 +976,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type Layout = PyCell<MyClass>;
type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>;
type Mutability = pyo3::pycell::Mutable;
type PyClassMutability = pyo3::pycell::MutableClass;
type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;

View File

@ -15,6 +15,7 @@ pub mod kw {
syn::custom_keyword!(extends);
syn::custom_keyword!(freelist);
syn::custom_keyword!(from_py_with);
syn::custom_keyword!(frozen);
syn::custom_keyword!(gc);
syn::custom_keyword!(get);
syn::custom_keyword!(item);
@ -29,7 +30,6 @@ pub mod kw {
syn::custom_keyword!(transparent);
syn::custom_keyword!(unsendable);
syn::custom_keyword!(weakref);
syn::custom_keyword!(immutable);
}
#[derive(Clone, Debug)]

View File

@ -61,7 +61,7 @@ pub struct PyClassPyO3Options {
pub dict: Option<kw::dict>,
pub extends: Option<ExtendsAttribute>,
pub freelist: Option<FreelistAttribute>,
pub immutable: Option<kw::immutable>,
pub frozen: Option<kw::frozen>,
pub mapping: Option<kw::mapping>,
pub module: Option<ModuleAttribute>,
pub name: Option<NameAttribute>,
@ -78,7 +78,7 @@ enum PyClassPyO3Option {
Dict(kw::dict),
Extends(ExtendsAttribute),
Freelist(FreelistAttribute),
Immutable(kw::immutable),
Frozen(kw::frozen),
Mapping(kw::mapping),
Module(ModuleAttribute),
Name(NameAttribute),
@ -101,8 +101,8 @@ impl Parse for PyClassPyO3Option {
input.parse().map(PyClassPyO3Option::Extends)
} else if lookahead.peek(attributes::kw::freelist) {
input.parse().map(PyClassPyO3Option::Freelist)
} else if lookahead.peek(attributes::kw::immutable) {
input.parse().map(PyClassPyO3Option::Immutable)
} else if lookahead.peek(attributes::kw::frozen) {
input.parse().map(PyClassPyO3Option::Frozen)
} else if lookahead.peek(attributes::kw::mapping) {
input.parse().map(PyClassPyO3Option::Mapping)
} else if lookahead.peek(attributes::kw::module) {
@ -160,7 +160,7 @@ impl PyClassPyO3Options {
PyClassPyO3Option::Dict(dict) => set_option!(dict),
PyClassPyO3Option::Extends(extends) => set_option!(extends),
PyClassPyO3Option::Freelist(freelist) => set_option!(freelist),
PyClassPyO3Option::Immutable(immutable) => set_option!(immutable),
PyClassPyO3Option::Frozen(frozen) => set_option!(frozen),
PyClassPyO3Option::Mapping(mapping) => set_option!(mapping),
PyClassPyO3Option::Module(module) => set_option!(module),
PyClassPyO3Option::Name(name) => set_option!(name),
@ -713,12 +713,14 @@ impl<'a> PyClassImplsBuilder<'a> {
let cls = self.cls;
quote! {
impl _pyo3::PyClass for #cls { }
impl _pyo3::PyClass for #cls {
type Frozen = <Self::PyClassMutability as _pyo3::pycell::PyClassMutability>::Frozen;
}
}
}
fn impl_extractext(&self) -> TokenStream {
let cls = self.cls;
if self.attr.options.immutable.is_some() {
if self.attr.options.frozen.is_some() {
quote! {
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls
{
@ -855,17 +857,7 @@ impl<'a> PyClassImplsBuilder<'a> {
let deprecations = &self.attr.deprecations;
let mutability = if self.attr.options.immutable.is_some() {
quote! {
_pyo3::pycell::Immutable
}
} else {
quote! {
_pyo3::pycell::Mutable
}
};
let class_mutability = if self.attr.options.immutable.is_some() {
let class_mutability = if self.attr.options.frozen.is_some() {
quote! {
ImmutableChild
}
@ -907,7 +899,6 @@ impl<'a> PyClassImplsBuilder<'a> {
type BaseType = #base;
type ThreadChecker = #thread_checker;
#inventory
type Mutability = #mutability;
type PyClassMutability = <<#base as _pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as _pyo3::pycell::PyClassMutability>::#class_mutability;
type Dict = #dict;
type WeakRef = #weakref;

View File

@ -10,9 +10,8 @@
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput};
use crate::{
exceptions, ffi, pyclass::MutablePyClass, FromPyObject, PyAny, PyCell, PyClass, PyObject,
};
use crate::pyclass::boolean_struct::False;
use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int;
/// Basic Python class customization
@ -28,14 +27,14 @@ pub trait PyObjectProtocol<'p>: PyClass {
fn __setattr__(&'p mut self, name: Self::Name, value: Self::Value) -> Self::Result
where
Self: PyObjectSetAttrProtocol<'p> + MutablePyClass,
Self: PyObjectSetAttrProtocol<'p> + PyClass<Frozen = False>,
{
unimplemented!()
}
fn __delattr__(&'p mut self, name: Self::Name) -> Self::Result
where
Self: PyObjectDelAttrProtocol<'p> + MutablePyClass,
Self: PyObjectDelAttrProtocol<'p> + PyClass<Frozen = False>,
{
unimplemented!()
}
@ -79,12 +78,12 @@ pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + PyClass<Frozen = False> {
type Name: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + PyClass<Frozen = False> {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -6,7 +6,8 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use crate::callback::IntoPyCallbackOutput;
use crate::{ffi, pyclass::MutablePyClass, PyCell, PyRefMut};
use crate::pyclass::boolean_struct::False;
use crate::{ffi, PyCell, PyClass, PyRefMut};
use std::os::raw::c_int;
/// Buffer protocol interface
@ -15,7 +16,7 @@ use std::os::raw::c_int;
/// c-api.
#[allow(unused_variables)]
#[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")]
pub trait PyBufferProtocol<'p>: MutablePyClass {
pub trait PyBufferProtocol<'p>: PyClass<Frozen = False> {
// No default implementations so that implementors of this trait provide both methods.
fn bf_getbuffer(

View File

@ -3,14 +3,15 @@
//! Python GC support
use crate::{ffi, pyclass::MutablePyClass, PyCell};
use crate::pyclass::boolean_struct::False;
use crate::{ffi, PyCell, PyClass};
use std::os::raw::{c_int, c_void};
pub use crate::impl_::pymethods::{PyTraverseError, PyVisit};
/// GC support
#[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")]
pub trait PyGCProtocol<'p>: MutablePyClass {
pub trait PyGCProtocol<'p>: PyClass<Frozen = False> {
fn __traverse__(&'p self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self);
}

View File

@ -5,12 +5,13 @@
//! Trait and support implementation for implementing mapping support
use crate::callback::IntoPyCallbackOutput;
use crate::{pyclass::MutablePyClass, FromPyObject, PyClass, PyObject};
use crate::pyclass::boolean_struct::False;
use crate::{FromPyObject, PyClass, PyObject};
/// Mapping interface
#[allow(unused_variables)]
#[deprecated(since = "0.16.0", note = "prefer `#[pymethods]` to `#[pyproto]`")]
pub trait PyMappingProtocol<'p>: PyClass {
pub trait PyMappingProtocol<'p>: PyClass<Frozen = False> {
fn __len__(&'p self) -> Self::Result
where
Self: PyMappingLenProtocol<'p>,
@ -52,13 +53,13 @@ pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + PyClass<Frozen = False> {
type Key: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + PyClass<Frozen = False> {
type Key: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -5,7 +5,8 @@
//! Trait and support implementation for implementing number protocol
use crate::callback::IntoPyCallbackOutput;
use crate::err::PyErr;
use crate::{ffi, pyclass::MutablePyClass, FromPyObject, PyClass, PyObject};
use crate::pyclass::boolean_struct::False;
use crate::{ffi, FromPyObject, PyClass, PyObject};
/// Number interface
#[allow(unused_variables)]
@ -461,47 +462,47 @@ pub trait PyNumberROrProtocol<'p>: PyNumberProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
// See https://bugs.python.org/issue36379
@ -509,28 +510,28 @@ pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
}
#[allow(clippy::upper_case_acronyms)]
pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
#[allow(clippy::upper_case_acronyms)]
pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> + MutablePyClass {
pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> + PyClass<Frozen = False> {
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -6,7 +6,8 @@
use crate::callback::IntoPyCallbackOutput;
use crate::conversion::{FromPyObject, IntoPy};
use crate::err::PyErr;
use crate::{exceptions, ffi, pyclass::MutablePyClass, PyAny, PyCell, PyClass, PyObject};
use crate::pyclass::boolean_struct::False;
use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject};
use std::os::raw::c_int;
/// Sequence interface
@ -89,13 +90,13 @@ pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + PyClass<Frozen = False> {
type Index: FromPyObject<'p> + From<isize>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + PyClass<Frozen = False> {
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<()>;
}
@ -116,14 +117,14 @@ pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> {
}
pub trait PySequenceInplaceConcatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass
PySequenceProtocol<'p> + IntoPy<PyObject> + PyClass<Frozen = False>
{
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<Self>;
}
pub trait PySequenceInplaceRepeatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass + 'p
PySequenceProtocol<'p> + IntoPy<PyObject> + PyClass<Frozen = False> + 'p
{
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<Self>;

View File

@ -2,11 +2,11 @@
//! Defines conversions between Rust and Python types.
use crate::err::{self, PyDowncastError, PyResult};
use crate::pyclass::boolean_struct::False;
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
use crate::{
ffi, gil, pyclass::MutablePyClass, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef,
PyRefMut, Python,
ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
use std::ptr::NonNull;
@ -375,7 +375,7 @@ where
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;

View File

@ -2,7 +2,7 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::{GetBorrowChecker, Mutability, PyCellLayout, PyClassMutability},
pycell::{GetBorrowChecker, PyCellLayout, PyClassMutability},
pyclass_init::PyObjectInit,
type_object::PyLayout,
Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
@ -164,9 +164,6 @@ pub trait PyClassImpl: Sized {
/// Base class
type BaseType: PyTypeInfo + PyClassBaseType;
/// Immutable or mutable
type Mutability: Mutability;
/// Immutable or mutable
type PyClassMutability: PyClassMutability + GetBorrowChecker<Self>;

View File

@ -1,3 +1,4 @@
use crate::pyclass::boolean_struct::False;
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::conversion::PyTryFrom;
use crate::err::{self, PyDowncastError, PyErr, PyResult};
@ -5,8 +6,8 @@ use crate::gil;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::types::{PyDict, PyString, PyTuple};
use crate::{
ffi, pyclass::MutablePyClass, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass,
PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
};
use std::marker::PhantomData;
use std::mem;
@ -432,7 +433,7 @@ where
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
self.as_ref(py).borrow_mut()
}
@ -462,7 +463,7 @@ where
py: Python<'py>,
) -> Result<PyRefMut<'py, T>, PyBorrowMutError>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
self.as_ref(py).try_borrow_mut()
}
@ -908,7 +909,7 @@ where
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
fn from(pyref: PyRefMut<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) }
@ -1116,4 +1117,32 @@ a = A()
Ok(())
})
}
#[cfg(feature = "macros")]
mod using_macros {
use super::*;
#[crate::pyclass]
#[pyo3(crate = "crate")]
struct SomeClass(i32);
#[test]
fn instance_borrow_methods() {
// More detailed tests of the underlying semantics in pycell.rs
Python::with_gil(|py| {
let instance = Py::new(py, SomeClass(0)).unwrap();
assert_eq!(instance.borrow(py).0, 0);
assert_eq!(instance.try_borrow(py).unwrap().0, 0);
assert_eq!(instance.borrow_mut(py).0, 0);
assert_eq!(instance.try_borrow_mut(py).unwrap().0, 0);
instance.borrow_mut(py).0 = 123;
assert_eq!(instance.borrow(py).0, 123);
assert_eq!(instance.try_borrow(py).unwrap().0, 123);
assert_eq!(instance.borrow_mut(py).0, 123);
assert_eq!(instance.try_borrow_mut(py).unwrap().0, 123);
})
}
}
}

View File

@ -200,7 +200,11 @@ use crate::exceptions::PyRuntimeError;
use crate::impl_::pyclass::{
PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef,
};
use crate::pyclass::{MutablePyClass, PyClass};
use crate::pyclass::PyClass;
use crate::pyclass::{
boolean_struct::{False, True},
Frozen,
};
use crate::pyclass_init::PyClassInitializer;
use crate::type_object::{PyLayout, PySizedLayout};
use crate::types::PyAny;
@ -321,6 +325,7 @@ pub trait PyClassMutability {
type Checker: PyClassBorrowChecker;
type ImmutableChild: PyClassMutability;
type MutableChild: PyClassMutability;
type Frozen: Frozen;
}
pub trait GetBorrowChecker<T: PyClassImpl> {
@ -360,6 +365,7 @@ impl PyClassMutability for ImmutableClass {
type Checker = EmptySlot;
type ImmutableChild = ImmutableClass;
type MutableChild = MutableClass;
type Frozen = True;
}
impl PyClassMutability for MutableClass {
@ -367,6 +373,7 @@ impl PyClassMutability for MutableClass {
type Checker = BorrowChecker;
type ImmutableChild = ExtendsMutableAncestor<ImmutableClass>;
type MutableChild = ExtendsMutableAncestor<MutableClass>;
type Frozen = False;
}
impl<M: PyClassMutability> PyClassMutability for ExtendsMutableAncestor<M> {
@ -374,15 +381,9 @@ impl<M: PyClassMutability> PyClassMutability for ExtendsMutableAncestor<M> {
type Checker = BorrowChecker;
type ImmutableChild = ExtendsMutableAncestor<ImmutableClass>;
type MutableChild = ExtendsMutableAncestor<MutableClass>;
type Frozen = M::Frozen;
}
pub trait Mutability {}
pub struct Mutable;
impl Mutability for Mutable {}
pub struct Immutable;
impl Mutability for Immutable {}
/// Base layout of PyCell.
#[doc(hidden)]
#[repr(C)]
@ -475,7 +476,7 @@ impl<T: PyClass> PyCell<T> {
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
self.try_borrow_mut().expect("Already borrowed")
}
@ -535,7 +536,7 @@ impl<T: PyClass> PyCell<T> {
/// ```
pub fn try_borrow_mut(&self) -> Result<PyRefMut<'_, T>, PyBorrowMutError>
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
self.ensure_threadsafe();
self.borrow_checker()
@ -587,7 +588,7 @@ impl<T: PyClass> PyCell<T> {
#[inline]
pub fn replace(&self, t: T) -> T
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
std::mem::replace(&mut *self.borrow_mut(), t)
}
@ -599,7 +600,7 @@ impl<T: PyClass> PyCell<T> {
/// Panics if the value is currently borrowed.
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
let mut_borrow = &mut *self.borrow_mut();
let replacement = f(mut_borrow);
@ -614,7 +615,7 @@ impl<T: PyClass> PyCell<T> {
#[inline]
pub fn swap(&self, other: &Self)
where
T: MutablePyClass,
T: PyClass<Frozen = False>,
{
std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
}
@ -759,8 +760,8 @@ impl<'p, T: PyClass> PyRef<'p, T> {
impl<'p, T, U> AsRef<U> for PyRef<'p, T>
where
T: MutablePyClass<BaseType = U>, // For now, only mutable classes can be extended
U: MutablePyClass,
T: PyClass<BaseType = U>,
U: PyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
@ -769,8 +770,8 @@ where
impl<'p, T, U> PyRef<'p, T>
where
T: MutablePyClass<BaseType = U>, // For now, only mutable classes can be extended
U: MutablePyClass,
T: PyClass<BaseType = U>,
U: PyClass,
{
/// Gets a `PyRef<T::BaseType>`.
///
@ -869,11 +870,11 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
/// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`.
///
/// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: MutablePyClass> {
pub struct PyRefMut<'p, T: PyClass<Frozen = False>> {
inner: &'p PyCell<T>,
}
impl<'p, T: MutablePyClass> PyRefMut<'p, T> {
impl<'p, T: PyClass<Frozen = False>> PyRefMut<'p, T> {
/// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`.
pub fn py(&self) -> Python<'_> {
unsafe { Python::assume_gil_acquired() }
@ -882,8 +883,8 @@ impl<'p, T: MutablePyClass> PyRefMut<'p, T> {
impl<'p, T, U> AsRef<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
T: PyClass<BaseType = U, Frozen = False>,
U: PyClass<Frozen = False>,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
@ -892,8 +893,8 @@ where
impl<'p, T, U> AsMut<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
T: PyClass<BaseType = U, Frozen = False>,
U: PyClass<Frozen = False>,
{
fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() }
@ -902,8 +903,8 @@ where
impl<'p, T, U> PyRefMut<'p, T>
where
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
T: PyClass<BaseType = U, Frozen = False>,
U: PyClass<Frozen = False>,
{
/// Gets a `PyRef<T::BaseType>`.
///
@ -917,7 +918,7 @@ where
}
}
impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> {
impl<'p, T: PyClass<Frozen = False>> Deref for PyRefMut<'p, T> {
type Target = T;
#[inline]
@ -926,39 +927,41 @@ impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> {
}
}
impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> {
impl<'p, T: PyClass<Frozen = False>> DerefMut for PyRefMut<'p, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.get_ptr() }
}
}
impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> {
impl<'p, T: PyClass<Frozen = False>> Drop for PyRefMut<'p, T> {
fn drop(&mut self) {
self.inner.borrow_checker().release_borrow_mut()
}
}
impl<T: MutablePyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
impl<T: PyClass<Frozen = False>> IntoPy<PyObject> for PyRefMut<'_, T> {
fn into_py(self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) }
}
}
impl<'a, T: MutablePyClass> AsPyPointer for PyRefMut<'a, T> {
impl<'a, T: PyClass<Frozen = False>> AsPyPointer for PyRefMut<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr()
}
}
impl<'a, T: MutablePyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> {
impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>>
for crate::PyRefMut<'a, T>
{
type Error = PyBorrowMutError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
cell.try_borrow_mut()
}
}
impl<T: MutablePyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
impl<T: PyClass<Frozen = False> + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*(self.deref()), f)
}
@ -1083,3 +1086,102 @@ where
<T::BaseType as PyClassBaseType>::LayoutAsBase::tp_dealloc(slf, py)
}
}
#[cfg(test)]
#[cfg(feature = "macros")]
mod tests {
use super::*;
#[crate::pyclass]
#[pyo3(crate = "crate")]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct SomeClass(i32);
#[test]
fn pycell_replace() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
assert_eq!(*cell.borrow(), SomeClass(0));
let previous = cell.replace(SomeClass(123));
assert_eq!(previous, SomeClass(0));
assert_eq!(*cell.borrow(), SomeClass(123));
})
}
#[test]
#[should_panic(expected = "Already borrowed: PyBorrowMutError")]
fn pycell_replace_panic() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
let _guard = cell.borrow();
cell.replace(SomeClass(123));
})
}
#[test]
fn pycell_replace_with() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
assert_eq!(*cell.borrow(), SomeClass(0));
let previous = cell.replace_with(|value| {
*value = SomeClass(2);
SomeClass(123)
});
assert_eq!(previous, SomeClass(2));
assert_eq!(*cell.borrow(), SomeClass(123));
})
}
#[test]
#[should_panic(expected = "Already borrowed: PyBorrowMutError")]
fn pycell_replace_with_panic() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
let _guard = cell.borrow();
cell.replace_with(|_| SomeClass(123));
})
}
#[test]
fn pycell_swap() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
let cell2 = PyCell::new(py, SomeClass(123)).unwrap();
assert_eq!(*cell.borrow(), SomeClass(0));
assert_eq!(*cell2.borrow(), SomeClass(123));
cell.swap(cell2);
assert_eq!(*cell.borrow(), SomeClass(123));
assert_eq!(*cell2.borrow(), SomeClass(0));
})
}
#[test]
#[should_panic(expected = "Already borrowed: PyBorrowMutError")]
fn pycell_swap_panic() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
let cell2 = PyCell::new(py, SomeClass(123)).unwrap();
let _guard = cell.borrow();
cell.swap(cell2);
})
}
#[test]
#[should_panic(expected = "Already borrowed: PyBorrowMutError")]
fn pycell_swap_panic_other_borrowed() {
Python::with_gil(|py| {
let cell = PyCell::new(py, SomeClass(0)).unwrap();
let cell2 = PyCell::new(py, SomeClass(123)).unwrap();
let _guard = cell2.borrow();
cell.swap(cell2);
})
}
}

View File

@ -1,5 +1,4 @@
//! `PyClass` and related traits.
use crate::pycell::{Immutable, Mutable};
use crate::{
callback::IntoPyCallbackOutput,
exceptions::PyTypeError,
@ -25,14 +24,10 @@ use std::{
pub trait PyClass:
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<Self>>
{
/// Frozen or not
type Frozen: Frozen;
}
pub trait MutablePyClass: PyClass<Mutability = Mutable> {}
pub trait ImmutablePyClass: PyClass<Mutability = Immutable> {}
impl<T> MutablePyClass for T where T: PyClass<Mutability = Mutable> {}
impl<T> ImmutablePyClass for T where T: PyClass<Mutability = Immutable> {}
fn into_raw<T>(vec: Vec<T>) -> *mut c_void {
Box::into_raw(vec.into_boxed_slice()) as _
}
@ -578,3 +573,27 @@ pub(crate) unsafe extern "C" fn no_constructor_defined(
))
})
}
/// A mechanism to have associated True / False values in the absence of
/// associated const equality.
pub mod boolean_struct {
pub(crate) mod private {
use super::*;
/// A way to "seal" the boolean traits.
pub trait Boolean {}
impl Boolean for True {}
impl Boolean for False {}
}
pub struct True(());
pub struct False(());
}
/// A trait which is used to describe whether a `#[pyclass]` is frozen.
pub trait Frozen: boolean_struct::private::Boolean {}
impl Frozen for boolean_struct::True {}
impl Frozen for boolean_struct::False {}

View File

@ -1,7 +1,6 @@
//! Contains initialization utilities for `#[pyclass]`.
use crate::callback::IntoPyCallbackOutput;
use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
use crate::pyclass::MutablePyClass;
use crate::{ffi, PyCell, PyClass, PyErr, PyResult, Python};
use crate::{
ffi::PyTypeObject,
@ -270,8 +269,8 @@ where
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: MutablePyClass<BaseType = B>,
B: MutablePyClass,
S: PyClass<BaseType = B>,
B: PyClass,
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {

View File

@ -98,7 +98,7 @@ fn _test_compile_errors() {
#[rustversion::since(1.60)]
fn tests_rust_1_60(t: &trybuild::TestCases) {
t.compile_fail("tests/ui/invalid_intern_arg.rs");
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
t.compile_fail("tests/ui/invalid_frozen_pyclass_borrow.rs");
t.compile_fail("tests/ui/invalid_pymethod_receiver.rs");
t.compile_fail("tests/ui/missing_intopy.rs");
}

View File

@ -5,6 +5,7 @@ use pyo3::prelude::*;
use pyo3::pycell::{
BorrowChecker, ExtendsMutableAncestor, ImmutableClass, MutableClass, PyClassMutability,
};
use pyo3::pyclass::boolean_struct::{False, True};
use pyo3::PyClass;
#[pyclass(subclass)]
@ -13,7 +14,7 @@ struct MutableBase;
#[pyclass(extends = MutableBase, subclass)]
struct MutableChildOfMutableBase;
#[pyclass(extends = MutableBase, immutable, subclass)]
#[pyclass(extends = MutableBase, frozen, subclass)]
struct ImmutableChildOfMutableBase;
#[pyclass(extends = MutableChildOfMutableBase)]
@ -22,19 +23,19 @@ struct MutableChildOfMutableChildOfMutableBase;
#[pyclass(extends = ImmutableChildOfMutableBase)]
struct MutableChildOfImmutableChildOfMutableBase;
#[pyclass(extends = MutableChildOfMutableBase, immutable)]
#[pyclass(extends = MutableChildOfMutableBase, frozen)]
struct ImmutableChildOfMutableChildOfMutableBase;
#[pyclass(extends = ImmutableChildOfMutableBase, immutable)]
#[pyclass(extends = ImmutableChildOfMutableBase, frozen)]
struct ImmutableChildOfImmutableChildOfMutableBase;
#[pyclass(immutable, subclass)]
#[pyclass(frozen, subclass)]
struct ImmutableBase;
#[pyclass(extends = ImmutableBase, subclass)]
struct MutableChildOfImmutableBase;
#[pyclass(extends = ImmutableBase, immutable, subclass)]
#[pyclass(extends = ImmutableBase, frozen, subclass)]
struct ImmutableChildOfImmutableBase;
#[pyclass(extends = MutableChildOfImmutableBase)]
@ -43,16 +44,16 @@ struct MutableChildOfMutableChildOfImmutableBase;
#[pyclass(extends = ImmutableChildOfImmutableBase)]
struct MutableChildOfImmutableChildOfImmutableBase;
#[pyclass(extends = MutableChildOfImmutableBase, immutable)]
#[pyclass(extends = MutableChildOfImmutableBase, frozen)]
struct ImmutableChildOfMutableChildOfImmutableBase;
#[pyclass(extends = ImmutableChildOfImmutableBase, immutable)]
#[pyclass(extends = ImmutableChildOfImmutableBase, frozen)]
struct ImmutableChildOfImmutableChildOfImmutableBase;
fn assert_mutable<T: PyClass<PyClassMutability = MutableClass>>() {}
fn assert_immutable<T: PyClass<PyClassMutability = ImmutableClass>>() {}
fn assert_mutable<T: PyClass<Frozen = False, PyClassMutability = MutableClass>>() {}
fn assert_immutable<T: PyClass<Frozen = True, PyClassMutability = ImmutableClass>>() {}
fn assert_mutable_with_mutable_ancestor<
T: PyClass<PyClassMutability = ExtendsMutableAncestor<MutableClass>>,
T: PyClass<Frozen = False, PyClassMutability = ExtendsMutableAncestor<MutableClass>>,
>()
// These horrible bounds are necessary for Rust 1.48 but not newer versions
where
@ -63,7 +64,7 @@ where
{
}
fn assert_immutable_with_mutable_ancestor<
T: PyClass<PyClassMutability = ExtendsMutableAncestor<ImmutableClass>>,
T: PyClass<Frozen = True, PyClassMutability = ExtendsMutableAncestor<ImmutableClass>>,
>()
// These horrible bounds are necessary for Rust 1.48 but not newer versions
where

View File

@ -1,3 +1,26 @@
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
5 | #[pyclass(extends=PyDict)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
5 | #[pyclass(extends=PyDict)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
note: required by a bound in `PyRefMut`
--> src/pycell.rs
|
| pub struct PyRefMut<'p, T: PyClass<Frozen = False>> {
| ^^^^^^^^^^^^^^ required by this bound in `PyRefMut`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
@ -11,12 +34,3 @@ note: required by a bound in `ThreadCheckerInherited`
| pub struct ThreadCheckerInherited<T: PyClass + Send, U: PyClassBaseType>(
| ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
--> tests/ui/abi3_nativetype_inheritance.rs:5:1
|
5 | #[pyclass(extends=PyDict)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict`
|
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -1,6 +1,6 @@
use pyo3::prelude::*;
#[pyclass(immutable)]
#[pyclass(frozen)]
pub struct Foo {
#[pyo3(get)]
field: u32,
@ -13,7 +13,7 @@ fn borrow_mut_fails(foo: Py<Foo>, py: Python){
#[pyclass(subclass)]
struct MutableBase;
#[pyclass(immutable, extends = MutableBase)]
#[pyclass(frozen, extends = MutableBase)]
struct ImmutableChild;
fn borrow_mut_of_child_fails(child: Py<ImmutableChild>, py: Python){

View File

@ -0,0 +1,23 @@
error[E0271]: type mismatch resolving `<Foo as PyClass>::Frozen == False`
--> tests/ui/invalid_frozen_pyclass_borrow.rs:10:33
|
10 | let borrow = foo.as_ref(py).borrow_mut();
| ^^^^^^^^^^ expected struct `False`, found struct `True`
|
note: required by a bound in `PyCell::<T>::borrow_mut`
--> src/pycell.rs
|
| T: PyClass<Frozen = False>,
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
error[E0271]: type mismatch resolving `<ImmutableChild as PyClass>::Frozen == False`
--> tests/ui/invalid_frozen_pyclass_borrow.rs:20:35
|
20 | let borrow = child.as_ref(py).borrow_mut();
| ^^^^^^^^^^ expected struct `False`, found struct `True`
|
note: required by a bound in `PyCell::<T>::borrow_mut`
--> src/pycell.rs
|
| T: PyClass<Frozen = False>,
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`

View File

@ -1,25 +0,0 @@
error[E0271]: type mismatch resolving `<Foo as PyClassImpl>::Mutability == Mutable`
--> tests/ui/invalid_immutable_pyclass_borrow.rs:10:33
|
10 | let borrow = foo.as_ref(py).borrow_mut();
| ^^^^^^^^^^ expected struct `Mutable`, found struct `Immutable`
|
= note: required because of the requirements on the impl of `MutablePyClass` for `Foo`
note: required by a bound in `PyCell::<T>::borrow_mut`
--> src/pycell.rs
|
| T: MutablePyClass,
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`
error[E0271]: type mismatch resolving `<ImmutableChild as PyClassImpl>::Mutability == Mutable`
--> tests/ui/invalid_immutable_pyclass_borrow.rs:20:35
|
20 | let borrow = child.as_ref(py).borrow_mut();
| ^^^^^^^^^^ expected struct `Mutable`, found struct `Immutable`
|
= note: required because of the requirements on the impl of `MutablePyClass` for `ImmutableChild`
note: required by a bound in `PyCell::<T>::borrow_mut`
--> src/pycell.rs
|
| T: MutablePyClass,
| ^^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::borrow_mut`

View File

@ -1,4 +1,4 @@
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `immutable`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
--> tests/ui/invalid_pyclass_args.rs:3:11
|
3 | #[pyclass(extend=pyo3::types::PyDict)]
@ -34,7 +34,7 @@ error: expected string literal
18 | #[pyclass(module = my_module)]
| ^^^^^^^^^
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `immutable`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `mapping`, `module`, `name`, `subclass`, `text_signature`, `unsendable`, `weakref`, `gc`
--> tests/ui/invalid_pyclass_args.rs:21:11
|
21 | #[pyclass(weakrev)]