Fix PyCell to share BorrowFlag with parents

This commit is contained in:
kngwyu 2020-02-15 13:42:25 +09:00
parent 4a5f219cc4
commit da4ed398bb
23 changed files with 416 additions and 278 deletions

View file

@ -35,6 +35,7 @@ pub enum FnType {
#[derive(Clone, PartialEq, Debug)]
pub struct FnSpec<'a> {
pub tp: FnType,
pub self_: Option<bool>,
// 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::<pyo3::PyErr>.restore_and_null(_py),
}
}
} else {
quote! {
match _slf.try_borrow_mut() {
Ok(ref_) => ref_,
Err(e) => return e.into::<pyo3::PyErr>.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,

View file

@ -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<method::FnArg<'a>> {
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<pyfunction::Argument>,
) -> 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,

View file

@ -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! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::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! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::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! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::Layout }
} else {
quote! { pyo3::pycell::PyCellBase<pyo3::types::PyAny> }
};
let base_nativetype = if attr.has_extends {
quote! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::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<Self>;
type Layout = pyo3::pycell::PyCell<Self>;
type BaseLayout = #base_layout;
type Initializer = pyo3::pyclass_init::PyClassInitializer<Self>;
type Reference = pyo3::pycell::PyCell<Self>;
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(),

View file

@ -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(&quote! { &mut #cls });
let borrow_self = spec.borrow_self();
let slf = quote! {
let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_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::<pyo3::PyCell<#cls>>(_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::<pyo3::PyCell<#cls>>(_slf);
let _slf = #borrow_self;
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_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<To
quote! { _slf.#name() }
};
let borrow_self = spec.borrow_self();
Ok(quote! {
unsafe extern "C" fn __wrap(
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
@ -274,7 +288,8 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result<To
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::<pyo3::PyCell<#cls>>(_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::<pyo3::PyCell<#cls>>(_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<T: quote::ToTokens>(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 {

View file

@ -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>) -> Self::Result
fn __iter__(slf: PyRefMut<Self>) -> Self::Result
where
Self: PyIterIterProtocol<'p>,
{
unimplemented!()
}
fn __next__(slf: &mut PyCell<Self>) -> Self::Result
fn __next__(slf: PyRefMut<Self>) -> Self::Result
where
Self: PyIterNextProtocol<'p>,
{
@ -77,7 +77,7 @@ where
{
#[inline]
fn tp_iter() -> Option<ffi::getiterfunc> {
py_unary_pyref_func!(
py_unary_refmut_func!(
PyIterIterProtocol,
T::__iter__,
T::Success,
@ -105,7 +105,7 @@ where
{
#[inline]
fn tp_iternext() -> Option<ffi::iternextfunc> {
py_unary_pyref_func!(
py_unary_refmut_func!(
PyIterNextProtocol,
T::__next__,
Option<T::Success>,

View file

@ -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<T>(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<T> = &mut *(slf as *mut PyCell<T>);
let res = $class::$f(slf).into();
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let res = $class::$f(slf.borrow_mut()).into();
$crate::callback::cb_convert($conv, py, res)
}
Some(wrap::<$class>)

View file

@ -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<Target>;
impl<'a, T> FromPyObject<'a> for &'a PyCell<T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
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<T> {
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,
<T as FromPyObjectImpl>::Impl: ExtractImpl<'a, T>,
T: PyClass + Clone,
{
#[inline]
fn extract(ob: &'a PyAny) -> PyResult<T> {
<T as FromPyObjectImpl>::Impl::extract(ob)
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<Self> = 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<Self> {
let cell: &PyCell<T> = 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<Self> {
let cell: &PyCell<T> = 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<V: Into<&'v PyAny>>(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<T>: 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<U> PyTryInto<U> 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<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
@ -387,6 +369,36 @@ where
}
}
impl<'v, T> PyTryFrom<'v> for PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(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<V: Into<&'v PyAny>>(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<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
}
}
/// Converts `()` to an empty Python tuple.
impl FromPy<()> for Py<PyTuple> {
fn from_py(_: (), py: Python) -> Py<PyTuple> {
@ -449,6 +461,21 @@ where
}
}
unsafe impl<'p, T> FromPyPointer<'p> for PyCell<T>
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<T>))
}
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<T>))
}
}
#[cfg(test)]
mod test {
use crate::types::PyList;

View file

@ -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<T: PyClass, I: Into<PyClassInitializer<T>>> IntoPyNewResult<T, I> for PyRes
self
}
}
/// Utitlities for basetype
pub trait PyBaseTypeUtils {
type Dict;
type WeakRef;
type Layout;
type BaseNativeType;
}
impl<T: PyClass> PyBaseTypeUtils for T {
type Dict = T::Dict;
type WeakRef = T::WeakRef;
type Layout = crate::pycell::PyCellInner<T>;
type BaseNativeType = T::BaseNativeType;
}
pub trait PySelf<'a, T: PyClass>: Sized {
fn from_cell(cell: &'a crate::PyCell<T>) -> PyResult<Self>;
}
impl<'a, T: PyClass> PySelf<'a, T> for &'a crate::PyCell<T> {
fn from_cell(cell: &'a crate::PyCell<T>) -> PyResult<Self> {
Ok(cell)
}
}
impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRef<'a, T> {
fn from_cell(cell: &'a crate::PyCell<T>) -> PyResult<Self> {
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<T>) -> PyResult<Self> {
cell.try_borrow_mut().map_err(Into::into)
}
}

View file

@ -59,9 +59,6 @@ pub type PyResult<T> = Result<T, PyErr>;
/// 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

View file

@ -72,7 +72,7 @@ impl<T> 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) = <Self as PyClassWithFreeList>::get_free_list().pop() {
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::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 _;

View file

@ -39,8 +39,7 @@ impl<T> Py<T> {
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>>
where
T: PyClass,
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
let initializer = value.into();
let obj = unsafe { initializer.create_cell(py)? };

View file

@ -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;

View file

@ -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;

View file

@ -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<T: PyTypeInfo> {
ob_base: T::Layout,
borrow_flag: Cell<BorrowFlag>,
}
unsafe impl<T> PyObjectLayout<T> for PyCellBase<T>
where
T: PyTypeInfo + PyNativeType,
T::Layout: PyObjectSizedLayout<T>,
{
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<T> PyObjectSizedLayout<T> for PyCellBase<T>
where
T: PyTypeInfo + PyNativeType,
T::Layout: PyObjectSizedLayout<T>,
{
}
/// 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<T: PyClass> {
ob_base: <T::BaseType as PyTypeInfo>::ConcreteLayout,
pub struct PyCellInner<T: PyClass> {
ob_base: T::BaseLayout,
value: ManuallyDrop<UnsafeCell<T>>,
}
impl<T: PyClass> AsPyPointer for PyCellBase<T> {
impl<T: PyClass> AsPyPointer for PyCellInner<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
}
}
unsafe impl<T: PyClass> PyObjectLayout<T> for PyCellBase<T> {
unsafe impl<T: PyClass> PyObjectLayout<T> for PyCellInner<T> {
const IS_NATIVE_TYPE: bool = false;
fn get_super_or(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::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<T: PyClass> PyObjectLayout<T> for PyCellBase<T> {
}
}
// This impl ensures `PyCellInner` can be a base type.
impl<T: PyClass> PyObjectSizedLayout<T> for PyCellInner<T> {}
impl<T: PyClass> PyCellInner<T> {
fn get_borrow_flag(&self) -> BorrowFlag {
let base = (&self.ob_base) as *const _ as *const PyCellBase<T::BaseNativeType>;
unsafe { (*base).borrow_flag.get() }
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
let base = (&self.ob_base) as *const _ as *const PyCellBase<T::BaseNativeType>;
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<T: PyClass> PyObjectLayout<T> for PyCellBase<T> {
/// ```
#[repr(C)]
pub struct PyCell<T: PyClass> {
base: PyCellBase<T>,
borrow_flag: Cell<BorrowFlag>,
inner: PyCellInner<T>,
dict: T::Dict,
weakref: T::WeakRef,
}
@ -84,8 +130,7 @@ impl<T: PyClass> PyCell<T> {
/// Make new `PyCell` on the Python heap and returns the reference of it.
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
unsafe {
let initializer = value.into();
@ -103,57 +148,51 @@ impl<T: PyClass> PyCell<T> {
}
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, 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<PyRefMut<'_, T>, 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
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
let base = T::alloc(py);
if base.is_null() {
return Err(PyErr::fetch(py));
}
let base = base as *mut PyCellBase<T::BaseNativeType>;
(*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<T: PyClass> PyCell<T> {
unsafe impl<T: PyClass> PyObjectLayout<T> for PyCell<T> {
const IS_NATIVE_TYPE: bool = false;
fn get_super_or(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::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<T> {
private_impl! {}
}
impl<T: PyClass> PyObjectSizedLayout<T> for PyCell<T> {}
impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
@ -203,35 +240,13 @@ impl<T: PyClass> ToPyObject for &PyCell<T> {
}
}
impl<T: PyClass> ToPyObject for &mut PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
unsafe impl<'p, T> FromPyPointer<'p> for PyCell<T>
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<T>))
}
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<T>))
}
}
pub struct PyRef<'p, T: PyClass> {
value: &'p PyCellBase<T>,
flag: &'p Cell<BorrowFlag>,
inner: &'p PyCellInner<T>,
}
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<T>,
flag: &'p Cell<BorrowFlag>,
inner: &'p PyCellInner<T>,
}
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)
}
}

View file

@ -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<T: PyTypeInfo>() -> *mut ffi::PyObject {
let type_obj = T::type_object();
if T::FLAGS & type_flags::EXTENDED != 0
&& <T::BaseType as PyTypeInfo>::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 = <T::BaseType as PyTypeInfo>::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::<Self>() 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<ConcreteLayout = PyCell<Self>> + Sized + PyClassAlloc + PyMethodsProtocol
PyTypeInfo<Layout = PyCell<Self>> + 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);
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::ConcreteLayout) as _)
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::Layout) as _)
}
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
// type size
type_object.tp_basicsize = std::mem::size_of::<T::ConcreteLayout>() as ffi::Py_ssize_t;
type_object.tp_basicsize = std::mem::size_of::<T::Layout>() as ffi::Py_ssize_t;
let mut offset = type_object.tp_basicsize;

View file

@ -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<T: PyTypeInfo>: Sized {
fn init_class(self, layout: &mut T::ConcreteLayout);
fn init_class<L: PyObjectLayout<T>>(self, layout: &mut L);
private_decl! {}
}
@ -16,7 +16,7 @@ pub trait PyObjectInit<T: PyTypeInfo>: Sized {
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
fn init_class(self, _layout: &mut T::ConcreteLayout) {}
fn init_class<L: PyObjectLayout<T>>(self, _layout: &mut L) {}
private_impl! {}
}
@ -108,6 +108,7 @@ impl<T: PyClass> PyClassInitializer<T> {
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = T>,
S::BaseLayout: PyObjectSizedLayout<T>,
S::BaseType: PyTypeInfo<Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
@ -117,7 +118,7 @@ impl<T: PyClass> PyClassInitializer<T> {
pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell<T>>
where
T: PyClass,
<T::BaseType as PyTypeInfo>::ConcreteLayout: PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: PyObjectSizedLayout<T::BaseType>,
{
let cell = PyCell::internal_new(py)?;
self.init_class(&mut *cell);
@ -126,12 +127,12 @@ impl<T: PyClass> PyClassInitializer<T> {
}
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
fn init_class(self, obj: &mut T::ConcreteLayout) {
fn init_class<L: PyObjectLayout<T>>(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<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = B>,
S::BaseLayout: PyObjectSizedLayout<B>,
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{

View file

@ -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<T: PyTypeInfo> {
const IS_NATIVE_TYPE: bool = true;
fn get_super_or(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::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<U>` represents `T` is not a instance of
@ -100,9 +98,14 @@ pub unsafe trait PyTypeInfo: Sized {
type BaseType: PyTypeInfo + PyTypeObject;
/// Layout
type ConcreteLayout: PyObjectLayout<Self>;
type Layout: PyObjectLayout<Self>;
/// This type is an abstraction layer for `AsPyRef`.
/// Layout of Basetype
type BaseLayout: PyObjectLayout<Self::BaseType>;
/// Abstraction layer for `AsPyRef`.
///
/// Simply, it's `Self` for native types and `PyCell<Self>` for pyclasses.
type Reference;
/// Initializer for layout

View file

@ -27,8 +27,8 @@ unsafe impl crate::type_object::PyObjectLayout<PyAny> 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<PyAny> for ffi::PyObject {}

View file

@ -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<Self> {
$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<Self>;

View file

@ -19,7 +19,7 @@ fn empty_class_with_new() {
assert!(typeobj
.call((), None)
.unwrap()
.cast_as::<EmptyClassWithNew>()
.cast_as::<PyCell<EmptyClassWithNew>>()
.is_ok());
}
@ -43,8 +43,9 @@ fn new_with_one_arg() {
let py = gil.python();
let typeobj = py.get_type::<NewWithOneArg>();
let wrp = typeobj.call((42,), None).unwrap();
let obj = wrp.cast_as::<NewWithOneArg>().unwrap();
assert_eq!(obj._data, 42);
let obj = wrp.cast_as::<PyCell<NewWithOneArg>>().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::<NewWithTwoArgs>().unwrap();
assert_eq!(obj._data1, 10);
assert_eq!(obj._data2, 20);
let obj = wrp.cast_as::<PyCell<NewWithTwoArgs>>().unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref._data1, 10);
assert_eq!(obj_ref._data2, 20);
}

4
tests/test_dunder.rs Executable file → Normal file
View file

@ -53,11 +53,11 @@ struct Iterator {
#[pyproto]
impl<'p> PyIterProtocol for Iterator {
fn __iter__(slf: &mut PyCell<Self>) -> PyResult<Py<Iterator>> {
fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Iterator>> {
Ok(slf.into())
}
fn __next__(slf: &mut PyCell<Self>) -> PyResult<Option<i32>> {
fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<i32>> {
Ok(slf.iter.next())
}
}

View file

@ -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::<SubClassWithDrop>();
let inst = typeobj.call((), None).unwrap();
let obj = SubClassWithDrop::try_from_mut(inst).unwrap();
obj.data = Some(Arc::clone(&drop_called1));
let base: &mut <SubClassWithDrop as pyo3::PyTypeInfo>::BaseType =
unsafe { py.mut_from_borrowed_ptr(inst.as_ptr()) };
base.data = Some(Arc::clone(&drop_called2));
let obj: &PyCell<SubClassWithDrop> = 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));

View file

@ -64,7 +64,7 @@ impl PySequenceProtocol for ByteSequence {
}
}
fn __concat__(&self, other: &Self) -> PyResult<Self> {
fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult<Self> {
let mut elements = self.elements.clone();
elements.extend_from_slice(&other.elements);
Ok(Self { elements })