From da4ed398bb7ee1861f7d79719c967d8f080591a4 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 15 Feb 2020 13:42:25 +0900 Subject: [PATCH] Fix PyCell to share BorrowFlag with parents --- pyo3-derive-backend/src/method.rs | 36 +++++- pyo3-derive-backend/src/module.rs | 46 ++++---- pyo3-derive-backend/src/pyclass.rs | 47 ++++---- pyo3-derive-backend/src/pymethod.rs | 33 ++++-- src/class/iter.rs | 10 +- src/class/macros.rs | 7 +- src/conversion.rs | 171 ++++++++++++++++------------ src/derive_utils.rs | 43 ++++++- src/err.rs | 3 - src/freelist.rs | 4 +- src/instance.rs | 3 +- src/lib.rs | 6 +- src/prelude.rs | 3 +- src/pycell.rs | 167 ++++++++++++++------------- src/pyclass.rs | 19 ++-- src/pyclass_init.rs | 14 ++- src/type_object.rs | 17 +-- src/types/any.rs | 4 +- src/types/mod.rs | 19 +++- tests/test_class_new.rs | 14 ++- tests/test_dunder.rs | 4 +- tests/test_gc.rs | 22 ++-- tests/test_sequence.rs | 2 +- 23 files changed, 416 insertions(+), 278 deletions(-) mode change 100755 => 100644 tests/test_dunder.rs diff --git a/pyo3-derive-backend/src/method.rs b/pyo3-derive-backend/src/method.rs index ae0186ca..a4505ac4 100644 --- a/pyo3-derive-backend/src/method.rs +++ b/pyo3-derive-backend/src/method.rs @@ -35,6 +35,7 @@ pub enum FnType { #[derive(Clone, PartialEq, Debug)] pub struct FnSpec<'a> { pub tp: FnType, + pub self_: Option, // Rust function name pub name: &'a syn::Ident, // Wrapped python name. This should not have any leading r#. @@ -54,6 +55,28 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type { } impl<'a> FnSpec<'a> { + /// Generate the code for borrowing self + pub(crate) fn borrow_self(&self) -> TokenStream { + let is_mut = self + .self_ + .expect("impl_borrow_self is called for non-self fn"); + if is_mut { + quote! { + match _slf.try_borrow() { + Ok(ref_) => ref_, + Err(e) => return e.into::.restore_and_null(_py), + } + } + } else { + quote! { + match _slf.try_borrow_mut() { + Ok(ref_) => ref_, + Err(e) => return e.into::.restore_and_null(_py), + } + } + } + } + /// Parser function signature and function attributes pub fn parse( sig: &'a syn::Signature, @@ -67,19 +90,19 @@ impl<'a> FnSpec<'a> { mut python_name, } = parse_method_attributes(meth_attrs, allow_custom_name)?; - let mut has_self = false; + let mut self_ = None; let mut arguments = Vec::new(); for input in sig.inputs.iter() { match input { - syn::FnArg::Receiver(_) => { - has_self = true; + syn::FnArg::Receiver(recv) => { + self_ = Some(recv.mutability.is_some()); } syn::FnArg::Typed(syn::PatType { ref pat, ref ty, .. }) => { // skip first argument (cls) - if fn_type == FnType::FnClass && !has_self { - has_self = true; + if fn_type == FnType::FnClass && !self_.is_none() { + self_ = Some(false); continue; } @@ -114,7 +137,7 @@ impl<'a> FnSpec<'a> { let ty = get_return_info(&sig.output); - if fn_type == FnType::Fn && !has_self { + if fn_type == FnType::Fn && self_.is_none() { if arguments.is_empty() { return Err(syn::Error::new_spanned( name, @@ -174,6 +197,7 @@ impl<'a> FnSpec<'a> { Ok(FnSpec { tp: fn_type, + self_, name, python_name, attrs: fn_attrs, diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index badaf37f..05046fdd 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -54,27 +54,22 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) { } /// Transforms a rust fn arg parsed with syn into a method::FnArg -fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option> { - match input { - syn::FnArg::Receiver(_) => None, - syn::FnArg::Typed(ref cap) => { - let (mutability, by_ref, ident) = match *cap.pat { - syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), - _ => panic!("unsupported argument: {:?}", cap.pat), - }; +fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> method::FnArg<'a> { + let (mutability, by_ref, ident) = match *cap.pat { + syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), + _ => panic!("unsupported argument: {:?}", cap.pat), + }; - let py = crate::utils::if_type_is_python(&cap.ty); - let opt = method::check_arg_ty_and_optional(&name, &cap.ty); - Some(method::FnArg { - name: ident, - mutability, - by_ref, - ty: &cap.ty, - optional: opt, - py, - reference: method::is_ref(&name, &cap.ty), - }) - } + let py = crate::utils::if_type_is_python(&cap.ty); + let opt = method::check_arg_ty_and_optional(&name, &cap.ty); + method::FnArg { + name: ident, + mutability, + by_ref, + ty: &cap.ty, + optional: opt, + py, + reference: method::is_ref(&name, &cap.ty), } } @@ -138,10 +133,16 @@ pub fn add_fn_to_module( pyfn_attrs: Vec, ) -> TokenStream { let mut arguments = Vec::new(); + let mut self_ = None; for input in func.sig.inputs.iter() { - if let Some(fn_arg) = wrap_fn_argument(input, &func.sig.ident) { - arguments.push(fn_arg); + match input { + syn::FnArg::Receiver(recv) => { + self_ = Some(recv.mutability.is_some()); + } + syn::FnArg::Typed(ref cap) => { + arguments.push(wrap_fn_argument(cap, &func.sig.ident)); + } } } @@ -160,6 +161,7 @@ pub fn add_fn_to_module( let spec = method::FnSpec { tp: method::FnType::Fn, + self_, name: &function_wrapper_ident, python_name, attrs: pyfn_attrs, diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index bf0f1af3..fc7eb11e 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -315,14 +315,18 @@ fn impl_class( } let weakref = if has_weakref { - quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; } + quote! { pyo3::pyclass_slots::PyClassWeakRefSlot } + } else if attr.has_extends { + quote! { ::WeakRef } } else { - quote! { type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; } + quote! { pyo3::pyclass_slots::PyClassDummySlot } }; let dict = if has_dict { - quote! { type Dict = pyo3::pyclass_slots::PyClassDictSlot; } + quote! { pyo3::pyclass_slots::PyClassDictSlot } + } else if attr.has_extends { + quote! { ::Dict } } else { - quote! { type Dict = pyo3::pyclass_slots::PyClassDummySlot; } + quote! { pyo3::pyclass_slots::PyClassDummySlot } }; let module = if let Some(m) = &attr.module { quote! { Some(#m) } @@ -355,6 +359,16 @@ fn impl_class( } else { quote! { 0 } }; + let base_layout = if attr.has_extends { + quote! { ::Layout } + } else { + quote! { pyo3::pycell::PyCellBase } + }; + let base_nativetype = if attr.has_extends { + quote! { ::BaseNativeType } + } else { + quote! { pyo3::types::PyAny } + }; // If #cls is not extended type, we allow Self->PyObject conversion let into_pyobject = if !attr.has_extends { @@ -373,8 +387,10 @@ fn impl_class( unsafe impl pyo3::type_object::PyTypeInfo for #cls { type Type = #cls; type BaseType = #base; - type ConcreteLayout = pyo3::pyclass::PyCell; + type Layout = pyo3::pycell::PyCell; + type BaseLayout = #base_layout; type Initializer = pyo3::pyclass_init::PyClassInitializer; + type Reference = pyo3::pycell::PyCell; const NAME: &'static str = #cls_name; const MODULE: Option<&'static str> = #module; @@ -390,20 +406,9 @@ fn impl_class( } impl pyo3::PyClass for #cls { - #dict - #weakref - } - - impl pyo3::conversion::FromPyObjectImpl for #cls { - type Impl = pyo3::conversion::extract_impl::Cloned; - } - - impl pyo3::conversion::FromPyObjectImpl for &'_ #cls { - type Impl = pyo3::conversion::extract_impl::Reference; - } - - impl pyo3::conversion::FromPyObjectImpl for &'_ mut #cls { - type Impl = pyo3::conversion::extract_impl::MutReference; + type Dict = #dict; + type WeakRef = #weakref; + type BaseNativeType = #base_nativetype; } #into_pyobject @@ -472,6 +477,8 @@ fn impl_descriptors( FnType::Getter => { let spec = FnSpec { tp: FnType::Getter, + // Assume that the getter has &self receiver + self_: Some(false), name: &name, python_name: name.unraw(), attrs: Vec::new(), @@ -488,6 +495,8 @@ fn impl_descriptors( ); let spec = FnSpec { tp: FnType::Setter, + // Assume that the setter has &mut self receiver + self_: Some(true), name: &setter_name, python_name: name.unraw(), attrs: Vec::new(), diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 7b737057..7cae4497 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -45,7 +45,11 @@ fn check_generic(sig: &syn::Signature) -> syn::Result<()> { /// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords) pub fn impl_wrap(cls: &syn::Type, spec: &FnSpec<'_>, noargs: bool) -> TokenStream { let body = impl_call(cls, &spec); - let slf = impl_self("e! { &mut #cls }); + let borrow_self = spec.borrow_self(); + let slf = quote! { + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; + }; impl_wrap_common(cls, spec, noargs, slf, body) } @@ -60,7 +64,13 @@ pub fn impl_wrap_pyslf( let body = quote! { #cls::#name(_slf, #(#names),*) }; - let slf = impl_self(self_ty); + let slf = quote! { + let _cell = _py.from_borrowed_ptr::>(_slf); + let _slf: #self_ty = match pyo3::derive_utils::PySelf::from_cell(_cell) { + Ok(_slf) => _slf, + Err(e) => return e.restore_and_null(py), + }; + }; impl_wrap_common(cls, spec, noargs, slf, body) } @@ -123,6 +133,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { let python_name = &spec.python_name; let cb = impl_call(cls, &spec); let body = impl_arg_params(&spec, cb); + let borrow_self = spec.borrow_self(); quote! { #[allow(unused_mut)] @@ -134,7 +145,8 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()"); let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); - let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let _args = _py.from_borrowed_ptr::(_args); let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); @@ -266,6 +278,8 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result *mut pyo3::ffi::PyObject @@ -274,7 +288,8 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let result = pyo3::derive_utils::IntoPyResult::into_py_result(#fncall); @@ -312,6 +327,7 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul } }; + let borrow_self = spec.borrow_self(); Ok(quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( @@ -321,7 +337,8 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()"); let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); - let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let _value = _py.from_borrowed_ptr(_value); let _result = match <#val_ty as pyo3::FromPyObject>::extract(_value) { @@ -354,12 +371,6 @@ fn impl_call(_cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { quote! { _slf.#fname(#(#names),*) } } -fn impl_self(self_ty: &T) -> TokenStream { - quote! { - let _slf: #self_ty = pyo3::FromPyPointer::from_borrowed_ptr(_py, _slf); - } -} - /// Converts a bool to "true" or "false" fn bool_to_ident(condition: bool) -> syn::Ident { if condition { diff --git a/src/class/iter.rs b/src/class/iter.rs index aca9c030..d347b65d 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -4,7 +4,7 @@ use crate::callback::{CallbackConverter, PyObjectCallbackConverter}; use crate::err::PyResult; -use crate::{ffi, IntoPy, IntoPyPointer, PyCell, PyClass, PyObject, Python}; +use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, PyRefMut, Python}; use std::ptr; /// Python Iterator Interface. @@ -13,14 +13,14 @@ use std::ptr; /// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter` #[allow(unused_variables)] pub trait PyIterProtocol<'p>: PyClass { - fn __iter__(slf: &mut PyCell) -> Self::Result + fn __iter__(slf: PyRefMut) -> Self::Result where Self: PyIterIterProtocol<'p>, { unimplemented!() } - fn __next__(slf: &mut PyCell) -> Self::Result + fn __next__(slf: PyRefMut) -> Self::Result where Self: PyIterNextProtocol<'p>, { @@ -77,7 +77,7 @@ where { #[inline] fn tp_iter() -> Option { - py_unary_pyref_func!( + py_unary_refmut_func!( PyIterIterProtocol, T::__iter__, T::Success, @@ -105,7 +105,7 @@ where { #[inline] fn tp_iternext() -> Option { - py_unary_pyref_func!( + py_unary_refmut_func!( PyIterNextProtocol, T::__next__, Option, diff --git a/src/class/macros.rs b/src/class/macros.rs index 642bc9aa..74f58029 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -39,17 +39,16 @@ macro_rules! py_unary_func { #[macro_export] #[doc(hidden)] -macro_rules! py_unary_pyref_func { +macro_rules! py_unary_refmut_func { ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject where T: for<'p> $trait<'p>, { - use $crate::PyCell; let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf: &mut PyCell = &mut *(slf as *mut PyCell); - let res = $class::$f(slf).into(); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); + let res = $class::$f(slf.borrow_mut()).into(); $crate::callback::cb_convert($conv, py, res) } Some(wrap::<$class>) diff --git a/src/conversion.rs b/src/conversion.rs index 269a06d4..b6790d32 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -6,7 +6,7 @@ use crate::object::PyObject; use crate::type_object::{PyDowncastImpl, PyTypeInfo}; use crate::types::PyAny; use crate::types::PyTuple; -use crate::{ffi, gil, Py, Python}; +use crate::{ffi, gil, Py, PyCell, PyClass, PyNativeType, PyRef, PyRefMut, Python}; use std::ptr::NonNull; /// This trait represents that, **we can do zero-cost conversion from the object to FFI pointer**. @@ -244,74 +244,43 @@ where } } -#[doc(hidden)] -pub mod extract_impl { - use super::*; - - pub trait ExtractImpl<'a, Target>: Sized { - fn extract(source: &'a PyAny) -> PyResult; +impl<'a, T> FromPyObject<'a> for &'a PyCell +where + T: PyClass, +{ + fn extract(obj: &'a PyAny) -> PyResult { + PyTryFrom::try_from(obj).map_err(Into::into) } - - pub struct Cloned { - _private: (), - } - pub struct Reference { - _private: (), - } - - impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned - where - T: Clone + PyTryFrom<'a>, - Reference: ExtractImpl<'a, &'a T>, - { - fn extract(source: &'a PyAny) -> PyResult { - Ok(Reference::extract(source)?.clone()) - } - } - - impl<'a, T: 'a> ExtractImpl<'a, &'a T> for Reference - where - T: PyTryFrom<'a>, - { - fn extract(source: &'a PyAny) -> PyResult<&'a T> { - Ok(T::try_from(source)?) - } - } -} - -use extract_impl::ExtractImpl; - -/// This is an internal trait for re-using `FromPyObject` implementations for many pyo3 types. -/// -/// Users should implement `FromPyObject` directly instead of via this trait. -pub trait FromPyObjectImpl { - // Implement this trait with to specify the implementor of `extract_impl::ExtractImpl` to use for - // extracting this type from Python objects. - // - // Example valid implementations are `extract_impl::Cloned`, `extract_impl::Reference`, and - // `extract_impl::MutReference`, which are for extracting `T`, `&T` and `&mut T` respectively via - // PyTryFrom. - // - // We deliberately don't require Impl: ExtractImpl here because we allow #[pyclass] - // to specify an Impl which doesn't satisfy the ExtractImpl constraints. - // - // e.g. non-clone #[pyclass] can still have Impl: Cloned. - // - // We catch invalid Impls in the blanket impl for FromPyObject, which only - // complains when .extract() is actually used. - - /// The type which implements `ExtractImpl`. - type Impl; } impl<'a, T> FromPyObject<'a> for T where - T: FromPyObjectImpl + 'a, - ::Impl: ExtractImpl<'a, T>, + T: PyClass + Clone, { - #[inline] - fn extract(ob: &'a PyAny) -> PyResult { - ::Impl::extract(ob) + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + let ref_ = unsafe { cell.try_borrow_unguarded()? }; + Ok(ref_.clone()) + } +} + +impl<'a, T> FromPyObject<'a> for PyRef<'a, T> +where + T: PyClass, +{ + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + cell.try_borrow().map_err(Into::into) + } +} + +impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T> +where + T: PyClass, +{ + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + cell.try_borrow_mut().map_err(Into::into) } } @@ -345,19 +314,32 @@ pub trait PyTryFrom<'v>: Sized + PyDowncastImpl<'v> { unsafe fn try_from_unchecked>(value: V) -> &'v Self; } -// /// Trait implemented by Python object types that allow a checked downcast. -// /// This trait is similar to `std::convert::TryInto` -// pub trait PyTryInto<'v, T: PyDowncastImpl<'v>>: Sized { -// /// Cast from PyObject to a concrete Python object type. -// fn try_into(&self) -> Result<&T, PyDowncastError>; +/// Trait implemented by Python object types that allow a checked downcast. +/// This trait is similar to `std::convert::TryInto` +pub trait PyTryInto: Sized { + /// Cast from PyObject to a concrete Python object type. + fn try_into(&self) -> Result<&T, PyDowncastError>; -// /// Cast from PyObject to a concrete Python object type. With exact type check. -// fn try_into_exact(&self) -> Result<&T, PyDowncastError>; -// } + /// Cast from PyObject to a concrete Python object type. With exact type check. + fn try_into_exact(&self) -> Result<&T, PyDowncastError>; +} + +// TryFrom implies TryInto +impl PyTryInto for PyAny +where + U: for<'v> PyTryFrom<'v>, +{ + fn try_into(&self) -> Result<&U, PyDowncastError> { + U::try_from(self) + } + fn try_into_exact(&self) -> Result<&U, PyDowncastError> { + U::try_from_exact(self) + } +} impl<'v, T> PyTryFrom<'v> for T where - T: PyDowncastImpl<'v> + PyTypeInfo, + T: PyDowncastImpl<'v> + PyTypeInfo + PyNativeType, { fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { let value = value.into(); @@ -387,6 +369,36 @@ where } } +impl<'v, T> PyTryFrom<'v> for PyCell +where + T: 'v + PyClass, +{ + fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { + let value = value.into(); + unsafe { + if T::is_instance(value) { + Ok(Self::try_from_unchecked(value)) + } else { + Err(PyDowncastError) + } + } + } + fn try_from_exact>(value: V) -> Result<&'v Self, PyDowncastError> { + let value = value.into(); + unsafe { + if T::is_exact_instance(value) { + Ok(Self::try_from_unchecked(value)) + } else { + Err(PyDowncastError) + } + } + } + #[inline] + unsafe fn try_from_unchecked>(value: V) -> &'v Self { + Self::unchecked_downcast(value.into()) + } +} + /// Converts `()` to an empty Python tuple. impl FromPy<()> for Py { fn from_py(_: (), py: Python) -> Py { @@ -449,6 +461,21 @@ where } } +unsafe impl<'p, T> FromPyPointer<'p> for PyCell +where + T: PyClass, +{ + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) + } + unsafe fn from_borrowed_ptr_or_opt( + py: Python<'p>, + ptr: *mut ffi::PyObject, + ) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) + } +} + #[cfg(test)] mod test { use crate::types::PyList; diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 1dd35daf..3e6a3d64 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -11,7 +11,7 @@ use crate::instance::PyNativeType; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::types::{PyAny, PyDict, PyModule, PyTuple}; -use crate::{ffi, AsPyRef, GILPool, IntoPy, PyObject, Python}; +use crate::{ffi, GILPool, IntoPy, PyObject, Python}; use std::ptr; /// Description of a python parameter; used for `parse_args()`. @@ -100,8 +100,8 @@ pub fn parse_fn_args<'p>( // Adjust the remaining args let args = if accept_args { let py = args.py(); - // args.slice(used_args as isize, nargs as isize).as_ref(py) - unimplemented!() + let slice = args.slice(used_args as isize, nargs as isize).into_py(py); + py.checked_cast_as(slice).unwrap() } else { args }; @@ -199,3 +199,40 @@ impl>> IntoPyNewResult for PyRes self } } + +/// Utitlities for basetype +pub trait PyBaseTypeUtils { + type Dict; + type WeakRef; + type Layout; + type BaseNativeType; +} + +impl PyBaseTypeUtils for T { + type Dict = T::Dict; + type WeakRef = T::WeakRef; + type Layout = crate::pycell::PyCellInner; + type BaseNativeType = T::BaseNativeType; +} + +pub trait PySelf<'a, T: PyClass>: Sized { + fn from_cell(cell: &'a crate::PyCell) -> PyResult; +} + +impl<'a, T: PyClass> PySelf<'a, T> for &'a crate::PyCell { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + Ok(cell) + } +} + +impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRef<'a, T> { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + cell.try_borrow().map_err(Into::into) + } +} + +impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRefMut<'a, T> { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + cell.try_borrow_mut().map_err(Into::into) + } +} diff --git a/src/err.rs b/src/err.rs index 44ecb4dd..0841a340 100644 --- a/src/err.rs +++ b/src/err.rs @@ -59,9 +59,6 @@ pub type PyResult = Result; /// Marker type that indicates an error while downcasting pub struct PyDowncastError; -/// Marker type for `PyCell`. -pub struct PyBorrowError; - /// Helper conversion trait that allows to use custom arguments for exception constructor. pub trait PyErrArguments { /// Arguments for exception diff --git a/src/freelist.rs b/src/freelist.rs index 9dd44edf..e40dfdcc 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -72,7 +72,7 @@ impl PyClassAlloc for T where T: PyTypeInfo + PyClassWithFreeList, { - unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout { + unsafe fn alloc(_py: Python) -> *mut Self::Layout { if let Some(obj) = ::get_free_list().pop() { ffi::PyObject_Init(obj, ::type_object() as *const _ as _); obj as _ @@ -81,7 +81,7 @@ where } } - unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) { + unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); let obj = self_ as _; diff --git a/src/instance.rs b/src/instance.rs index 98d592a0..1eec1351 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -39,8 +39,7 @@ impl Py { pub fn new(py: Python, value: impl Into>) -> PyResult> where T: PyClass, - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { let initializer = value.into(); let obj = unsafe { initializer.create_cell(py)? }; diff --git a/src/lib.rs b/src/lib.rs index df4acc0c..28ec89f4 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ pub use crate::class::*; pub use crate::conversion::{ - AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, + AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToBorrowedObject, ToPyObject, }; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult}; @@ -127,7 +127,7 @@ pub use crate::gil::{init_once, GILGuard, GILPool}; pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType}; pub use crate::object::PyObject; pub use crate::objectprotocol::ObjectProtocol; -pub use crate::pycell::PyCell; +pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pyclass::PyClass; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::{prepare_freethreaded_python, Python}; @@ -171,7 +171,7 @@ pub mod marshal; mod object; mod objectprotocol; pub mod prelude; -mod pycell; +pub mod pycell; pub mod pyclass; pub mod pyclass_init; pub mod pyclass_slots; diff --git a/src/prelude.rs b/src/prelude.rs index 14d3a2b7..d97879a5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -15,9 +15,10 @@ pub use crate::gil::GILGuard; pub use crate::instance::{AsPyRef, Py}; pub use crate::object::PyObject; pub use crate::objectprotocol::ObjectProtocol; +pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::Python; -pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToPyObject}; +pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject}; // This is only part of the prelude because we need it for the pymodule function pub use crate::types::PyModule; pub use pyo3cls::pymodule; diff --git a/src/pycell.rs b/src/pycell.rs index b40fbe93..667938b7 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,43 +1,76 @@ //! Traits and structs for `#[pyclass]`. -use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; +use crate::conversion::{AsPyPointer, FromPyPointer, PyTryFrom, ToPyObject}; +use crate::err::PyDowncastError; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout}; +use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; use crate::types::PyAny; -use crate::{ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; +use crate::{ffi, gil, PyErr, PyNativeType, PyObject, PyResult, Python}; use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; +#[doc(hidden)] +#[repr(C)] +pub struct PyCellBase { + ob_base: T::Layout, + borrow_flag: Cell, +} + +unsafe impl PyObjectLayout for PyCellBase +where + T: PyTypeInfo + PyNativeType, + T::Layout: PyObjectSizedLayout, +{ + const IS_NATIVE_TYPE: bool = false; + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + None + } + unsafe fn unchecked_ref(&self) -> &T { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&self) -> &mut T { + &mut *((&self) as *const &Self as *const _ as *mut _) + } +} + +// This impl ensures `PyCellBase` can be a base type. +impl PyObjectSizedLayout for PyCellBase +where + T: PyTypeInfo + PyNativeType, + T::Layout: PyObjectSizedLayout, +{ +} + /// Inner type of `PyCell` without dict slots and reference counter. /// This struct has two usages: /// 1. As an inner type of `PyRef` and `PyRefMut`. /// 2. As a base class when `#[pyclass(Base)]` is specified. #[doc(hidden)] #[repr(C)] -pub struct PyCellBase { - ob_base: ::ConcreteLayout, +pub struct PyCellInner { + ob_base: T::BaseLayout, value: ManuallyDrop>, } -impl AsPyPointer for PyCellBase { +impl AsPyPointer for PyCellInner { fn as_ptr(&self) -> *mut ffi::PyObject { (self as *const _) as *mut _ } } -unsafe impl PyObjectLayout for PyCellBase { +unsafe impl PyObjectLayout for PyCellInner { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.ob_base) } unsafe fn unchecked_ref(&self) -> &T { &*self.value.get() } - unsafe fn unchecked_refmut(&mut self) -> &mut T { + unsafe fn unchecked_refmut(&self) -> &mut T { &mut *self.value.get() } unsafe fn py_init(&mut self, value: T) { @@ -49,6 +82,20 @@ unsafe impl PyObjectLayout for PyCellBase { } } +// This impl ensures `PyCellInner` can be a base type. +impl PyObjectSizedLayout for PyCellInner {} + +impl PyCellInner { + fn get_borrow_flag(&self) -> BorrowFlag { + let base = (&self.ob_base) as *const _ as *const PyCellBase; + unsafe { (*base).borrow_flag.get() } + } + fn set_borrow_flag(&self, flag: BorrowFlag) { + let base = (&self.ob_base) as *const _ as *const PyCellBase; + unsafe { (*base).borrow_flag.set(flag) } + } +} + /// `PyCell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// @@ -74,8 +121,7 @@ unsafe impl PyObjectLayout for PyCellBase { /// ``` #[repr(C)] pub struct PyCell { - base: PyCellBase, - borrow_flag: Cell, + inner: PyCellInner, dict: T::Dict, weakref: T::WeakRef, } @@ -84,8 +130,7 @@ impl PyCell { /// Make new `PyCell` on the Python heap and returns the reference of it. pub fn new(py: Python, value: impl Into>) -> PyResult<&Self> where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { unsafe { let initializer = value.into(); @@ -103,57 +148,51 @@ impl PyCell { } pub fn try_borrow(&self) -> Result, PyBorrowError> { - let flag = self.borrow_flag.get(); + let flag = self.inner.get_borrow_flag(); if flag != BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { - self.borrow_flag.set(flag.increment()); - Ok(PyRef { - value: &self.base, - flag: &self.borrow_flag, - }) + self.inner.set_borrow_flag(flag.increment()); + Ok(PyRef { inner: &self.inner }) } } pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> { - if self.borrow_flag.get() != BorrowFlag::UNUSED { + if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { Err(PyBorrowMutError { _private: () }) } else { - self.borrow_flag.set(BorrowFlag::HAS_MUTABLE_BORROW); - Ok(PyRefMut { - value: unsafe { &mut *(self.base.as_ptr() as *mut _) }, - flag: &self.borrow_flag, - }) + self.inner.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW); + Ok(PyRefMut { inner: &self.inner }) } } pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - if self.borrow_flag.get() != BorrowFlag::HAS_MUTABLE_BORROW { + if self.inner.get_borrow_flag() != BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { - Ok(&*self.base.value.get()) + Ok(&*self.inner.value.get()) } } pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { - if self.borrow_flag.get() != BorrowFlag::UNUSED { + if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { Err(PyBorrowMutError { _private: () }) } else { - Ok(&mut *self.base.value.get()) + Ok(&mut *self.inner.value.get()) } } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { let base = T::alloc(py); if base.is_null() { return Err(PyErr::fetch(py)); } + let base = base as *mut PyCellBase; + (*base).borrow_flag = Cell::new(BorrowFlag::UNUSED); let self_ = base as *mut Self; - (*self_).borrow_flag = Cell::new(BorrowFlag::UNUSED); (*self_).dict = T::Dict::new(); (*self_).weakref = T::WeakRef::new(); Ok(self_) @@ -162,23 +201,23 @@ impl PyCell { unsafe impl PyObjectLayout for PyCell { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { - Some(&mut self.base.ob_base) + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + Some(&mut self.inner.ob_base) } unsafe fn unchecked_ref(&self) -> &T { - self.base.unchecked_ref() + self.inner.unchecked_ref() } - unsafe fn unchecked_refmut(&mut self) -> &mut T { - self.base.unchecked_refmut() + unsafe fn unchecked_refmut(&self) -> &mut T { + self.inner.unchecked_refmut() } unsafe fn py_init(&mut self, value: T) { - self.base.value = ManuallyDrop::new(UnsafeCell::new(value)); + self.inner.value = ManuallyDrop::new(UnsafeCell::new(value)); } unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.base.value); + ManuallyDrop::drop(&mut self.inner.value); self.dict.clear_dict(py); self.weakref.clear_weakrefs(self.as_ptr(), py); - self.base.ob_base.py_drop(py); + self.inner.ob_base.py_drop(py); } } @@ -189,8 +228,6 @@ unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { private_impl! {} } -impl PyObjectSizedLayout for PyCell {} - impl AsPyPointer for PyCell { fn as_ptr(&self) -> *mut ffi::PyObject { (self as *const _) as *mut _ @@ -203,35 +240,13 @@ impl ToPyObject for &PyCell { } } -impl ToPyObject for &mut PyCell { - fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } -} - -unsafe impl<'p, T> FromPyPointer<'p> for PyCell -where - T: PyClass, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { - NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) - } - unsafe fn from_borrowed_ptr_or_opt( - py: Python<'p>, - ptr: *mut ffi::PyObject, - ) -> Option<&'p Self> { - NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) - } -} - pub struct PyRef<'p, T: PyClass> { - value: &'p PyCellBase, - flag: &'p Cell, + inner: &'p PyCellInner, } impl<'p, T: PyClass> PyRef<'p, T> { pub fn get_super(&'p self) -> &'p T::BaseType { - unsafe { self.value.ob_base.unchecked_ref() } + unsafe { self.inner.ob_base.unchecked_ref() } } } @@ -240,27 +255,27 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.value.unchecked_ref() } + unsafe { self.inner.unchecked_ref() } } } impl<'p, T: PyClass> Drop for PyRef<'p, T> { fn drop(&mut self) { - self.flag.set(self.flag.get().decrement()); + let flag = self.inner.get_borrow_flag(); + self.inner.set_borrow_flag(flag.decrement()) } } pub struct PyRefMut<'p, T: PyClass> { - value: &'p mut PyCellBase, - flag: &'p Cell, + inner: &'p PyCellInner, } impl<'p, T: PyClass> PyRefMut<'p, T> { pub fn get_super(&'p self) -> &'p T::BaseType { - unsafe { self.value.ob_base.unchecked_ref() } + unsafe { self.inner.ob_base.unchecked_ref() } } - pub fn get_super_mut(&'p mut self) -> &'p mut T::BaseType { - unsafe { self.value.ob_base.unchecked_refmut() } + pub fn get_super_mut(&'p self) -> &'p mut T::BaseType { + unsafe { self.inner.ob_base.unchecked_refmut() } } } @@ -269,20 +284,20 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.value.unchecked_ref() } + unsafe { self.inner.unchecked_ref() } } } impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { self.value.unchecked_refmut() } + unsafe { self.inner.unchecked_refmut() } } } impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { fn drop(&mut self) { - self.flag.set(BorrowFlag::UNUSED); + self.inner.set_borrow_flag(BorrowFlag::UNUSED) } } diff --git a/src/pyclass.rs b/src/pyclass.rs index 9ca01845..c9fd8130 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -2,7 +2,7 @@ use crate::class::methods::{PyMethodDefType, PyMethodsProtocol}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{type_flags, PyObjectLayout}; -use crate::{class, ffi, gil, PyCell, PyErr, PyResult, PyTypeInfo, Python}; +use crate::{class, ffi, gil, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python}; use std::ffi::CString; use std::os::raw::c_void; use std::ptr; @@ -10,9 +10,8 @@ use std::ptr; #[inline] pub(crate) unsafe fn default_alloc() -> *mut ffi::PyObject { let type_obj = T::type_object(); - if T::FLAGS & type_flags::EXTENDED != 0 - && ::ConcreteLayout::IS_NATIVE_TYPE - { + // if the class derives native types(e.g., PyDict), call special new + if T::FLAGS & type_flags::EXTENDED != 0 && T::BaseLayout::IS_NATIVE_TYPE { let base_tp = ::type_object(); if let Some(base_new) = base_tp.tp_new { return base_new(type_obj as *const _ as _, ptr::null_mut(), ptr::null_mut()); @@ -28,7 +27,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { /// /// # Safety /// This function must return a valid pointer to the Python heap. - unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout { + unsafe fn alloc(_py: Python) -> *mut Self::Layout { default_alloc::() as _ } @@ -36,7 +35,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { /// /// # Safety /// `self_` must be a valid pointer to the Python heap. - unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) { + unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); let obj = self_ as _; if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { @@ -72,10 +71,12 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { /// The `#[pyclass]` attribute automatically implements this trait for your Rust struct, /// so you don't have to use this trait directly. pub trait PyClass: - PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol + PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol { type Dict: PyClassDict; type WeakRef: PyClassWeakRef; + /// The closest native ancestor. + type BaseNativeType: PyTypeInfo + PyNativeType; } #[cfg(not(Py_LIMITED_API))] @@ -111,12 +112,12 @@ where { let py = Python::assume_gil_acquired(); let _pool = gil::GILPool::new_no_pointers(py); - ::dealloc(py, (obj as *mut T::ConcreteLayout) as _) + ::dealloc(py, (obj as *mut T::Layout) as _) } type_object.tp_dealloc = Some(tp_dealloc_callback::); // type size - type_object.tp_basicsize = std::mem::size_of::() as ffi::Py_ssize_t; + type_object.tp_basicsize = std::mem::size_of::() as ffi::Py_ssize_t; let mut offset = type_object.tp_basicsize; diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index daf59de3..33654ecf 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -8,7 +8,7 @@ use std::marker::PhantomData; /// This trait is intended to use internally for distinguishing `#[pyclass]` and /// Python native types. pub trait PyObjectInit: Sized { - fn init_class(self, layout: &mut T::ConcreteLayout); + fn init_class>(self, layout: &mut L); private_decl! {} } @@ -16,7 +16,7 @@ pub trait PyObjectInit: Sized { pub struct PyNativeTypeInitializer(PhantomData); impl PyObjectInit for PyNativeTypeInitializer { - fn init_class(self, _layout: &mut T::ConcreteLayout) {} + fn init_class>(self, _layout: &mut L) {} private_impl! {} } @@ -108,6 +108,7 @@ impl PyClassInitializer { pub fn add_subclass(self, subclass_value: S) -> PyClassInitializer where S: PyClass + PyTypeInfo, + S::BaseLayout: PyObjectSizedLayout, S::BaseType: PyTypeInfo, { PyClassInitializer::new(subclass_value, self) @@ -117,7 +118,7 @@ impl PyClassInitializer { pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell> where T: PyClass, - ::ConcreteLayout: PyObjectSizedLayout, + T::BaseLayout: PyObjectSizedLayout, { let cell = PyCell::internal_new(py)?; self.init_class(&mut *cell); @@ -126,12 +127,12 @@ impl PyClassInitializer { } impl PyObjectInit for PyClassInitializer { - fn init_class(self, obj: &mut T::ConcreteLayout) { + fn init_class>(self, layout: &mut L) { let Self { init, super_init } = self; unsafe { - obj.py_init(init); + layout.py_init(init); } - if let Some(super_obj) = obj.get_super_or() { + if let Some(super_obj) = layout.get_super_or() { super_init.init_class(super_obj); } } @@ -151,6 +152,7 @@ where impl From<(S, B)> for PyClassInitializer where S: PyClass + PyTypeInfo, + S::BaseLayout: PyObjectSizedLayout, B: PyClass + PyTypeInfo>, B::BaseType: PyTypeInfo>, { diff --git a/src/type_object.rs b/src/type_object.rs index eb9e5299..42ea897a 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -1,11 +1,10 @@ // Copyright (c) 2017-present PyO3 Project and Contributors //! Python type object information -use crate::instance::Py; use crate::pyclass::{initialize_type_object, PyClass}; use crate::pyclass_init::PyObjectInit; use crate::types::{PyAny, PyType}; -use crate::{ffi, AsPyPointer, Python}; +use crate::{ffi, AsPyPointer, Py, Python}; use std::cell::UnsafeCell; use std::ptr::NonNull; use std::sync::atomic::{AtomicBool, Ordering}; @@ -17,14 +16,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// This trait is intended to be used internally. pub unsafe trait PyObjectLayout { const IS_NATIVE_TYPE: bool = true; - - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { None } unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} unsafe fn unchecked_ref(&self) -> &T; - unsafe fn unchecked_refmut(&mut self) -> &mut T; + unsafe fn unchecked_refmut(&self) -> &mut T; } /// `T: PyObjectSizedLayout` represents `T` is not a instance of @@ -100,9 +98,14 @@ pub unsafe trait PyTypeInfo: Sized { type BaseType: PyTypeInfo + PyTypeObject; /// Layout - type ConcreteLayout: PyObjectLayout; + type Layout: PyObjectLayout; - /// This type is an abstraction layer for `AsPyRef`. + /// Layout of Basetype + type BaseLayout: PyObjectLayout; + + /// Abstraction layer for `AsPyRef`. + /// + /// Simply, it's `Self` for native types and `PyCell` for pyclasses. type Reference; /// Initializer for layout diff --git a/src/types/any.rs b/src/types/any.rs index a234e41b..fdfe7c58 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -27,8 +27,8 @@ unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { unsafe fn unchecked_ref(&self) -> &PyAny { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&mut self) -> &mut PyAny { - &mut *((&self) as *const &mut Self as *const _ as *mut _) + unsafe fn unchecked_refmut(&self) -> &mut PyAny { + &mut *((&self) as *const &Self as *const _ as *mut _) } } impl crate::type_object::PyObjectSizedLayout for ffi::PyObject {} diff --git a/src/types/mod.rs b/src/types/mod.rs index 79880313..e94306ea 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -62,8 +62,8 @@ macro_rules! impl_layout { unsafe fn unchecked_ref(&self) -> &$name { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&mut self) -> &mut $name { - &mut *((&self) as *const &mut Self as *const _ as *mut _) + unsafe fn unchecked_refmut(&self) -> &mut $name { + &mut *((&self) as *const &Self as *const _ as *mut _) } } }; @@ -74,6 +74,12 @@ macro_rules! pyobject_native_type { ($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => { impl_layout!($name, $layout); impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {} + impl $crate::derive_utils::PyBaseTypeUtils for $name { + type Dict = $crate::pyclass_slots::PyClassDummySlot; + type WeakRef = $crate::pyclass_slots::PyClassDummySlot; + type Layout = $crate::pycell::PyCellBase<$name>; + type BaseNativeType = $name; + } pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*); pyobject_native_type_extract!($name $(,$type_param)*); @@ -117,8 +123,10 @@ macro_rules! pyobject_native_var_type { // because rust-numpy has a special implementation. macro_rules! pyobject_native_type_extract { ($name: ty $(,$type_param: ident)*) => { - impl<$($type_param,)*> $crate::conversion::FromPyObjectImpl for &'_ $name { - type Impl = $crate::conversion::extract_impl::Reference; + impl<'py, $($type_param,)*> $crate::FromPyObject<'py> for &'py $name { + fn extract(obj: &'py crate::types::PyAny) -> $crate::PyResult { + $crate::PyTryFrom::try_from(obj).map_err(Into::into) + } } } } @@ -130,7 +138,8 @@ macro_rules! pyobject_native_type_convert( unsafe impl<$($type_param,)*> $crate::type_object::PyTypeInfo for $name { type Type = (); type BaseType = $crate::types::PyAny; - type ConcreteLayout = $layout; + type Layout = $layout; + type BaseLayout = ffi::PyObject; type Reference = $name; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; diff --git a/tests/test_class_new.rs b/tests/test_class_new.rs index ce3107e9..c225608f 100644 --- a/tests/test_class_new.rs +++ b/tests/test_class_new.rs @@ -19,7 +19,7 @@ fn empty_class_with_new() { assert!(typeobj .call((), None) .unwrap() - .cast_as::() + .cast_as::>() .is_ok()); } @@ -43,8 +43,9 @@ fn new_with_one_arg() { let py = gil.python(); let typeobj = py.get_type::(); let wrp = typeobj.call((42,), None).unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data, 42); + let obj = wrp.cast_as::>().unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref._data, 42); } #[pyclass] @@ -73,7 +74,8 @@ fn new_with_two_args() { .call((10, 20), None) .map_err(|e| e.print(py)) .unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data1, 10); - assert_eq!(obj._data2, 20); + let obj = wrp.cast_as::>().unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref._data1, 10); + assert_eq!(obj_ref._data2, 20); } diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs old mode 100755 new mode 100644 index 3790c73c..80efb364 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -53,11 +53,11 @@ struct Iterator { #[pyproto] impl<'p> PyIterProtocol for Iterator { - fn __iter__(slf: &mut PyCell) -> PyResult> { + fn __iter__(slf: PyRefMut) -> PyResult> { Ok(slf.into()) } - fn __next__(slf: &mut PyCell) -> PyResult> { + fn __next__(slf: PyRefMut) -> PyResult> { Ok(slf.iter.next()) } } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 1f8de776..6cd11f38 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -3,7 +3,7 @@ use pyo3::class::PyTraverseError; use pyo3::class::PyVisit; use pyo3::prelude::*; use pyo3::types::{PyAny, PyTuple}; -use pyo3::{ffi, py_run, AsPyPointer, PyCell}; +use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto}; use std::cell::RefCell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -153,7 +153,7 @@ fn gc_integration() { { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_mut( + let inst = PyCell::new( py, GCIntegration { self_ref: RefCell::new(py.None()), @@ -164,7 +164,8 @@ fn gc_integration() { ) .unwrap(); - *inst.self_ref.borrow_mut() = inst.to_object(py); + let borrow = inst.borrow_mut(); + *borrow.self_ref.borrow_mut() = inst.to_object(py); } let gil = Python::acquire_gil(); @@ -188,7 +189,7 @@ impl PyGCProtocol for GCIntegration2 { fn gc_integration2() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, GCIntegration2 {}).unwrap(); + let inst = PyCell::new(py, GCIntegration2 {}).unwrap(); py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); } @@ -199,7 +200,7 @@ struct WeakRefSupport {} fn weakref_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, WeakRefSupport {}).unwrap(); + let inst = PyCell::new(py, WeakRefSupport {}).unwrap(); py_run!( py, inst, @@ -264,12 +265,11 @@ fn inheritance_with_new_methods_with_drop() { let typeobj = py.get_type::(); let inst = typeobj.call((), None).unwrap(); - let obj = SubClassWithDrop::try_from_mut(inst).unwrap(); - obj.data = Some(Arc::clone(&drop_called1)); - - let base: &mut ::BaseType = - unsafe { py.mut_from_borrowed_ptr(inst.as_ptr()) }; - base.data = Some(Arc::clone(&drop_called2)); + let obj: &PyCell = inst.try_into().unwrap(); + let mut obj_ref_mut = obj.borrow_mut(); + obj_ref_mut.data = Some(Arc::clone(&drop_called1)); + let super_ = obj_ref_mut.get_super_mut(); + super_.data = Some(Arc::clone(&drop_called2)); } assert!(drop_called1.load(Ordering::Relaxed)); diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 9b97aa6b..500bf5b0 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -64,7 +64,7 @@ impl PySequenceProtocol for ByteSequence { } } - fn __concat__(&self, other: &Self) -> PyResult { + fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult { let mut elements = self.elements.clone(); elements.extend_from_slice(&other.elements); Ok(Self { elements })