Implement opt-in immutable pyclasses

This commit is contained in:
mejrs 2021-11-22 09:26:34 +01:00 committed by David Hewitt
parent ada301773e
commit 7520b49ac1
19 changed files with 364 additions and 148 deletions

View File

@ -932,36 +932,41 @@ The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplC
# #[cfg(not(feature = "multiple-pymethods"))] { # #[cfg(not(feature = "multiple-pymethods"))] {
# use pyo3::prelude::*; # use pyo3::prelude::*;
// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled. // Note: the implementation differs slightly with the `multiple-pymethods` feature enabled.
/// Class for demonstration
struct MyClass { struct MyClass {
# #[allow(dead_code)] # #[allow(dead_code)]
num: i32, num: i32,
} }
unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
unsafe impl pyo3::PyTypeInfo for MyClass { type AsRefTarget = ::pyo3::PyCell<Self>;
type AsRefTarget = PyCell<Self>;
const NAME: &'static str = "MyClass"; const NAME: &'static str = "MyClass";
const MODULE: Option<&'static str> = None; const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None;
#[inline] #[inline]
fn type_object_raw(py: pyo3::Python) -> *mut pyo3::ffi::PyTypeObject { fn type_object_raw(py: ::pyo3::Python<'_>) -> *mut ::pyo3::ffi::PyTypeObject {
use pyo3::type_object::LazyStaticType; use ::pyo3::type_object::LazyStaticType;
static TYPE_OBJECT: LazyStaticType = LazyStaticType::new(); static TYPE_OBJECT: LazyStaticType = LazyStaticType::new();
TYPE_OBJECT.get_or_init::<Self>(py) TYPE_OBJECT.get_or_init::<Self>(py)
} }
} }
impl pyo3::pyclass::PyClass for MyClass { impl ::pyo3::PyClass for MyClass {
type Dict = pyo3::impl_::pyclass::PyClassDummySlot; type Dict = ::pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot; type WeakRef = ::pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = PyAny; type BaseNativeType = ::pyo3::PyAny;
} }
impl pyo3::IntoPy<PyObject> for MyClass { unsafe impl ::pyo3::pyclass::MutablePyClass for MyClass {}
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py) impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut MyClass {
type Target = ::pyo3::PyRefMut<'a, MyClass>;
}
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a MyClass {
type Target = ::pyo3::PyRef<'a, MyClass>;
}
impl ::pyo3::IntoPy<::pyo3::PyObject> for MyClass {
fn into_py(self, py: ::pyo3::Python) -> ::pyo3::PyObject {
::pyo3::IntoPy::into_py(::pyo3::Py::new(py, self).unwrap(), py)
} }
} }
@ -973,6 +978,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type Layout = PyCell<MyClass>; type Layout = PyCell<MyClass>;
type BaseType = PyAny; type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>; type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>;
type Mutabilty = pyo3::pyclass::Mutable;
fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) { fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) {
use pyo3::impl_::pyclass::*; use pyo3::impl_::pyclass::*;
@ -997,6 +1003,15 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
collector.free_impl() collector.free_impl()
} }
} }
impl ::pyo3::impl_::pyclass::PyClassDescriptors<MyClass>
for ::pyo3::impl_::pyclass::PyClassImplCollector<MyClass>
{
fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] {
static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[];
METHODS
}
}
# Python::with_gil(|py| { # Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>(); # let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'") # pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")

View File

