pymethods: implement more mapping methods

This commit is contained in:
David Hewitt 2021-09-10 12:01:25 +01:00
parent fda18b07d7
commit 5210517695
3 changed files with 335 additions and 281 deletions

View file

@ -599,6 +599,9 @@ fn impl_class(
if let ::std::option::Option::Some(setdescr) = ::pyo3::generate_pyclass_setdescr_slot!(#cls) {
generated_slots.push(setdescr);
}
if let ::std::option::Option::Some(setdescr) = ::pyo3::generate_pyclass_setitem_slot!(#cls) {
generated_slots.push(setdescr);
}
visitor(&generated_slots);
}

View file

@ -10,7 +10,7 @@ use crate::{
pyfunction::PyFunctionOptions,
};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use quote::{format_ident, quote, ToTokens};
use syn::Ident;
use syn::{ext::IdentExt, spanned::Spanned, Result};
@ -31,8 +31,9 @@ pub fn gen_py_method(
ensure_function_options_valid(&options)?;
let spec = FnSpec::parse(sig, &mut *meth_attrs, options)?;
if let Some(proto) = pyproto(cls, &spec) {
return Ok(GeneratedPyMethod::Proto(proto));
if let Some(slot_def) = pyproto(&spec.python_name.to_string()) {
let slot = slot_def.generate_type_slot(cls, &spec);
return Ok(GeneratedPyMethod::Proto(slot));
}
if let Some(proto) = pyproto_fragment(cls, &spec)? {
@ -374,76 +375,65 @@ impl PropertyType<'_> {
}
}
fn pyproto(cls: &syn::Type, spec: &FnSpec) -> Option<TokenStream> {
match spec.python_name.to_string().as_str() {
"__getattr__" => Some(
SlotDef::new("Py_tp_getattro", "getattrofunc")
.arguments(&[Ty::Object])
.before_call_method(quote! {
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
// for existing fields and methods first
let existing = ::pyo3::ffi::PyObject_GenericGetAttr(_slf, arg0);
if existing.is_null() {
// PyObject_HasAttr also tries to get an object and clears the error if it fails
::pyo3::ffi::PyErr_Clear();
} else {
return existing;
}
})
.generate_type_slot(cls, spec),
),
"__str__" => Some(SlotDef::new("Py_tp_str", "reprfunc").generate_type_slot(cls, spec)),
"__repr__" => Some(SlotDef::new("Py_tp_repr", "reprfunc").generate_type_slot(cls, spec)),
"__hash__" => Some(
SlotDef::new("Py_tp_hash", "hashfunc")
.ret_ty(Ty::PyHashT)
.return_conversion(quote! { ::pyo3::callback::HashCallbackOutput })
.generate_type_slot(cls, spec),
),
"__richcmp__" => Some(
SlotDef::new("Py_tp_richcompare", "richcmpfunc")
.arguments(&[Ty::Object, Ty::CompareOp])
.generate_type_slot(cls, spec),
),
"__bool__" => Some(
SlotDef::new("Py_nb_bool", "inquiry")
.ret_ty(Ty::Int)
.generate_type_slot(cls, spec),
),
"__get__" => Some(
SlotDef::new("Py_tp_descr_get", "descrgetfunc")
.arguments(&[Ty::Object, Ty::Object])
.generate_type_slot(cls, spec),
),
"__iter__" => Some(SlotDef::new("Py_tp_iter", "getiterfunc").generate_type_slot(cls, spec)),
"__next__" => Some(
SlotDef::new("Py_tp_iternext", "iternextfunc")
.return_conversion(quote! { ::pyo3::class::iter::IterNextOutput::<_, _> })
.generate_type_slot(cls, spec),
),
"__await__" => Some(SlotDef::new("Py_am_await", "unaryfunc").generate_type_slot(cls, spec)),
"__aiter__" => Some(SlotDef::new("Py_am_aiter", "unaryfunc").generate_type_slot(cls, spec)),
"__anext__" => Some(
SlotDef::new("Py_am_anext", "unaryfunc")
.return_conversion(quote! { ::pyo3::class::pyasync::IterANextOutput::<_, _> })
.generate_type_slot(cls, spec),
),
"__len__" => Some(
SlotDef::new("Py_mp_length", "lenfunc")
.ret_ty(Ty::PySsizeT)
.generate_type_slot(cls, spec),
),
"__contains__" => Some(
SlotDef::new("Py_sq_contains", "objobjproc")
.arguments(&[Ty::Object])
.ret_ty(Ty::Int)
.generate_type_slot(cls, spec),
),
"__getitem__" => Some(
SlotDef::new("Py_mp_subscript", "binaryfunc")
.arguments(&[Ty::Object])
.generate_type_slot(cls, spec),
),
const __GETATTR__: SlotDef = SlotDef::new("Py_tp_getattro", "getattrofunc")
.arguments(&[Ty::Object])
.before_call_method(TokenGenerator(|| {
quote! {
// Behave like python's __getattr__ (as opposed to __getattribute__) and check
// for existing fields and methods first
let existing = ::pyo3::ffi::PyObject_GenericGetAttr(_slf, arg0);
if existing.is_null() {
// PyObject_HasAttr also tries to get an object and clears the error if it fails
::pyo3::ffi::PyErr_Clear();
} else {
return existing;
}
}
}));
const __STR__: SlotDef = SlotDef::new("Py_tp_str", "reprfunc");
const __REPR__: SlotDef = SlotDef::new("Py_tp_repr", "reprfunc");
const __HASH__: SlotDef = SlotDef::new("Py_tp_hash", "hashfunc")
.ret_ty(Ty::PyHashT)
.return_conversion(TokenGenerator(
|| quote! { ::pyo3::callback::HashCallbackOutput },
));
const __RICHCMP__: SlotDef =
SlotDef::new("Py_tp_richcompare", "richcmpfunc").arguments(&[Ty::Object, Ty::CompareOp]);
const __BOOL__: SlotDef = SlotDef::new("Py_nb_bool", "inquiry").ret_ty(Ty::Int);
const __GET__: SlotDef =
SlotDef::new("Py_tp_descr_get", "descrgetfunc").arguments(&[Ty::Object, Ty::Object]);
const __ITER__: SlotDef = SlotDef::new("Py_tp_iter", "getiterfunc");
const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc").return_conversion(
TokenGenerator(|| quote! { ::pyo3::class::iter::IterNextOutput::<_, _> }),
);
const __AWAIT__: SlotDef = SlotDef::new("Py_am_await", "unaryfunc");
const __AITER__: SlotDef = SlotDef::new("Py_am_aiter", "unaryfunc");
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_conversion(
TokenGenerator(|| quote! { ::pyo3::class::pyasync::IterANextOutput::<_, _> }),
);
const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT);
const __CONTAINS__: SlotDef = SlotDef::new("Py_sq_contains", "objobjproc")
.arguments(&[Ty::Object])
.ret_ty(Ty::Int);
const __GETITEM__: SlotDef = SlotDef::new("Py_mp_subscript", "binaryfunc").arguments(&[Ty::Object]);
fn pyproto(method_name: &str) -> Option<&'static SlotDef> {
match method_name {
"__getattr__" => Some(&__GETATTR__),
"__str__" => Some(&__STR__),
"__repr__" => Some(&__REPR__),
"__hash__" => Some(&__HASH__),
"__richcmp__" => Some(&__RICHCMP__),
"__bool__" => Some(&__BOOL__),
"__get__" => Some(&__GET__),
"__iter__" => Some(&__ITER__),
"__next__" => Some(&__NEXT__),
"__await__" => Some(&__AWAIT__),
"__aiter__" => Some(&__AITER__),
"__anext__" => Some(&__ANEXT__),
"__len__" => Some(&__LEN__),
"__contains__" => Some(&__CONTAINS__),
"__getitem__" => Some(&__GETITEM__),
_ => None,
}
}
@ -544,19 +534,19 @@ fn extract_from_any(self_: &syn::Type, target: &syn::Type, ident: &syn::Ident) -
}
struct SlotDef {
slot: syn::Ident,
func_ty: syn::Ident,
slot: StaticIdent,
func_ty: StaticIdent,
arguments: &'static [Ty],
ret_ty: Ty,
before_call_method: Option<TokenStream>,
return_conversion: Option<TokenStream>,
before_call_method: Option<TokenGenerator>,
return_conversion: Option<TokenGenerator>,
}
impl SlotDef {
fn new(slot: &str, func_ty: &str) -> Self {
const fn new(slot: &'static str, func_ty: &'static str) -> Self {
SlotDef {
slot: syn::Ident::new(slot, Span::call_site()),
func_ty: syn::Ident::new(func_ty, Span::call_site()),
slot: StaticIdent(slot),
func_ty: StaticIdent(func_ty),
arguments: &[],
ret_ty: Ty::Object,
before_call_method: None,
@ -564,22 +554,22 @@ impl SlotDef {
}
}
fn arguments(mut self, arguments: &'static [Ty]) -> Self {
const fn arguments(mut self, arguments: &'static [Ty]) -> Self {
self.arguments = arguments;
self
}
fn ret_ty(mut self, ret_ty: Ty) -> Self {
const fn ret_ty(mut self, ret_ty: Ty) -> Self {
self.ret_ty = ret_ty;
self
}
fn before_call_method(mut self, before_call_method: TokenStream) -> Self {
const fn before_call_method(mut self, before_call_method: TokenGenerator) -> Self {
self.before_call_method = Some(before_call_method);
self
}
fn return_conversion(mut self, return_conversion: TokenStream) -> Self {
const fn return_conversion(mut self, return_conversion: TokenGenerator) -> Self {
self.return_conversion = Some(return_conversion);
self
}
@ -594,34 +584,13 @@ impl SlotDef {
return_conversion,
} = self;
let py = syn::Ident::new("_py", Span::call_site());
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let arguments = arguments.into_iter().enumerate().map(|(i, arg)| {
let ident = syn::Ident::new(&format!("arg{}", i), Span::call_site());
let ffi_type = arg.ffi_type();
quote! {
#ident: #ffi_type
}
});
let method_arguments = generate_method_arguments(arguments);
let ret_ty = ret_ty.ffi_type();
let (arg_idents, conversions) =
extract_proto_arguments(cls, &py, &spec.args, &self.arguments);
let call =
quote! { ::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*)) };
let body = if let Some(return_conversion) = return_conversion {
quote! {
let _result: PyResult<#return_conversion> = #call;
::pyo3::callback::convert(#py, _result)
}
} else {
call
};
let body = generate_method_body(cls, spec, &py, arguments, return_conversion.as_ref());
quote!({
unsafe extern "C" fn __wrap(_slf: *mut ::pyo3::ffi::PyObject, #(#arguments),*) -> #ret_ty {
unsafe extern "C" fn __wrap(_slf: *mut ::pyo3::ffi::PyObject, #(#method_arguments),*) -> #ret_ty {
#before_call_method
::pyo3::callback::handle_panic(|#py| {
#self_conversion
#conversions
#body
})
}
@ -633,94 +602,110 @@ impl SlotDef {
}
}
fn generate_method_arguments(arguments: &[Ty]) -> impl Iterator<Item = TokenStream> + '_ {
arguments.into_iter().enumerate().map(|(i, arg)| {
let ident = syn::Ident::new(&format!("arg{}", i), Span::call_site());
let ffi_type = arg.ffi_type();
quote! {
#ident: #ffi_type
}
})
}
fn generate_method_body(
cls: &syn::Type,
spec: &FnSpec,
py: &syn::Ident,
arguments: &[Ty],
return_conversion: Option<&TokenGenerator>,
) -> TokenStream {
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let (arg_idents, conversions) = extract_proto_arguments(cls, &py, &spec.args, arguments);
let call = quote! { ::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*)) };
let body = if let Some(return_conversion) = return_conversion {
quote! {
let _result: PyResult<#return_conversion> = #call;
::pyo3::callback::convert(#py, _result)
}
} else {
call
};
quote! {
#self_conversion
#conversions
#body
}
}
fn generate_pyproto_fragment(
cls: &syn::Type,
spec: &FnSpec,
fragment: &str,
arguments: &[Ty],
) -> TokenStream {
let fragment_trait = format_ident!("PyClass{}SlotFragment", fragment);
let implemented = format_ident!("{}implemented", fragment);
let method = syn::Ident::new(fragment, Span::call_site());
let py = syn::Ident::new("_py", Span::call_site());
let method_arguments = generate_method_arguments(arguments);
let body = generate_method_body(cls, spec, &py, arguments, None);
quote! {
impl ::pyo3::class::impl_::#fragment_trait<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> {
#[inline]
fn #implemented(self) -> bool { true }
#[inline]
unsafe fn #method(
self,
_slf: *mut ::pyo3::ffi::PyObject,
#(#method_arguments),*
) -> ::pyo3::PyResult<()> {
let #py = ::pyo3::Python::assume_gil_acquired();
#body
}
}
}
}
fn pyproto_fragment(cls: &syn::Type, spec: &FnSpec) -> Result<Option<TokenStream>> {
Ok(match spec.python_name.to_string().as_str() {
"__setattr__" => {
let py = syn::Ident::new("_py", Span::call_site());
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let (arg_idents, conversions) =
extract_proto_arguments(cls, &py, &spec.args, &[Ty::Object, Ty::NonNullObject]);
Some(quote! {
impl ::pyo3::class::impl_::PyClassSetattrSlotFragment<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> {
#[inline]
fn setattr_implemented(self) -> bool { true }
#[inline]
unsafe fn setattr(
self,
_slf: *mut ::pyo3::ffi::PyObject,
arg0: *mut ::pyo3::ffi::PyObject,
arg1: ::std::ptr::NonNull<::pyo3::ffi::PyObject>
) -> ::pyo3::PyResult<()> {
let #py = ::pyo3::Python::assume_gil_acquired();
#self_conversion
#conversions
::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*))
}
}
})
}
"__delattr__" => {
let py = syn::Ident::new("_py", Span::call_site());
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let (arg_idents, conversions) =
extract_proto_arguments(cls, &py, &spec.args, &[Ty::Object]);
Some(quote! {
impl ::pyo3::class::impl_::PyClassDelattrSlotFragment<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> {
fn delattr_impl(self) -> ::std::option::Option<unsafe fn (_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject) -> ::pyo3::PyResult<()>> {
unsafe fn __wrap(_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject) -> ::pyo3::PyResult<()> {
let #py = ::pyo3::Python::assume_gil_acquired();
#self_conversion
#conversions
::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*))
}
Some(__wrap)
}
}
})
}
"__set__" => {
let py = syn::Ident::new("_py", Span::call_site());
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let (arg_idents, conversions) =
extract_proto_arguments(cls, &py, &spec.args, &[Ty::Object, Ty::NonNullObject]);
Some(quote! {
impl ::pyo3::class::impl_::PyClassSetSlotFragment<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> {
fn set_impl(self) -> ::std::option::Option<unsafe fn (_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject, arg1: ::std::ptr::NonNull<::pyo3::ffi::PyObject>) -> ::pyo3::PyResult<()>> {
unsafe fn __wrap(_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject, arg1: ::std::ptr::NonNull<::pyo3::ffi::PyObject>) -> ::pyo3::PyResult<()> {
let #py = ::pyo3::Python::assume_gil_acquired();
#self_conversion
#conversions
::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*))
}
Some(__wrap)
}
}
})
}
"__delete__" => {
let py = syn::Ident::new("_py", Span::call_site());
let self_conversion = spec.tp.self_conversion(Some(cls));
let rust_name = spec.name;
let (arg_idents, conversions) =
extract_proto_arguments(cls, &py, &spec.args, &[Ty::Object]);
Some(quote! {
impl ::pyo3::class::impl_::PyClassDeleteSlotFragment<#cls> for ::pyo3::class::impl_::PyClassImplCollector<#cls> {
fn delete_impl(self) -> ::std::option::Option<unsafe fn (_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject) -> ::pyo3::PyResult<()>> {
unsafe fn __wrap(_slf: *mut ::pyo3::ffi::PyObject, arg0: *mut ::pyo3::ffi::PyObject) -> ::pyo3::PyResult<()> {
let #py = ::pyo3::Python::assume_gil_acquired();
#self_conversion
#conversions
::pyo3::callback::convert(#py, #cls::#rust_name(_slf, #(#arg_idents),*))
}
Some(__wrap)
}
}
})
}
"__setattr__" => Some(generate_pyproto_fragment(
cls,
spec,
"__setattr__",
&[Ty::Object, Ty::NonNullObject],
)),
"__delattr__" => Some(generate_pyproto_fragment(
cls,
spec,
"__delattr__",
&[Ty::Object],
)),
"__set__" => Some(generate_pyproto_fragment(
cls,
spec,
"__set__",
&[Ty::Object, Ty::NonNullObject],
)),
"__delete__" => Some(generate_pyproto_fragment(
cls,
spec,
"__delete__",
&[Ty::Object],
)),
"__setitem__" => Some(generate_pyproto_fragment(
cls,
spec,
"__setitem__",
&[Ty::Object, Ty::NonNullObject],
)),
"__delitem__" => Some(generate_pyproto_fragment(
cls,
spec,
"__delitem__",
&[Ty::Object],
)),
_ => None,
})
}
@ -749,3 +734,19 @@ fn extract_proto_arguments(
let conversions = quote!(#(#args_conversion)*);
(arg_idents, conversions)
}
struct StaticIdent(&'static str);
impl ToTokens for StaticIdent {
fn to_tokens(&self, tokens: &mut TokenStream) {
syn::Ident::new(self.0, Span::call_site()).to_tokens(tokens)
}
}
struct TokenGenerator(fn() -> TokenStream);
impl ToTokens for TokenGenerator {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0().to_tokens(tokens)
}
}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::{
exceptions::PyAttributeError,
exceptions::{PyAttributeError, PyNotImplementedError},
ffi,
impl_::freelist::FreeList,
pycell::PyCellLayout,
@ -108,23 +108,16 @@ impl<T> PyClassCallImpl<T> for &'_ PyClassImplCollector<T> {
}
}
pub trait PyClassSetattrSlotFragment<T>: Sized {
#[allow(non_camel_case_types)]
pub trait PyClass__setattr__SlotFragment<T>: Sized {
#[inline]
fn setattr_implemented(self) -> bool {
#[allow(non_snake_case)]
fn __setattr__implemented(self) -> bool {
false
}
unsafe fn setattr(
self,
_slf: *mut ffi::PyObject,
attr: *mut ffi::PyObject,
value: NonNull<ffi::PyObject>,
) -> PyResult<()>;
}
impl<T> PyClassSetattrSlotFragment<T> for &'_ PyClassImplCollector<T> {
#[inline]
unsafe fn setattr(
unsafe fn __setattr__(
self,
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
@ -134,20 +127,28 @@ impl<T> PyClassSetattrSlotFragment<T> for &'_ PyClassImplCollector<T> {
}
}
pub trait PyClassDelattrSlotFragment<T> {
fn delattr_impl(
self,
) -> Option<unsafe fn(_slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> PyResult<()>>;
}
impl<T> PyClass__setattr__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
impl<T> PyClassDelattrSlotFragment<T> for &'_ PyClassImplCollector<T> {
fn delattr_impl(
#[allow(non_camel_case_types)]
pub trait PyClass__delattr__SlotFragment<T>: Sized {
#[inline]
#[allow(non_snake_case)]
fn __delattr__implemented(self) -> bool {
false
}
#[inline]
unsafe fn __delattr__(
self,
) -> Option<unsafe fn(_slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> PyResult<()>> {
None
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
) -> PyResult<()> {
Err(PyAttributeError::new_err("can't delete attribute"))
}
}
impl<T> PyClass__delattr__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
#[doc(hidden)]
#[macro_export]
macro_rules! generate_pyclass_setattr_slot {
@ -155,28 +156,19 @@ macro_rules! generate_pyclass_setattr_slot {
use ::std::option::Option::*;
use $crate::class::impl_::*;
let collector = PyClassImplCollector::<$cls>::new();
let delattr = collector.delattr_impl();
if collector.setattr_implemented() || delattr.is_some() {
if collector.__setattr__implemented() || collector.__delattr__implemented() {
unsafe extern "C" fn __wrap(
_slf: *mut $crate::ffi::PyObject,
attr: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> ::std::os::raw::c_int {
$crate::callback::handle_panic::<_, ::std::os::raw::c_int>(|py| {
$crate::callback::handle_panic(|py| {
let collector = PyClassImplCollector::<$cls>::new();
$crate::callback::convert(py, {
if let Some(value) = ::std::ptr::NonNull::new(value) {
collector.setattr(_slf, attr, value)
collector.__setattr__(_slf, attr, value)
} else {
if let Some(del) = collector.delattr_impl() {
del(_slf, attr)
} else {
::std::result::Result::Err(
$crate::exceptions::PyAttributeError::new_err(
"can't delete attribute",
),
)
}
collector.__delattr__(_slf, attr)
}
})
})
@ -191,46 +183,47 @@ macro_rules! generate_pyclass_setattr_slot {
}};
}
pub trait PyClassSetSlotFragment<T> {
fn set_impl(
self,
) -> Option<
unsafe fn(
_slf: *mut ffi::PyObject,
attr: *mut ffi::PyObject,
value: NonNull<ffi::PyObject>,
) -> PyResult<()>,
>;
}
#[allow(non_camel_case_types)]
pub trait PyClass__set__SlotFragment<T>: Sized {
#[inline]
#[allow(non_snake_case)]
fn __set__implemented(self) -> bool {
false
}
impl<T> PyClassSetSlotFragment<T> for &'_ PyClassImplCollector<T> {
fn set_impl(
#[inline]
unsafe fn __set__(
self,
) -> Option<
unsafe fn(
_slf: *mut ffi::PyObject,
attr: *mut ffi::PyObject,
value: NonNull<ffi::PyObject>,
) -> PyResult<()>,
> {
None
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
_value: NonNull<ffi::PyObject>,
) -> PyResult<()> {
Err(PyNotImplementedError::new_err("can't set descriptor"))
}
}
pub trait PyClassDeleteSlotFragment<T> {
fn delete_impl(
self,
) -> Option<unsafe fn(_slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> PyResult<()>>;
}
impl<T> PyClass__set__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
impl<T> PyClassDeleteSlotFragment<T> for &'_ PyClassImplCollector<T> {
fn delete_impl(
#[allow(non_camel_case_types)]
pub trait PyClass__delete__SlotFragment<T>: Sized {
#[allow(non_snake_case)]
#[inline]
fn __delete__implemented(self) -> bool {
false
}
#[inline]
unsafe fn __delete__(
self,
) -> Option<unsafe fn(_slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> PyResult<()>> {
None
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
) -> PyResult<()> {
Err(PyNotImplementedError::new_err("can't delete descriptor"))
}
}
impl<T> PyClass__delete__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
#[doc(hidden)]
#[macro_export]
macro_rules! generate_pyclass_setdescr_slot {
@ -238,37 +231,19 @@ macro_rules! generate_pyclass_setdescr_slot {
use ::std::option::Option::*;
use $crate::class::impl_::*;
let collector = PyClassImplCollector::<$cls>::new();
let set = collector.set_impl();
let delete = collector.delete_impl();
if set.is_some() || delete.is_some() {
if collector.__set__implemented() || collector.__delete__implemented() {
unsafe extern "C" fn __wrap(
_slf: *mut $crate::ffi::PyObject,
attr: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> ::std::os::raw::c_int {
$crate::callback::handle_panic::<_, ::std::os::raw::c_int>(|py| {
$crate::callback::handle_panic(|py| {
let collector = PyClassImplCollector::<$cls>::new();
$crate::callback::convert(py, {
if let Some(value) = ::std::ptr::NonNull::new(value) {
if let Some(set) = collector.set_impl() {
set(_slf, attr, value)
} else {
::std::result::Result::Err(
$crate::exceptions::PyTypeError::new_err(
"can't set descriptor",
),
)
}
collector.__set__(_slf, attr, value)
} else {
if let Some(del) = collector.delete_impl() {
del(_slf, attr)
} else {
::std::result::Result::Err(
$crate::exceptions::PyTypeError::new_err(
"can't delete descriptor",
),
)
}
collector.__delete__(_slf, attr)
}
})
})
@ -283,6 +258,81 @@ macro_rules! generate_pyclass_setdescr_slot {
}};
}
#[allow(non_camel_case_types)]
pub trait PyClass__setitem__SlotFragment<T>: Sized {
#[inline]
#[allow(non_snake_case)]
fn __setitem__implemented(self) -> bool {
false
}
#[inline]
unsafe fn __setitem__(
self,
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
_value: NonNull<ffi::PyObject>,
) -> PyResult<()> {
Err(PyNotImplementedError::new_err("can't set item"))
}
}
impl<T> PyClass__setitem__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
#[allow(non_camel_case_types)]
pub trait PyClass__delitem__SlotFragment<T>: Sized {
#[allow(non_snake_case)]
#[inline]
fn __delitem__implemented(self) -> bool {
false
}
#[inline]
unsafe fn __delitem__(
self,
_slf: *mut ffi::PyObject,
_attr: *mut ffi::PyObject,
) -> PyResult<()> {
Err(PyNotImplementedError::new_err("can't delete item"))
}
}
impl<T> PyClass__delitem__SlotFragment<T> for &'_ PyClassImplCollector<T> {}
#[doc(hidden)]
#[macro_export]
macro_rules! generate_pyclass_setitem_slot {
($cls:ty) => {{
use ::std::option::Option::*;
use $crate::class::impl_::*;
let collector = PyClassImplCollector::<$cls>::new();
if collector.__setitem__implemented() || collector.__delitem__implemented() {
unsafe extern "C" fn __wrap(
_slf: *mut $crate::ffi::PyObject,
attr: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> ::std::os::raw::c_int {
$crate::callback::handle_panic(|py| {
let collector = PyClassImplCollector::<$cls>::new();
$crate::callback::convert(py, {
if let Some(value) = ::std::ptr::NonNull::new(value) {
collector.__setitem__(_slf, attr, value)
} else {
collector.__delitem__(_slf, attr)
}
})
})
}
Some($crate::ffi::PyType_Slot {
slot: $crate::ffi::Py_mp_ass_subscript,
pfunc: __wrap as $crate::ffi::objobjargproc as _,
})
} else {
None
}
}};
}
pub trait PyClassAllocImpl<T> {
fn alloc_impl(self) -> Option<ffi::allocfunc>;
}