Implement opt-in immutable pyclasses

This commit is contained in:
mejrs 2021-11-22 09:26:34 +01:00
parent a28be4d9bf
commit 0fa03a67cd
16 changed files with 220 additions and 74 deletions

View File

@ -27,6 +27,7 @@ pub struct PyClassArgs {
pub has_extends: bool,
pub has_unsendable: bool,
pub module: Option<syn::LitStr>,
pub is_immutable: bool,
}
impl Parse for PyClassArgs {
@ -54,6 +55,7 @@ impl Default for PyClassArgs {
is_basetype: false,
has_extends: false,
has_unsendable: false,
is_immutable: false,
}
}
}
@ -158,6 +160,9 @@ impl PyClassArgs {
"unsendable" => {
self.has_unsendable = true;
}
"immutable" => {
self.is_immutable = true;
}
_ => bail_spanned!(
exp.path.span() => "expected one of gc/weakref/subclass/dict/unsendable"
),
@ -515,6 +520,41 @@ fn impl_class(
let is_basetype = attr.is_basetype;
let is_subclass = attr.has_extends;
let mutability = if attr.is_immutable {
quote! {
unsafe impl ::pyo3::pyclass::ImmutablePyClass for #cls {}
unsafe impl ::pyo3::class::impl_::BorrowImpl for #cls {
fn get_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell<Self>) -> ::pyo3::pycell::BorrowFlag
where Self: ::pyo3::PyClass
{
::pyo3::pycell::impl_::get_borrow_flag_dummy
}
fn increment_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell<Self>, ::pyo3::pycell::BorrowFlag)
where Self: ::pyo3::PyClass
{
::pyo3::pycell::impl_::increment_borrow_flag_dummy
}
fn decrement_borrow_flag() -> for<'r> fn(&'r ::pyo3::pycell::PyCell<Self>, ::pyo3::pycell::BorrowFlag)
where Self: ::pyo3::PyClass
{
::pyo3::pycell::impl_::decrement_borrow_flag_dummy
}
}
}
} else {
quote! {
unsafe impl ::pyo3::pyclass::MutablePyClass for #cls {}
unsafe impl ::pyo3::class::impl_::BorrowImpl for #cls {}
impl<'a> ::pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{
type Target = ::pyo3::PyRefMut<'a, #cls>;
}
}
};
Ok(quote! {
unsafe impl ::pyo3::type_object::PyTypeInfo for #cls {
type AsRefTarget = ::pyo3::PyCell<Self>;
@ -538,15 +578,14 @@ fn impl_class(
type BaseNativeType = #base_nativetype;
}
#mutability
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
{
type Target = ::pyo3::PyRefMut<'a, #cls>;
}
#into_pyobject

View File

@ -9,7 +9,9 @@
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
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;
/// Operators for the `__richcmp__` method
@ -55,14 +57,14 @@ pub trait PyObjectProtocol<'p>: PyClass {
fn __setattr__(&'p mut self, name: Self::Name, value: Self::Value) -> Self::Result
where
Self: PyObjectSetAttrProtocol<'p>,
Self: PyObjectSetAttrProtocol<'p> + MutablePyClass,
{
unimplemented!()
}
fn __delattr__(&'p mut self, name: Self::Name) -> Self::Result
where
Self: PyObjectDelAttrProtocol<'p>,
Self: PyObjectDelAttrProtocol<'p> + MutablePyClass,
{
unimplemented!()
}
@ -128,12 +130,12 @@ pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> {
pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
type Name: FromPyObject<'p>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> {
pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> + MutablePyClass {
type Name: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}

View File

@ -5,7 +5,7 @@
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use crate::callback::IntoPyCallbackOutput;
use crate::{ffi, PyCell, PyClass, PyRefMut};
use crate::{ffi, pyclass::MutablePyClass, PyCell, PyRefMut};
use std::os::raw::c_int;
/// 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)
/// c-api.
#[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.
fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result

View File

@ -2,7 +2,7 @@
//! 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};
#[repr(transparent)]
@ -10,7 +10,7 @@ pub struct PyTraverseError(c_int);
/// GC support
#[allow(clippy::upper_case_acronyms)]
pub trait PyGCProtocol<'p>: PyClass {
pub trait PyGCProtocol<'p>: MutablePyClass {
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self);
}

View File

@ -4,7 +4,7 @@ use crate::{
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::PyCellLayout,
pycell::{self, PyCellLayout},
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
@ -39,7 +39,7 @@ impl<T> Copy for PyClassImplCollector<T> {}
///
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
/// and may be changed at any time.
pub trait PyClassImpl: Sized {
pub trait PyClassImpl: Sized + BorrowImpl {
/// Class doc string
const DOC: &'static str = "\0";
@ -83,6 +83,27 @@ pub trait PyClassImpl: Sized {
}
}
pub unsafe trait BorrowImpl {
fn get_borrow_flag() -> for<'r> fn(&'r pycell::PyCell<Self>) -> pycell::BorrowFlag
where
Self: PyClass,
{
pycell::impl_::get_borrow_flag
}
fn increment_borrow_flag() -> for<'r> fn(&'r pycell::PyCell<Self>, pycell::BorrowFlag)
where
Self: PyClass,
{
pycell::impl_::increment_borrow_flag
}
fn decrement_borrow_flag() -> for<'r> fn(&'r pycell::PyCell<Self>, pycell::BorrowFlag)
where
Self: PyClass,
{
pycell::impl_::decrement_borrow_flag
}
}
// Traits describing known special methods.
pub trait PyClassNewImpl<T> {

View File

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

View File

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

View File

@ -6,7 +6,7 @@
use crate::callback::IntoPyCallbackOutput;
use crate::conversion::{FromPyObject, IntoPy};
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;
/// Sequence interface
@ -88,13 +88,13 @@ pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> {
pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
type Index: FromPyObject<'p> + From<isize>;
type Value: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<()>;
}
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> {
pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> + MutablePyClass {
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<()>;
}
@ -115,14 +115,14 @@ pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> {
}
pub trait PySequenceInplaceConcatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + 'p
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass
{
type Other: FromPyObject<'p>;
type Result: IntoPyCallbackOutput<Self>;
}
pub trait PySequenceInplaceRepeatProtocol<'p>:
PySequenceProtocol<'p> + IntoPy<PyObject> + 'p
PySequenceProtocol<'p> + IntoPy<PyObject> + MutablePyClass + 'p
{
type Index: FromPyObject<'p> + From<isize>;
type Result: IntoPyCallbackOutput<Self>;

View File

@ -5,7 +5,8 @@ use crate::err::{self, PyDowncastError, PyResult};
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
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;
@ -329,7 +330,7 @@ where
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where
T: PyClass,
T: MutablePyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;

View File

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

View File

@ -175,7 +175,7 @@
//! [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;
use crate::pyclass::{ImmutablePyClass, MutablePyClass, PyClass};
use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{PyLayout, PySizedLayout};
@ -362,7 +362,10 @@ impl<T: PyClass> PyCell<T> {
///
/// 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> {
pub fn borrow_mut(&self) -> PyRefMut<'_, T>
where
T: MutablePyClass,
{
self.try_borrow_mut().expect("Already borrowed")
}
@ -392,11 +395,11 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
let flag = self.get_borrow_flag();
let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self);
if flag == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
self.set_borrow_flag(flag.increment());
crate::class::impl_::BorrowImpl::increment_borrow_flag()(self, flag);
Ok(PyRef { inner: self })
}
}
@ -422,7 +425,10 @@ impl<T: PyClass> PyCell<T> {
/// 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>
where
T: MutablePyClass,
{
if self.get_borrow_flag() != BorrowFlag::UNUSED {
Err(PyBorrowMutError { _private: () })
} else {
@ -461,7 +467,9 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if self.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW {
if crate::class::impl_::BorrowImpl::get_borrow_flag()(self)
== BorrowFlag::HAS_MUTABLE_BORROW
{
Err(PyBorrowError { _private: () })
} else {
Ok(&*self.contents.value.get())
@ -474,7 +482,10 @@ impl<T: PyClass> PyCell<T> {
///
/// Panics if the value is currently borrowed.
#[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)
}
@ -483,7 +494,10 @@ impl<T: PyClass> PyCell<T> {
/// # Panics
///
/// 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 replacement = f(mut_borrow);
std::mem::replace(mut_borrow, replacement)
@ -495,7 +509,10 @@ impl<T: PyClass> PyCell<T> {
///
/// Panics if the value in either `PyCell` is currently borrowed.
#[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())
}
@ -504,6 +521,42 @@ impl<T: PyClass> PyCell<T> {
}
}
#[doc(hidden)]
pub mod impl_ {
use super::*;
#[inline]
pub fn get_borrow_flag<T: PyClass>(slf: &PyCell<T>) -> BorrowFlag {
PyCellLayout::get_borrow_flag(slf)
}
#[inline]
pub fn get_borrow_flag_dummy<T: ImmutablePyClass>(_slf: &PyCell<T>) -> BorrowFlag {
debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED);
BorrowFlag::UNUSED
}
#[inline]
pub fn increment_borrow_flag<T: PyClass>(slf: &PyCell<T>, flag: BorrowFlag) {
PyCellLayout::set_borrow_flag(slf, flag.increment());
}
#[inline]
pub fn increment_borrow_flag_dummy<T: ImmutablePyClass>(_slf: &PyCell<T>, _flag: BorrowFlag) {
debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED);
}
#[inline]
pub fn decrement_borrow_flag<T: PyClass>(slf: &PyCell<T>, flag: BorrowFlag) {
PyCellLayout::set_borrow_flag(slf, flag.decrement());
}
#[inline]
pub fn decrement_borrow_flag_dummy<T: ImmutablePyClass>(_slf: &PyCell<T>, _flag: BorrowFlag) {
debug_assert_eq!(PyCellLayout::get_borrow_flag(_slf), BorrowFlag::UNUSED);
}
}
unsafe impl<T: PyClass> PyLayout<T> for PyCell<T> {}
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
@ -687,8 +740,8 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> {
impl<'p, T: PyClass> Drop for PyRef<'p, T> {
fn drop(&mut self) {
let flag = self.inner.get_borrow_flag();
self.inner.set_borrow_flag(flag.decrement())
let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self.inner);
crate::class::impl_::BorrowImpl::decrement_borrow_flag()(self.inner, flag);
}
}
@ -720,11 +773,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: PyClass> {
pub struct PyRefMut<'p, T: MutablePyClass> {
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`.
pub fn py(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
@ -733,8 +786,8 @@ impl<'p, T: PyClass> PyRefMut<'p, T> {
impl<'p, T, U> AsRef<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
@ -743,8 +796,8 @@ where
impl<'p, T, U> AsMut<U> for PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() }
@ -753,8 +806,8 @@ where
impl<'p, T, U> PyRefMut<'p, T>
where
T: PyClass<BaseType = U>,
U: PyClass,
T: PyClass<BaseType = U> + MutablePyClass,
U: MutablePyClass,
{
/// Gets a `PyRef<T::BaseType>`.
///
@ -768,7 +821,7 @@ where
}
}
impl<'p, T: PyClass> Deref for PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> Deref for PyRefMut<'p, T> {
type Target = T;
#[inline]
@ -777,46 +830,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]
fn deref_mut(&mut self) -> &mut T {
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) {
self.inner.set_borrow_flag(BorrowFlag::UNUSED)
}
}
impl<T: PyClass> IntoPy<PyObject> for PyRefMut<'_, T> {
impl<T: MutablePyClass> 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: PyClass> AsPyPointer for PyRefMut<'a, T> {
impl<'a, T: MutablePyClass> AsPyPointer for PyRefMut<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
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;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
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 {
fmt::Debug::fmt(&*(self.deref()), f)
}
}
#[doc(hidden)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct BorrowFlag(usize);
impl BorrowFlag {
@ -884,6 +937,7 @@ impl From<PyBorrowMutError> for PyErr {
pub trait PyCellLayout<T>: PyLayout<T> {
fn get_borrow_flag(&self) -> BorrowFlag;
fn set_borrow_flag(&self, flag: BorrowFlag);
/// Implementation of tp_dealloc.
/// # Safety
/// - slf must be a valid pointer to an instance of a T or a subclass.
@ -902,6 +956,7 @@ where
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.borrow_flag.set(flag)
}
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// For `#[pyclass]` types which inherit from PyAny, we can just call tp_free
if T::type_object_raw(py) == &mut PyBaseObject_Type {

View File

@ -29,6 +29,9 @@ pub trait PyClass:
type BaseNativeType: PyTypeInfo + PyNativeType;
}
pub unsafe trait MutablePyClass: PyClass {}
pub unsafe trait ImmutablePyClass: PyClass {}
/// For collecting slot items.
#[derive(Default)]
struct TypeSlots(Vec<ffi::PyType_Slot>);

View File

@ -24,6 +24,7 @@ fn _test_compile_errors() {
t.compile_fail("tests/ui/invalid_pymethod_names.rs");
t.compile_fail("tests/ui/invalid_argument_attributes.rs");
t.compile_fail("tests/ui/reject_generics.rs");
t.compile_fail("tests/ui/invalid_immutable_pyclass_borrow.rs");
tests_rust_1_48(&t);
tests_rust_1_49(&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,8 @@ note: required because it appears within the type `NotThreadSafe`
5 | struct NotThreadSafe {
| ^^^^^^^^^^^^^
note: required by a bound in `ThreadCheckerStub`
--> src/class/impl_.rs:710:33
--> src/class/impl_.rs:731:33
|
710 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
731 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
| ^^^^ 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)