@ -35,6 +35,7 @@ pub struct PyClassArgs {
pub has_unsendable: bool, pub has_unsendable: bool,
pub module: Option<syn::LitStr>, pub module: Option<syn::LitStr>,
pub class_kind: PyClassKind, pub class_kind: PyClassKind,
pub is_immutable: bool,
} }
impl PyClassArgs { impl PyClassArgs {
@ -67,6 +68,7 @@ impl PyClassArgs {
is_basetype: false, is_basetype: false,
has_extends: false, has_extends: false,
has_unsendable: false, has_unsendable: false,
is_immutable: false,
class_kind, class_kind,
} }
} }
@ -176,6 +178,9 @@ impl PyClassArgs {
"unsendable" => { "unsendable" => {
self.has_unsendable = true; self.has_unsendable = true;
} }
"immutable" => {
self.is_immutable = true;
}
_ => bail_spanned!( _ => bail_spanned!(
exp.path.span() => "expected one of gc/weakref/subclass/dict/unsendable" exp.path.span() => "expected one of gc/weakref/subclass/dict/unsendable"
), ),
@ -587,12 +592,29 @@ fn impl_enum_class(
let default_items = let default_items =
gen_default_items(cls, vec![default_repr_impl, default_richcmp, default_int]); gen_default_items(cls, vec![default_repr_impl, default_richcmp, default_int]);
let mutability = if args.is_immutable {
quote! {
unsafe impl _pyo3::pyclass::ImmutablePyClass for #cls {}
}
} else {
quote! {
unsafe impl _pyo3::pyclass::MutablePyClass for #cls {}
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{
type Target = _pyo3::PyRefMut<'a, #cls>;
}
}
};
Ok(quote! { Ok(quote! {
const _: () = { const _: () = {
use #krate as _pyo3; use #krate as _pyo3;
#pytypeinfo #pytypeinfo
#mutability
#pyclass_impls #pyclass_impls
#default_items #default_items
@ -788,20 +810,30 @@ impl<'a> PyClassImplsBuilder<'a> {
type Dict = #dict; type Dict = #dict;
type WeakRef = #weakref; type WeakRef = #weakref;
type BaseNativeType = #base_nativetype; type BaseNativeType = #base_nativetype;
type Mutability = _pyo3::pycell::Immutable;
} }
} }
} }
fn impl_extractext(&self) -> TokenStream { fn impl_extractext(&self) -> TokenStream {
let cls = self.cls; let cls = self.cls;
quote! { if self.attr.is_immutable {
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls quote! {
{ impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls
type Target = _pyo3::PyRef<'a, #cls>; {
type Target = _pyo3::PyRef<'a, #cls>;
}
} }
} else {
quote! {
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls
{
type Target = _pyo3::PyRef<'a, #cls>;
}
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{ {
type Target = _pyo3::PyRefMut<'a, #cls>; type Target = _pyo3::PyRefMut<'a, #cls>;
}
} }
} }
} }
@ -901,8 +933,18 @@ impl<'a> PyClassImplsBuilder<'a> {
let default_items = &self.default_items; let default_items = &self.default_items;
let mutability = if self.attr.is_immutable {
quote! {
_pyo3::pycell::Immutable
}
} else {
quote! {
_pyo3::pycell::Mutable
}
};
quote! { quote! {
impl _pyo3::impl_::pyclass::PyClassImpl for #cls { impl _pyo3::impl_::pyclass::PyClassImpl<#mutability> for #cls {
const DOC: &'static str = #doc; const DOC: &'static str = #doc;
const IS_GC: bool = #is_gc; const IS_GC: bool = #is_gc;
const IS_BASETYPE: bool = #is_basetype; const IS_BASETYPE: bool = #is_basetype;
@ -943,9 +985,9 @@ impl<'a> PyClassImplsBuilder<'a> {
#dict_offset #dict_offset
#weaklist_offset #weaklist_offset
}
#inventory_class #inventory_class
}
} }
} }

View File

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

View File

@ -5,7 +5,7 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) //! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api //! c-api
use crate::callback::IntoPyCallbackOutput; use crate::callback::IntoPyCallbackOutput;
use crate::{ffi, PyCell, PyClass, PyRefMut}; use crate::{ffi, pyclass::MutablePyClass, PyCell, PyRefMut};
use std::os::raw::c_int; use std::os::raw::c_int;
/// Buffer protocol interface /// Buffer protocol interface
@ -13,7 +13,7 @@ use std::os::raw::c_int;
/// For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) /// For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
/// c-api. /// c-api.
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait PyBufferProtocol<'p>: PyClass { pub trait PyBufferProtocol<'p>: MutablePyClass {
// No default implementations so that implementors of this trait provide both methods. // No default implementations so that implementors of this trait provide both methods.
fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result

View File

@ -2,14 +2,14 @@
//! Python GC support //! Python GC support
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python}; use crate::{ffi, pyclass::MutablePyClass, AsPyPointer, PyCell, Python};
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
#[repr(transparent)] #[repr(transparent)]
pub struct PyTraverseError(c_int); pub struct PyTraverseError(c_int);
/// GC support /// GC support
pub trait PyGCProtocol<'p>: PyClass { pub trait PyGCProtocol<'p>: MutablePyClass {
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>; fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self); fn __clear__(&'p mut self);
} }

View File

@ -4,7 +4,7 @@
//! Trait and support implementation for implementing mapping support //! Trait and support implementation for implementing mapping support
use crate::callback::IntoPyCallbackOutput; use crate::callback::IntoPyCallbackOutput;
use crate::{FromPyObject, PyClass, PyObject}; use crate::{pyclass::MutablePyClass, FromPyObject, PyClass, PyObject};
/// Mapping interface /// Mapping interface
#[allow(unused_variables)] #[allow(unused_variables)]
@ -50,13 +50,13 @@ pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>; type Result: IntoPyCallbackOutput<PyObject>;
} }
pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> { pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
type Key: FromPyObject<'p>; type Key: FromPyObject<'p>;
type Value: FromPyObject<'p>; type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>; type Result: IntoPyCallbackOutput<()>;
} }
pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> { pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> + MutablePyClass {
type Key: FromPyObject<'p>; type Key: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>; type Result: IntoPyCallbackOutput<()>;
} }

