This commit is contained in:
mejrs 2021-11-26 17:16:05 +01:00
parent a44e2f8eea
commit 5648815fff
5 changed files with 126 additions and 228 deletions

View File

@ -814,115 +814,7 @@ The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplC
```rust
# #[cfg(not(feature = "multiple-pymethods"))] {
# use pyo3::prelude::*;
// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled.
struct MyClass {
# #[allow(dead_code)]
num: i32,
}
unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
type AsRefTarget = ::pyo3::PyCell<Self>;
const NAME: &'static str = "MyClass";
const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None;
#[inline]
fn type_object_raw(py: ::pyo3::Python<'_>) -> *mut ::pyo3::ffi::PyTypeObject {
use ::pyo3::type_object::LazyStaticType;
static TYPE_OBJECT: LazyStaticType = LazyStaticType::new();
TYPE_OBJECT.get_or_init::<Self>(py)
}
}
impl ::pyo3::PyClass for MyClass {
type Dict = ::pyo3::pyclass_slots::PyClassDummySlot;
type WeakRef = ::pyo3::pyclass_slots::PyClassDummySlot;
type BaseNativeType = ::pyo3::PyAny;
}
unsafe impl ::pyo3::pyclass::MutablePyClass for MyClass {}
unsafe impl ::pyo3::class::impl_::BorrowImpl for MyClass {}
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)
}
}
impl ::pyo3::class::impl_::PyClassImpl for MyClass {
const DOC: &'static str = "\u{0}";
const IS_GC: bool = false;
const IS_BASETYPE: bool = false;
const IS_SUBCLASS: bool = false;
type Layout = ::pyo3::PyCell<Self>;
type BaseType = ::pyo3::PyAny;
type ThreadChecker = ::pyo3::class::impl_::ThreadCheckerStub<MyClass>;
fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
visitor(collector.py_methods());
visitor(collector.py_class_descriptors());
visitor(collector.object_protocol_methods());
visitor(collector.async_protocol_methods());
visitor(collector.descr_protocol_methods());
visitor(collector.mapping_protocol_methods());
visitor(collector.number_protocol_methods());
}
fn get_new() -> ::std::option::Option<::pyo3::ffi::newfunc> {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
collector.new_impl()
}
fn get_alloc() -> ::std::option::Option<::pyo3::ffi::allocfunc> {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
collector.alloc_impl()
}
fn get_free() -> ::std::option::Option<::pyo3::ffi::freefunc> {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
collector.free_impl()
}
fn for_each_proto_slot(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::ffi::PyType_Slot])) {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
visitor(collector.object_protocol_slots());
visitor(collector.number_protocol_slots());
visitor(collector.iter_protocol_slots());
visitor(collector.gc_protocol_slots());
visitor(collector.descr_protocol_slots());
visitor(collector.mapping_protocol_slots());
visitor(collector.sequence_protocol_slots());
visitor(collector.async_protocol_slots());
visitor(collector.buffer_protocol_slots());
visitor(collector.methods_protocol_slots());
}
fn get_buffer() -> ::std::option::Option<&'static ::pyo3::class::impl_::PyBufferProcs> {
use ::pyo3::class::impl_::*;
let collector = PyClassImplCollector::<Self>::new();
collector.buffer_procs()
}
}
impl ::pyo3::class::impl_::PyClassDescriptors<MyClass>
for ::pyo3::class::impl_::PyClassImplCollector<MyClass>
{
fn py_class_descriptors(self) -> &'static [::pyo3::class::methods::PyMethodDefType] {
static METHODS: &[::pyo3::class::methods::PyMethodDefType] = &[];
METHODS
}
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
# });
# }
```

View File

@ -760,6 +760,17 @@ impl<'a> PyClassImplsBuilder<'a> {
}
},
};
let mutability = if self.attr.is_immutable {
quote! {
::pyo3::pycell::Immutable
}
} else {
quote! {
::pyo3::pycell::Mutable
}
};
quote! {
impl ::pyo3::class::impl_::PyClassImpl for #cls {
const DOC: &'static str = #doc;
@ -770,6 +781,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type Layout = ::pyo3::PyCell<Self>;
type BaseType = #base;
type ThreadChecker = #thread_checker;
type Mutability= #mutability;
fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) {
use ::pyo3::class::impl_::*;
@ -828,30 +840,10 @@ impl<'a> PyClassImplsBuilder<'a> {
if self.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 {}
}
}
}

View File

