Skip to content

Commit

Permalink
Merge pull request #1979 from mejrs/immutable
Browse files Browse the repository at this point in the history
Opt out of PyCell borrow tracking
  • Loading branch information
davidhewitt authored Apr 23, 2022
2 parents dea9eb7 + b196aa1 commit ca8958c
Show file tree
Hide file tree
Showing 22 changed files with 686 additions and 197 deletions.
29 changes: 17 additions & 12 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -940,19 +940,14 @@ The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplC
# #[cfg(not(feature = "multiple-pymethods"))] {
# use pyo3::prelude::*;
// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled.

/// Class for demonstration
struct MyClass {
# #[allow(dead_code)]
num: i32,
}

unsafe impl pyo3::PyTypeInfo for MyClass {
type AsRefTarget = PyCell<Self>;

unsafe impl ::pyo3::type_object::PyTypeInfo for MyClass {
type AsRefTarget = ::pyo3::PyCell<Self>;
const NAME: &'static str = "MyClass";
const MODULE: Option<&'static str> = None;

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;
Expand All @@ -961,10 +956,14 @@ unsafe impl pyo3::PyTypeInfo for MyClass {
}
}

impl pyo3::pyclass::PyClass for MyClass {
type Dict = pyo3::impl_::pyclass::PyClassDummySlot;
type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot;
type BaseNativeType = PyAny;
impl ::pyo3::PyClass 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<PyObject> for MyClass {
Expand All @@ -980,6 +979,11 @@ 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;
type BaseNativeType = ::pyo3::PyAny;

fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) {
use pyo3::impl_::pyclass::*;
Expand All @@ -989,6 +993,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
visitor(collector.py_methods());
}
}

# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
Expand Down
1 change: 1 addition & 0 deletions pyo3-macros-backend/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod kw {
syn::custom_keyword!(transparent);
syn::custom_keyword!(unsendable);
syn::custom_keyword!(weakref);
syn::custom_keyword!(immutable);
}

#[derive(Clone, Debug)]
Expand Down
98 changes: 68 additions & 30 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct PyClassPyO3Options {
pub dict: Option<kw::dict>,
pub extends: Option<ExtendsAttribute>,
pub freelist: Option<FreelistAttribute>,
pub immutable: Option<kw::immutable>,
pub mapping: Option<kw::mapping>,
pub module: Option<ModuleAttribute>,
pub name: Option<NameAttribute>,
Expand All @@ -70,6 +71,7 @@ enum PyClassPyO3Option {
Dict(kw::dict),
Extends(ExtendsAttribute),
Freelist(FreelistAttribute),
Immutable(kw::immutable),
Mapping(kw::mapping),
Module(ModuleAttribute),
Name(NameAttribute),
Expand All @@ -92,6 +94,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::mapping) {
input.parse().map(PyClassPyO3Option::Mapping)
} else if lookahead.peek(attributes::kw::module) {
Expand Down Expand Up @@ -149,6 +153,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::Mapping(mapping) => set_option!(mapping),
PyClassPyO3Option::Module(module) => set_option!(module),
PyClassPyO3Option::Name(name) => set_option!(name),
Expand Down Expand Up @@ -684,44 +689,31 @@ impl<'a> PyClassImplsBuilder<'a> {

fn impl_pyclass(&self) -> TokenStream {
let cls = self.cls;
let attr = self.attr;
let dict = if attr.options.dict.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

// insert space for weak ref
let weakref = if attr.options.weakref.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

let base_nativetype = if attr.options.extends.is_some() {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
};
quote! {
impl _pyo3::PyClass for #cls {
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
}
impl _pyo3::PyClass for #cls { }
}
}
fn impl_extractext(&self) -> TokenStream {
let cls = self.cls;
quote! {
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a #cls
{
type Target = _pyo3::PyRef<'a, #cls>;
if self.attr.options.immutable.is_some() {
quote! {
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'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
{
type Target = _pyo3::PyRefMut<'a, #cls>;
impl<'a> _pyo3::derive_utils::ExtractExt<'a> for &'a mut #cls
{
type Target = _pyo3::PyRefMut<'a, #cls>;
}
}
}
}
Expand Down Expand Up @@ -831,6 +823,47 @@ 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() {
quote! {
ImmutableChild
}
} else {
quote! {
MutableChild
}
};

let cls = self.cls;
let attr = self.attr;
let dict = if attr.options.dict.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassDictSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

// insert space for weak ref
let weakref = if attr.options.weakref.is_some() {
quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot }
} else {
quote! { _pyo3::impl_::pyclass::PyClassDummySlot }
};

let base_nativetype = if attr.options.extends.is_some() {
quote! { <Self::BaseType as _pyo3::impl_::pyclass::PyClassBaseType>::BaseNativeType }
} else {
quote! { _pyo3::PyAny }
};

quote! {
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
const DOC: &'static str = #doc;
Expand All @@ -842,6 +875,11 @@ 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;
type BaseNativeType = #base_nativetype;

fn for_all_items(visitor: &mut dyn ::std::ops::FnMut(& _pyo3::impl_::pyclass::PyClassItems)) {
use _pyo3::impl_::pyclass::*;
Expand Down
12 changes: 7 additions & 5 deletions src/class/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,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;

/// Basic Python class customization
Expand All @@ -26,14 +28,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!()
}
Expand Down Expand Up @@ -77,12 +79,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<()>;
}
Expand Down
4 changes: 2 additions & 2 deletions src/class/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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
Expand All @@ -15,7 +15,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>: PyClass {
pub trait PyBufferProtocol<'p>: MutablePyClass {
// No default implementations so that implementors of this trait provide both methods.

fn bf_getbuffer(
Expand Down
4 changes: 2 additions & 2 deletions src/class/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

//! Python GC support

use crate::{ffi, PyCell, PyClass};
use crate::{ffi, pyclass::MutablePyClass, PyCell};
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>: PyClass {
pub trait PyGCProtocol<'p>: MutablePyClass {
fn __traverse__(&'p self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self);
}
Expand Down
6 changes: 3 additions & 3 deletions src/class/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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)]
Expand Down Expand Up @@ -52,13 +52,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<()>;
}
Expand Down
Loading

0 comments on commit ca8958c

Please sign in to comment.