View File

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

View File

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

View File

@ -5,7 +5,8 @@ use crate::err::{self, PyDowncastError, PyResult};
use crate::type_object::PyTypeInfo; use crate::type_object::PyTypeInfo;
use crate::types::PyTuple; use crate::types::PyTuple;
use crate::{ use crate::{
ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python, ffi, gil, pyclass::MutablePyClass, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef,
PyRefMut, Python,
}; };
use std::ptr::NonNull; use std::ptr::NonNull;
@ -329,7 +330,7 @@ where
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T> impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where where
T: PyClass, T: MutablePyClass,
{ {
fn extract(obj: &'a PyAny) -> PyResult<Self> { fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?; let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;

View File

@ -2,7 +2,7 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError}, exceptions::{PyAttributeError, PyNotImplementedError},
ffi, ffi,
impl_::freelist::FreeList, impl_::freelist::FreeList,
pycell::PyCellLayout, pycell::{Mutability, PyCellLayout},
pyclass_init::PyObjectInit, pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject}, type_object::{PyLayout, PyTypeObject},
PyCell, PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python, PyCell, PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
@ -145,7 +145,7 @@ unsafe impl Sync for PyClassItems {}
/// ///
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail /// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
/// and may be changed at any time. /// and may be changed at any time.
pub trait PyClassImpl: Sized { pub trait PyClassImpl<M: Mutability>: Sized {
/// Class doc string /// Class doc string
const DOC: &'static str = "\0"; const DOC: &'static str = "\0";
@ -162,7 +162,7 @@ pub trait PyClassImpl: Sized {
type Layout: PyLayout<Self>; type Layout: PyLayout<Self>;
/// Base class /// Base class
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType; type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType<M>;
/// This handles following two situations: /// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing. /// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.
@ -818,9 +818,14 @@ impl<T> PyClassThreadChecker<T> for ThreadCheckerImpl<T> {
/// Thread checker for types that have `Send` and `extends=...`. /// Thread checker for types that have `Send` and `extends=...`.
/// Ensures that `T: Send` and the parent is not accessed by another thread. /// Ensures that `T: Send` and the parent is not accessed by another thread.
#[doc(hidden)] #[doc(hidden)]
pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType>(PhantomData<T>, U::ThreadChecker); pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType<M>, M: Mutability>(
PhantomData<T>,
U::ThreadChecker,
);
impl<T: Send, U: PyClassBaseType> PyClassThreadChecker<T> for ThreadCheckerInherited<T, U> { impl<T: Send, U: PyClassBaseType<M>, M: Mutability> PyClassThreadChecker<T>
for ThreadCheckerInherited<T, U, M>
{
fn ensure(&self) { fn ensure(&self) {
self.1.ensure(); self.1.ensure();
} }
@ -831,15 +836,15 @@ impl<T: Send, U: PyClassBaseType> PyClassThreadChecker<T> for ThreadCheckerInher
} }
/// Trait denoting that this class is suitable to be used as a base type for PyClass. /// Trait denoting that this class is suitable to be used as a base type for PyClass.
pub trait PyClassBaseType: Sized { pub trait PyClassBaseType<M: Mutability>: Sized {
type LayoutAsBase: PyCellLayout<Self>; type LayoutAsBase: PyCellLayout<Self, M>;
type BaseNativeType; type BaseNativeType;
type ThreadChecker: PyClassThreadChecker<Self>; type ThreadChecker: PyClassThreadChecker<Self>;
type Initializer: PyObjectInit<Self>; type Initializer: PyObjectInit<Self>;
} }
/// All PyClasses can be used as a base type. /// All PyClasses can be used as a base type.
impl<T: PyClass> PyClassBaseType for T { impl<T: PyClass> PyClassBaseType<T::Mutability> for T {
type LayoutAsBase = crate::pycell::PyCell<T>; type LayoutAsBase = crate::pycell::PyCell<T>;
type BaseNativeType = T::BaseNativeType; type BaseNativeType = T::BaseNativeType;
type ThreadChecker = T::ThreadChecker; type ThreadChecker = T::ThreadChecker;

View File

@ -5,8 +5,8 @@ use crate::gil;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell}; use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::types::{PyDict, PyTuple}; use crate::types::{PyDict, PyTuple};
use crate::{ use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer, ffi, pyclass::MutablePyClass, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass,
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
}; };
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
@ -427,7 +427,10 @@ where
/// # Panics /// # Panics
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut). /// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> { pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T>
where
T: MutablePyClass,
{
self.as_ref(py).borrow_mut() self.as_ref(py).borrow_mut()
} }
@ -454,7 +457,10 @@ where
pub fn try_borrow_mut<'py>( pub fn try_borrow_mut<'py>(
&'py self, &'py self,
py: Python<'py>, py: Python<'py>,
) -> Result<PyRefMut<'py, T>, PyBorrowMutError> { ) -> Result<PyRefMut<'py, T>, PyBorrowMutError>
where
T: MutablePyClass,
{
self.as_ref(py).try_borrow_mut() self.as_ref(py).try_borrow_mut()
} }
} }
@ -819,7 +825,7 @@ where
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T> impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
where where
T: PyClass, T: MutablePyClass,
{ {
fn from(pyref: PyRefMut<'a, T>) -> Self { fn from(pyref: PyRefMut<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) }

View File

@ -176,7 +176,7 @@
use crate::exceptions::PyRuntimeError; use crate::exceptions::PyRuntimeError;
use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef}; use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
use crate::pyclass::PyClass; use crate::pyclass::{MutablePyClass, PyClass};
use crate::pyclass_init::PyClassInitializer; use crate::pyclass_init::PyClassInitializer;
use crate::type_object::{PyLayout, PySizedLayout}; use crate::type_object::{PyLayout, PySizedLayout};
use crate::types::PyAny; use crate::types::PyAny;
@ -189,19 +189,117 @@ use crate::{
use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python}; use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python};
use std::cell::{Cell, UnsafeCell}; use std::cell::{Cell, UnsafeCell};
use std::fmt; use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub trait Mutability {
/// Creates a new borrow checker
fn new() -> Self;
/// Increments immutable borrow count, if possible
fn try_borrow(&self) -> Result<(), PyBorrowError>;
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError>;
/// Decrements immutable borrow count
fn release_borrow(&self);
/// Increments mutable borrow count, if possible
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError>;
/// Decremements mutable borrow count
fn release_borrow_mut(&self);
}
pub struct Mutable {
flag: Cell<BorrowFlag>,
}
impl Mutability for Mutable {
fn new() -> Self {
Self {
flag: Cell::new(BorrowFlag::UNUSED),
}
}
fn try_borrow(&self) -> Result<(), PyBorrowError> {
let flag = self.flag.get();
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
self.flag.set(flag.increment());
Ok(())
} else {
Err(PyBorrowError { _private: () })
}
}
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
let flag = self.flag.get();
if flag != BorrowFlag::HAS_MUTABLE_BORROW {
Ok(())
} else {
Err(PyBorrowError { _private: () })
}
}
fn release_borrow(&self) {
let flag = self.flag.get();
self.flag.set(flag.decrement())
}
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError> {
let flag = self.flag.get();
if flag == BorrowFlag::UNUSED {
self.flag.set(BorrowFlag::HAS_MUTABLE_BORROW);
Ok(())
} else {
Err(PyBorrowMutError { _private: () })
}
}
fn release_borrow_mut(&self) {
self.flag.set(BorrowFlag::UNUSED)
}
}
pub struct Immutable {
flag: PhantomData<Cell<BorrowFlag>>,
}
impl Mutability for Immutable {
fn new() -> Self {
Self { flag: PhantomData }
}
fn try_borrow(&self) -> Result<(), PyBorrowError> {
Ok(())
}
fn try_borrow_unguarded(&self) -> Result<(), PyBorrowError> {
Ok(())
}
fn release_borrow(&self) {}
fn try_borrow_mut(&self) -> Result<(), PyBorrowMutError> {
unreachable!()
}
fn release_borrow_mut(&self) {
unreachable!()
}
}
/// Base layout of PyCell. /// Base layout of PyCell.
/// This is necessary for sharing BorrowFlag between parents and children. /// This is necessary for sharing BorrowFlag between parents and children.
#[doc(hidden)] #[doc(hidden)]
#[repr(C)] #[repr(C)]
pub struct PyCellBase<T> { pub struct PyCellBase<T, M: Mutability> {
ob_base: T, ob_base: T,
borrow_flag: Cell<BorrowFlag>, borrow_impl: M,
} }
unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {} unsafe impl<T, U, M> PyLayout<T> for PyCellBase<U, M>
where
U: PySizedLayout<T>,
M: Mutability,
{
}
/// A container type for (mutably) accessing [`PyClass`] values /// A container type for (mutably) accessing [`PyClass`] values
/// ///
@ -240,7 +338,7 @@ unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {}
/// [module-level documentation](self). /// [module-level documentation](self).
#[repr(C)] #[repr(C)]
pub struct PyCell<T: PyClass> { pub struct PyCell<T: PyClass> {
ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase, ob_base: <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase,
contents: PyCellContents<T>, contents: PyCellContents<T>,
} }
@ -283,7 +381,10 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// Panics if the value is currently 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). /// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T> { pub fn borrow_mut(&self) -> PyRefMut<'_, T>
where
T: MutablePyClass,
{
self.try_borrow_mut().expect("Already borrowed") self.try_borrow_mut().expect("Already borrowed")
} }
@ -313,13 +414,9 @@ impl<T: PyClass> PyCell<T> {
/// }); /// });
/// ``` /// ```
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> { pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
let flag = self.get_borrow_flag(); self.borrow_checker()
if flag == BorrowFlag::HAS_MUTABLE_BORROW { .try_borrow()
Err(PyBorrowError { _private: () }) .map(|_| PyRef { inner: self })
} else {
self.set_borrow_flag(flag.increment());
Ok(PyRef { inner: self })
}
} }
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed. /// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
@ -343,13 +440,13 @@ impl<T: PyClass> PyCell<T> {
/// assert!(c.try_borrow_mut().is_ok()); /// assert!(c.try_borrow_mut().is_ok());
/// }); /// });
/// ``` /// ```
pub fn try_borrow_mut(&self) -> Result<PyRefMut<'_, T>, PyBorrowMutError> { pub fn try_borrow_mut(&self) -> Result<PyRefMut<'_, T>, PyBorrowMutError>
if self.get_borrow_flag() != BorrowFlag::UNUSED { where
Err(PyBorrowMutError { _private: () }) T: MutablePyClass,
} else { {
self.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW); self.borrow_checker()
Ok(PyRefMut { inner: self }) .try_borrow_mut()
} .map(|_| PyRefMut { inner: self })
} }
/// Immutably borrows the value `T`, returning an error if the value is /// Immutably borrows the value `T`, returning an error if the value is
@ -382,11 +479,9 @@ impl<T: PyClass> PyCell<T> {
/// }); /// });
/// ``` /// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if self.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW { self.borrow_checker()
Err(PyBorrowError { _private: () }) .try_borrow_unguarded()
} else { .map(|_: ()| &*self.contents.value.get())
Ok(&*self.contents.value.get())
}
} }
/// Replaces the wrapped value with a new one, returning the old value. /// Replaces the wrapped value with a new one, returning the old value.
@ -395,7 +490,10 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// Panics if the value is currently borrowed. /// Panics if the value is currently borrowed.
#[inline] #[inline]
pub fn replace(&self, t: T) -> T { pub fn replace(&self, t: T) -> T
where
T: MutablePyClass,
{
std::mem::replace(&mut *self.borrow_mut(), t) std::mem::replace(&mut *self.borrow_mut(), t)
} }
@ -404,7 +502,10 @@ impl<T: PyClass> PyCell<T> {
/// # Panics /// # Panics
/// ///
/// Panics if the value is currently borrowed. /// Panics if the value is currently borrowed.
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T { pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T
where
T: MutablePyClass,
{
let mut_borrow = &mut *self.borrow_mut(); let mut_borrow = &mut *self.borrow_mut();
let replacement = f(mut_borrow); let replacement = f(mut_borrow);
std::mem::replace(mut_borrow, replacement) std::mem::replace(mut_borrow, replacement)
@ -416,7 +517,10 @@ impl<T: PyClass> PyCell<T> {
/// ///
/// Panics if the value in either `PyCell` is currently borrowed. /// Panics if the value in either `PyCell` is currently borrowed.
#[inline] #[inline]
pub fn swap(&self, other: &Self) { pub fn swap(&self, other: &Self)
where
T: MutablePyClass,
{
std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
} }
@ -674,8 +778,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> {
impl<'p, T: PyClass> Drop for PyRef<'p, T> { impl<'p, T: PyClass> Drop for PyRef<'p, T> {
fn drop(&mut self) { fn drop(&mut self) {
let flag = self.inner.get_borrow_flag(); self.inner.borrow_checker().release_borrow()
self.inner.set_borrow_flag(flag.decrement())
} }
} }
@ -707,11 +810,11 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
/// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`. /// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`.
/// ///
/// See the [module-level documentation](self) for more information. /// See the [module-level documentation](self) for more information.
pub struct PyRefMut<'p, T: PyClass> { pub struct PyRefMut<'p, T: MutablePyClass> {
inner: &'p PyCell<T>, inner: &'p PyCell<T>,
} }
impl<'p, T: PyClass> PyRefMut<'p, T> { impl<'p, T: MutablePyClass> PyRefMut<'p, T> {
/// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`.
pub fn py(&self) -> Python { pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() } unsafe { Python::assume_gil_acquired() }
@ -720,8 +823,8 @@ impl<'p, T: PyClass> PyRefMut<'p, T> {
impl<'p, T, U> AsRef<U> for PyRefMut<'p, T> impl<'p, T, U> AsRef<U> for PyRefMut<'p, T>
where where
T: PyClass<BaseType = U>, T: PyClass<BaseType = U> + MutablePyClass,
U: PyClass, U: MutablePyClass,
{ {
fn as_ref(&self) -> &T::BaseType { fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() } unsafe { &*self.inner.ob_base.get_ptr() }
@ -730,8 +833,8 @@ where
impl<'p, T, U> AsMut<U> for PyRefMut<'p, T> impl<'p, T, U> AsMut<U> for PyRefMut<'p, T>
where where
T: PyClass<BaseType = U>, T: PyClass<BaseType = U> + MutablePyClass,
U: PyClass, U: MutablePyClass,
{ {
fn as_mut(&mut self) -> &mut T::BaseType { fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() } unsafe { &mut *self.inner.ob_base.get_ptr() }
@ -740,8 +843,8 @@ where
impl<'p, T, U> PyRefMut<'p, T> impl<'p, T, U> PyRefMut<'p, T>
where where
T: PyClass<BaseType = U>, T: PyClass<BaseType = U> + MutablePyClass,
U: PyClass, U: MutablePyClass,
{ {
/// Gets a `PyRef<T::BaseType>`. /// Gets a `PyRef<T::BaseType>`.
/// ///
@ -755,7 +858,7 @@ where
} }
} }
impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> {
type Target = T; type Target = T;
#[inline] #[inline]
@ -764,46 +867,46 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> {
} }
} }
impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.get_ptr() } unsafe { &mut *self.inner.get_ptr() }
} }
} }
impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> {
fn drop(&mut self) { fn drop(&mut self) {
self.inner.set_borrow_flag(BorrowFlag::UNUSED) self.inner.borrow_checker().release_borrow_mut()
} }
} }
impl<T: PyClass> IntoPy<PyObject> for PyRefMut<'_, T> { impl<T: MutablePyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
fn into_py(self, py: Python) -> PyObject { fn into_py(self, py: Python) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) }
} }
} }
impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> { impl<'a, T: MutablePyClass> AsPyPointer for PyRefMut<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject { fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr() self.inner.as_ptr()
} }
} }
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> { impl<'a, T: MutablePyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> {
type Error = PyBorrowMutError; type Error = PyBorrowMutError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> { fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
cell.try_borrow_mut() cell.try_borrow_mut()
} }
} }
impl<T: PyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { impl<T: MutablePyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*(self.deref()), f) fmt::Debug::fmt(&*(self.deref()), f)
} }
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct BorrowFlag(usize); pub struct BorrowFlag(usize);
impl BorrowFlag { impl BorrowFlag {
@ -868,9 +971,11 @@ impl From<PyBorrowMutError> for PyErr {
} }
#[doc(hidden)] #[doc(hidden)]
pub trait PyCellLayout<T>: PyLayout<T> { pub trait PyCellLayout<T, M>: PyLayout<T>
fn get_borrow_flag(&self) -> BorrowFlag; where
fn set_borrow_flag(&self, flag: BorrowFlag); M: Mutability,
{
fn borrow_checker(&self) -> &M;
/// Implementation of tp_dealloc. /// Implementation of tp_dealloc.
/// # Safety /// # Safety
/// - slf must be a valid pointer to an instance of a T or a subclass. /// - slf must be a valid pointer to an instance of a T or a subclass.
@ -878,17 +983,16 @@ pub trait PyCellLayout<T>: PyLayout<T> {
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python); unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python);
} }
impl<T, U> PyCellLayout<T> for PyCellBase<U> impl<T, U, M> PyCellLayout<T, M> for PyCellBase<U, M>
where where
U: PySizedLayout<T>, U: PySizedLayout<T>,
T: PyTypeInfo, T: PyTypeInfo,
M: Mutability,
{ {
fn get_borrow_flag(&self) -> BorrowFlag { fn borrow_checker(&self) -> &M {
self.borrow_flag.get() &self.borrow_impl
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.borrow_flag.set(flag)
} }
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) { unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// For `#[pyclass]` types which inherit from PyAny, we can just call tp_free // For `#[pyclass]` types which inherit from PyAny, we can just call tp_free
if T::type_object_raw(py) == &mut PyBaseObject_Type { if T::type_object_raw(py) == &mut PyBaseObject_Type {
@ -910,16 +1014,14 @@ where
} }
} }
impl<T: PyClass> PyCellLayout<T> for PyCell<T> impl<T: PyClass> PyCellLayout<T, T::Mutability> for PyCell<T>
where where
<T::BaseType as PyClassBaseType>::LayoutAsBase: PyCellLayout<T::BaseType>, <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase:
PyCellLayout<T::BaseType, T::Mutability>,
{ {
fn get_borrow_flag(&self) -> BorrowFlag { fn borrow_checker(&self) -> &T::Mutability {
self.contents.thread_checker.ensure(); self.contents.thread_checker.ensure();
self.ob_base.get_borrow_flag() self.ob_base.borrow_checker()
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.ob_base.set_borrow_flag(flag)
} }
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) { unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// Safety: Python only calls tp_dealloc when no references to the object remain. // Safety: Python only calls tp_dealloc when no references to the object remain.
@ -927,6 +1029,6 @@ where
ManuallyDrop::drop(&mut cell.contents.value); ManuallyDrop::drop(&mut cell.contents.value);
cell.contents.dict.clear_dict(py); cell.contents.dict.clear_dict(py);
cell.contents.weakref.clear_weakrefs(slf, py); cell.contents.weakref.clear_weakrefs(slf, py);
<T::BaseType as PyClassBaseType>::LayoutAsBase::tp_dealloc(slf, py) <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase::tp_dealloc(slf, py)
} }
} }