@ -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 + BorrowImpl {
pub trait PyClassImpl: Sized {
/// Class doc string
const DOC: &'static str = "\0";
@ -67,6 +67,8 @@ pub trait PyClassImpl: Sized + BorrowImpl {
/// can be accessed by multiple threads by `threading` module.
type ThreadChecker: PyClassThreadChecker<Self>;
type Mutability: crate::pycell::Mutability;
fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {}
fn get_new() -> Option<ffi::newfunc> {
None
@ -83,27 +85,6 @@ pub trait PyClassImpl: Sized + BorrowImpl {
}
}
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

@ -192,17 +192,112 @@ use std::cell::{Cell, UnsafeCell};
use std::fmt;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::marker::PhantomData;
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.
/// This is necessary for sharing BorrowFlag between parents and children.
#[doc(hidden)]
#[repr(C)]
pub struct PyCellBase<T> {
pub struct PyCellBase<T, M: Mutability> {
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
///
@ -395,13 +490,7 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self);
if flag == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
crate::class::impl_::BorrowImpl::increment_borrow_flag()(self, flag);
Ok(PyRef { inner: self })
}
self.borrow_checker().try_borrow().map(|_| PyRef { inner: self })
}
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
@ -429,12 +518,7 @@ impl<T: PyClass> PyCell<T> {
where
T: MutablePyClass,
{
if self.get_borrow_flag() != BorrowFlag::UNUSED {
Err(PyBorrowMutError { _private: () })
} else {
self.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW);
Ok(PyRefMut { inner: self })
}
self.borrow_checker().try_borrow_mut().map(|_| PyRefMut { inner: self })
}
/// Immutably borrows the value `T`, returning an error if the value is
@ -467,13 +551,7 @@ impl<T: PyClass> PyCell<T> {
/// });
/// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if crate::class::impl_::BorrowImpl::get_borrow_flag()(self)
== BorrowFlag::HAS_MUTABLE_BORROW
{
Err(PyBorrowError { _private: () })
} else {
Ok(&*self.contents.value.get())
}
self.borrow_checker().try_borrow_unguarded().map(|_:()| &*self.contents.value.get())
}
/// Replaces the wrapped value with a new one, returning the old value.
@ -521,42 +599,6 @@ 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> {}
@ -740,8 +782,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> {
impl<'p, T: PyClass> Drop for PyRef<'p, T> {
fn drop(&mut self) {
let flag = crate::class::impl_::BorrowImpl::get_borrow_flag()(self.inner);
crate::class::impl_::BorrowImpl::decrement_borrow_flag()(self.inner, flag);
self.inner.borrow_checker().release_borrow()
}
}
@ -839,7 +880,7 @@ impl<'p, T: MutablePyClass> DerefMut for PyRefMut<'p, T> {
impl<'p, T: MutablePyClass> Drop for PyRefMut<'p, T> {
fn drop(&mut self) {
self.inner.set_borrow_flag(BorrowFlag::UNUSED)
self.inner.borrow_checker().release_borrow_mut()
}
}
@ -935,9 +976,7 @@ impl From<PyBorrowMutError> for PyErr {
#[doc(hidden)]
pub trait PyCellLayout<T>: PyLayout<T> {
fn get_borrow_flag(&self) -> BorrowFlag;
fn set_borrow_flag(&self, flag: BorrowFlag);
fn borrow_checker(&self) -> &Mutable;
/// Implementation of tp_dealloc.
/// # Safety
/// - slf must be a valid pointer to an instance of a T or a subclass.
@ -945,16 +984,13 @@ pub trait PyCellLayout<T>: PyLayout<T> {
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python);
}
impl<T, U> PyCellLayout<T> for PyCellBase<U>
impl<T, U> PyCellLayout<T> for PyCellBase<U, Mutable>
where
U: PySizedLayout<T>,
T: PyTypeInfo,
{
fn get_borrow_flag(&self) -> BorrowFlag {
self.borrow_flag.get()
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.borrow_flag.set(flag)
fn borrow_checker(&self) -> &Mutable {
&self.borrow_impl
}
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
@ -982,12 +1018,9 @@ impl<T: PyClass> PyCellLayout<T> for PyCell<T>
where
<T::BaseType as PyClassBaseType>::LayoutAsBase: PyCellLayout<T::BaseType>,
{
fn get_borrow_flag(&self) -> BorrowFlag {
fn borrow_checker(&self) -> &Mutable {
self.contents.thread_checker.ensure();
self.ob_base.get_borrow_flag()
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
self.ob_base.set_borrow_flag(flag)
self.ob_base.borrow_checker()
}
unsafe fn tp_dealloc(slf: *mut ffi::PyObject, py: Python) {
// Safety: Python only calls tp_dealloc when no references to the object remain.

View File

@ -198,7 +198,7 @@ macro_rules! pyobject_native_type_sized {
impl<'a, $($generics,)*> $crate::class::impl_::PyClassBaseType for $name {
type Dict = $crate::pyclass_slots::PyClassDummySlot;
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
type LayoutAsBase = $crate::pycell::PyCellBase<$layout>;
type LayoutAsBase = $crate::pycell::PyCellBase<$layout, $crate::pycell::Mutable>;
type BaseNativeType = $name;
type ThreadChecker = $crate::class::impl_::ThreadCheckerStub<$crate::PyObject>;
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;