View File

@ -1,4 +1,5 @@
//! `PyClass` and related traits. //! `PyClass` and related traits.
use crate::pycell::Mutability;
use crate::{ use crate::{
callback::IntoPyCallbackOutput, callback::IntoPyCallbackOutput,
ffi, ffi,
@ -22,7 +23,7 @@ use std::{
/// The `#[pyclass]` attribute automatically implements this trait for your Rust struct, /// The `#[pyclass]` attribute automatically implements this trait for your Rust struct,
/// so you normally don't have to use this trait directly. /// so you normally don't have to use this trait directly.
pub trait PyClass: pub trait PyClass:
PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Layout = PyCell<Self>> PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl<Self::Mutability, Layout = PyCell<Self>>
{ {
/// Specify this class has `#[pyclass(dict)]` or not. /// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict; type Dict: PyClassDict;
@ -31,8 +32,13 @@ pub trait PyClass:
/// The closest native ancestor. This is `PyAny` by default, and when you declare /// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`. /// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType; type BaseNativeType: PyTypeInfo + PyNativeType;
type Mutability: Mutability;
} }
pub unsafe trait MutablePyClass: PyClass {}
pub unsafe trait ImmutablePyClass: PyClass {}
fn into_raw<T>(vec: Vec<T>) -> *mut c_void { fn into_raw<T>(vec: Vec<T>) -> *mut c_void {
Box::into_raw(vec.into_boxed_slice()) as _ Box::into_raw(vec.into_boxed_slice()) as _
} }

View File

@ -130,14 +130,17 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
/// ``` /// ```
pub struct PyClassInitializer<T: PyClass> { pub struct PyClassInitializer<T: PyClass> {
init: T, init: T,
super_init: <T::BaseType as PyClassBaseType>::Initializer, super_init: <T::BaseType as PyClassBaseType<T::Mutability>>::Initializer,
} }
impl<T: PyClass> PyClassInitializer<T> { impl<T: PyClass> PyClassInitializer<T> {
/// Constructs a new initializer from value `T` and base class' initializer. /// Constructs a new initializer from value `T` and base class' initializer.
/// ///
/// It is recommended to use `add_subclass` instead of this method for most usage. /// It is recommended to use `add_subclass` instead of this method for most usage.
pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self { pub fn new(
init: T,
super_init: <T::BaseType as PyClassBaseType<T::Mutability>>::Initializer,
) -> Self {
Self { init, super_init } Self { init, super_init }
} }
@ -190,7 +193,7 @@ impl<T: PyClass> PyClassInitializer<T> {
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S> pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where where
S: PyClass<BaseType = T>, S: PyClass<BaseType = T>,
S::BaseType: PyClassBaseType<Initializer = Self>, S::BaseType: PyClassBaseType<T::Mutability, Initializer = Self>,
{ {
PyClassInitializer::new(subclass_value, self) PyClassInitializer::new(subclass_value, self)
} }
@ -231,16 +234,16 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
/// Layout of a PyCellBase after base new has been called, but the borrow flag has not /// Layout of a PyCellBase after base new has been called, but the borrow flag has not
/// yet been initialized. /// yet been initialized.
#[repr(C)] #[repr(C)]
struct PartiallyInitializedPyCellBase<T> { struct PartiallyInitializedPyCellBase<T: PyClass> {
_ob_base: T, _ob_base: T,
borrow_flag: MaybeUninit<Cell<BorrowFlag>>, borrow_flag: MaybeUninit<T::Mutability>,
} }
/// Layout of a PyCell after base new has been called, but the contents have not yet been /// Layout of a PyCell after base new has been called, but the contents have not yet been
/// written. /// written.
#[repr(C)] #[repr(C)]
struct PartiallyInitializedPyCell<T: PyClass> { struct PartiallyInitializedPyCell<T: PyClass> {
_ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase, _ob_base: <T::BaseType as PyClassBaseType<T::Mutability>>::LayoutAsBase,
contents: MaybeUninit<PyCellContents<T>>, contents: MaybeUninit<PyCellContents<T>>,
} }
@ -274,7 +277,7 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
impl<T> From<T> for PyClassInitializer<T> impl<T> From<T> for PyClassInitializer<T>
where where
T: PyClass, T: PyClass,
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>, T::BaseType: PyClassBaseType<T::Mutability, Initializer = PyNativeTypeInitializer<T::BaseType>>,
{ {
#[inline] #[inline]
fn from(value: T) -> PyClassInitializer<T> { fn from(value: T) -> PyClassInitializer<T> {

View File

@ -196,8 +196,8 @@ macro_rules! pyobject_native_type_sized {
($name:ty, $layout:path $(;$generics:ident)*) => { ($name:ty, $layout:path $(;$generics:ident)*) => {
unsafe impl $crate::type_object::PyLayout<$name> for $layout {} unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
impl $crate::type_object::PySizedLayout<$name> for $layout {} impl $crate::type_object::PySizedLayout<$name> for $layout {}
impl<'a, $($generics,)*> $crate::impl_::pyclass::PyClassBaseType for $name { impl<'a, $($generics,)*> $crate::impl_::pyclass::PyClassBaseType<$crate::pycell::Mutable> for $name {
type LayoutAsBase = $crate::pycell::PyCellBase<$layout>; type LayoutAsBase = $crate::pycell::PyCellBase<$layout, $crate::pycell::Mutable>;
type BaseNativeType = $name; type BaseNativeType = $name;
type ThreadChecker = $crate::impl_::pyclass::ThreadCheckerStub<$crate::PyObject>; type ThreadChecker = $crate::impl_::pyclass::ThreadCheckerStub<$crate::PyObject>;
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;

View File

@ -28,8 +28,12 @@ fn _test_compile_errors() {
t.compile_fail("tests/ui/invalid_pymethod_names.rs"); t.compile_fail("tests/ui/invalid_pymethod_names.rs");
t.compile_fail("tests/ui/invalid_pymodule_args.rs"); t.compile_fail("tests/ui/invalid_pymodule_args.rs");
t.compile_fail("tests/ui/reject_generics.rs"); t.compile_fail("tests/ui/reject_generics.rs");
<<<<<<< HEAD
t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs"); t.compile_fail("tests/ui/invalid_pymethod_proto_args.rs");
t.compile_fail("tests/ui/invalid_pymethod_proto_args_py.rs"); t.compile_fail("tests/ui/invalid_pymethod_proto_args_py.rs");
=======
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
>>>>>>> 0fa03a67cd (Implement opt-in immutable pyclasses)
tests_rust_1_49(&t); tests_rust_1_49(&t);
tests_rust_1_56(&t); tests_rust_1_56(&t);

View File

@ -0,0 +1,13 @@
use pyo3::prelude::*;
#[pyclass(immutable)]
pub struct Foo {
#[pyo3(get)]
field: u32,
}
fn borrow_mut_fails(foo: Py<Foo>, py: Python){
let borrow = foo.as_ref(py).borrow_mut();
}
fn main(){}

View File

@ -0,0 +1,5 @@
error[E0277]: the trait bound `Foo: MutablePyClass` is not satisfied
--> tests/ui/invalid_immutable_pyclass_borrow.rs:10:33
|
10 | let borrow = foo.as_ref(py).borrow_mut();
| ^^^^^^^^^^ the trait `MutablePyClass` is not implemented for `Foo`

View File

@ -11,8 +11,20 @@ note: required because it appears within the type `NotThreadSafe`
5 | struct NotThreadSafe { 5 | struct NotThreadSafe {
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
note: required by a bound in `ThreadCheckerStub` note: required by a bound in `ThreadCheckerStub`
<<<<<<< HEAD
<<<<<<< HEAD
--> src/impl_/pyclass.rs --> src/impl_/pyclass.rs
| |
| pub struct ThreadCheckerStub<T: Send>(PhantomData<T>); | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
=======
--> src/class/impl_.rs:731:33
|
731 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
>>>>>>> 0fa03a67cd (Implement opt-in immutable pyclasses)
=======
--> src/class/impl_.rs:728:33
|
728 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
>>>>>>> 7cded1178d (Fix formatting.)
| ^^^^ required by this bound in `ThreadCheckerStub` | ^^^^ required by this bound in `ThreadCheckerStub`
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)