From c7a4b4770f1e51d6c613bdaa102f8d1b215fb29f Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:07:16 +0100 Subject: [PATCH] Refactor `#[pyproto]` Result types --- CHANGELOG.md | 2 + guide/src/class.md | 21 ++-- pyo3-derive-backend/src/defs.rs | 87 -------------- pyo3-derive-backend/src/func.rs | 207 +++++--------------------------- src/callback.rs | 50 ++++++-- src/class/basic.rs | 43 +++---- src/class/buffer.rs | 10 +- src/class/context.rs | 8 +- src/class/descr.rs | 13 +- src/class/iter.rs | 28 +++-- src/class/macros.rs | 61 +++++----- src/class/mapping.rs | 17 ++- src/class/number.rs | 143 ++++++++-------------- src/class/pyasync.rs | 65 +++++----- src/class/sequence.rs | 40 +++--- tests/test_sequence.rs | 38 +++++- 16 files changed, 305 insertions(+), 528 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2247dbea..ba89f0d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Update `num-bigint` optional dependendency from `0.2` to `0.3`. [#978](https://github.com/PyO3/pyo3/pull/978) - `#[pyproto]` is re-implemented without specialization. [#961](https://github.com/PyO3/pyo3/pull/961) - `PyClassAlloc::alloc` is renamed to `PyClassAlloc::new`. [#990](https://github.com/PyO3/pyo3/pull/990) +- `#[pyproto]` methods can now have return value `T` or `PyResult` (previously only `PyResult` was supported). [#996](https://github.com/PyO3/pyo3/pull/996) ### Removed - Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930) @@ -34,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fix passing explicit `None` to `Option` argument `#[pyfunction]` with a default value. [#936](https://github.com/PyO3/pyo3/pull/936) - Fix `PyClass.__new__`'s not respecting subclasses when inherited by a Python class. [#990](https://github.com/PyO3/pyo3/pull/990) +- Fix returning `Option` from `#[pyproto]` methods. [#996](https://github.com/PyO3/pyo3/pull/996) ## [0.10.1] - 2020-05-14 ### Fixed diff --git a/guide/src/class.md b/guide/src/class.md index 70c8a9ab..3c6699ee 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -688,6 +688,9 @@ mapping or number protocols. PyO3 defines separate traits for each of them. To p Python object behavior, you need to implement the specific trait for your struct. Important note, each protocol implementation block has to be annotated with the `#[pyproto]` attribute. +All `#[pyproto]` methods which can be defined below can return `T` instead of `PyResult` if the +method implementation is infallible. + ### Basic object customization The [`PyObjectProtocol`] trait provides several basic customizations. @@ -823,11 +826,11 @@ struct MyIterator { #[pyproto] impl PyIterProtocol for MyIterator { - fn __iter__(slf: PyRef) -> PyResult> { - Ok(slf.into()) + fn __iter__(slf: PyRef) -> Py { + slf.into() } - fn __next__(mut slf: PyRefMut) -> PyResult> { - Ok(slf.iter.next()) + fn __next__(mut slf: PyRefMut) -> Option { + slf.iter.next() } } ``` @@ -848,12 +851,12 @@ struct Iter { #[pyproto] impl PyIterProtocol for Iter { - fn __iter__(slf: PyRefMut) -> PyResult> { - Ok(slf.into()) + fn __iter__(slf: PyRefMut) -> Py { + slf.into() } - fn __next__(mut slf: PyRefMut) -> PyResult> { - Ok(slf.inner.next()) + fn __next__(mut slf: PyRefMut) -> Option { + slf.inner.next() } } @@ -868,7 +871,7 @@ impl PyIterProtocol for Container { let iter = Iter { inner: slf.iter.clone().into_iter(), }; - PyCell::new(slf.py(), iter).map(Into::into) + Py::new(slf.py(), iter) } } diff --git a/pyo3-derive-backend/src/defs.rs b/pyo3-derive-backend/src/defs.rs index d7d73cfc..13b15cd9 100644 --- a/pyo3-derive-backend/src/defs.rs +++ b/pyo3-derive-backend/src/defs.rs @@ -89,57 +89,47 @@ pub const OBJECT: Proto = Proto { MethodProto::Binary { name: "__getattr__", arg: "Name", - pyres: true, proto: "pyo3::class::basic::PyObjectGetAttrProtocol", }, MethodProto::Ternary { name: "__setattr__", arg1: "Name", arg2: "Value", - pyres: false, proto: "pyo3::class::basic::PyObjectSetAttrProtocol", }, MethodProto::Binary { name: "__delattr__", arg: "Name", - pyres: false, proto: "pyo3::class::basic::PyObjectDelAttrProtocol", }, MethodProto::Unary { name: "__str__", - pyres: true, proto: "pyo3::class::basic::PyObjectStrProtocol", }, MethodProto::Unary { name: "__repr__", - pyres: true, proto: "pyo3::class::basic::PyObjectReprProtocol", }, MethodProto::Binary { name: "__format__", arg: "Format", - pyres: true, proto: "pyo3::class::basic::PyObjectFormatProtocol", }, MethodProto::Unary { name: "__hash__", - pyres: false, proto: "pyo3::class::basic::PyObjectHashProtocol", }, MethodProto::Unary { name: "__bytes__", - pyres: true, proto: "pyo3::class::basic::PyObjectBytesProtocol", }, MethodProto::Binary { name: "__richcmp__", arg: "Other", - pyres: true, proto: "pyo3::class::basic::PyObjectRichcmpProtocol", }, MethodProto::Unary { name: "__bool__", - pyres: false, proto: "pyo3::class::basic::PyObjectBoolProtocol", }, ], @@ -173,24 +163,20 @@ pub const ASYNC: Proto = Proto { MethodProto::UnaryS { name: "__await__", arg: "Receiver", - pyres: true, proto: "pyo3::class::pyasync::PyAsyncAwaitProtocol", }, MethodProto::UnaryS { name: "__aiter__", arg: "Receiver", - pyres: true, proto: "pyo3::class::pyasync::PyAsyncAiterProtocol", }, MethodProto::UnaryS { name: "__anext__", arg: "Receiver", - pyres: true, proto: "pyo3::class::pyasync::PyAsyncAnextProtocol", }, MethodProto::Unary { name: "__aenter__", - pyres: true, proto: "pyo3::class::pyasync::PyAsyncAenterProtocol", }, MethodProto::Quaternary { @@ -225,12 +211,10 @@ pub const BUFFER: Proto = Proto { methods: &[ MethodProto::Unary { name: "bf_getbuffer", - pyres: false, proto: "pyo3::class::buffer::PyBufferGetBufferProtocol", }, MethodProto::Unary { name: "bf_releasebuffer", - pyres: false, proto: "pyo3::class::buffer::PyBufferReleaseBufferProtocol", }, ], @@ -248,7 +232,6 @@ pub const CONTEXT: Proto = Proto { methods: &[ MethodProto::Unary { name: "__enter__", - pyres: true, proto: "pyo3::class::context::PyContextEnterProtocol", }, MethodProto::Quaternary { @@ -303,7 +286,6 @@ pub const DESCR: Proto = Proto { arg1: "Receiver", arg2: "Inst", arg3: "Owner", - pyres: true, proto: "pyo3::class::descr::PyDescrGetProtocol", }, MethodProto::TernaryS { @@ -311,19 +293,16 @@ pub const DESCR: Proto = Proto { arg1: "Receiver", arg2: "Inst", arg3: "Value", - pyres: false, proto: "pyo3::class::descr::PyDescrSetProtocol", }, MethodProto::Binary { name: "__det__", arg: "Inst", - pyres: false, proto: "pyo3::class::descr::PyDescrDelProtocol", }, MethodProto::Binary { name: "__set_name__", arg: "Inst", - pyres: false, proto: "pyo3::class::descr::PyDescrSetNameProtocol", }, ], @@ -349,13 +328,11 @@ pub const ITER: Proto = Proto { MethodProto::UnaryS { name: "__iter__", arg: "Receiver", - pyres: true, proto: "pyo3::class::iter::PyIterIterProtocol", }, MethodProto::UnaryS { name: "__next__", arg: "Receiver", - pyres: true, proto: "pyo3::class::iter::PyIterNextProtocol", }, ], @@ -372,31 +349,26 @@ pub const MAPPING: Proto = Proto { methods: &[ MethodProto::Unary { name: "__len__", - pyres: false, proto: "pyo3::class::mapping::PyMappingLenProtocol", }, MethodProto::Binary { name: "__getitem__", arg: "Key", - pyres: true, proto: "pyo3::class::mapping::PyMappingGetItemProtocol", }, MethodProto::Ternary { name: "__setitem__", arg1: "Key", arg2: "Value", - pyres: false, proto: "pyo3::class::mapping::PyMappingSetItemProtocol", }, MethodProto::Binary { name: "__delitem__", arg: "Key", - pyres: false, proto: "pyo3::class::mapping::PyMappingDelItemProtocol", }, MethodProto::Unary { name: "__reversed__", - pyres: true, proto: "pyo3::class::mapping::PyMappingReversedProtocol", }, ], @@ -424,56 +396,47 @@ pub const SEQ: Proto = Proto { methods: &[ MethodProto::Unary { name: "__len__", - pyres: false, proto: "pyo3::class::sequence::PySequenceLenProtocol", }, MethodProto::Binary { name: "__getitem__", arg: "Index", - pyres: true, proto: "pyo3::class::sequence::PySequenceGetItemProtocol", }, MethodProto::Ternary { name: "__setitem__", arg1: "Index", arg2: "Value", - pyres: false, proto: "pyo3::class::sequence::PySequenceSetItemProtocol", }, MethodProto::Binary { name: "__delitem__", arg: "Index", - pyres: false, proto: "pyo3::class::sequence::PySequenceDelItemProtocol", }, MethodProto::Binary { name: "__contains__", arg: "Item", - pyres: false, proto: "pyo3::class::sequence::PySequenceContainsProtocol", }, MethodProto::Binary { name: "__concat__", arg: "Other", - pyres: true, proto: "pyo3::class::sequence::PySequenceConcatProtocol", }, MethodProto::Binary { name: "__repeat__", arg: "Index", - pyres: true, proto: "pyo3::class::sequence::PySequenceRepeatProtocol", }, MethodProto::Binary { name: "__inplace_concat__", arg: "Other", - pyres: true, proto: "pyo3::class::sequence::PySequenceInplaceConcatProtocol", }, MethodProto::Binary { name: "__inplace_repeat__", arg: "Index", - pyres: true, proto: "pyo3::class::sequence::PySequenceInplaceRepeatProtocol", }, ], @@ -505,56 +468,48 @@ pub const NUM: Proto = Proto { name: "__add__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberAddProtocol", }, MethodProto::BinaryS { name: "__sub__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberSubProtocol", }, MethodProto::BinaryS { name: "__mul__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberMulProtocol", }, MethodProto::BinaryS { name: "__matmul__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberMatmulProtocol", }, MethodProto::BinaryS { name: "__truediv__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberTruedivProtocol", }, MethodProto::BinaryS { name: "__floordiv__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberFloordivProtocol", }, MethodProto::BinaryS { name: "__mod__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberModProtocol", }, MethodProto::BinaryS { name: "__divmod__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberDivmodProtocol", }, MethodProto::TernaryS { @@ -562,251 +517,209 @@ pub const NUM: Proto = Proto { arg1: "Left", arg2: "Right", arg3: "Modulo", - pyres: true, proto: "pyo3::class::number::PyNumberPowProtocol", }, MethodProto::BinaryS { name: "__lshift__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberLShiftProtocol", }, MethodProto::BinaryS { name: "__rshift__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberRShiftProtocol", }, MethodProto::BinaryS { name: "__and__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberAndProtocol", }, MethodProto::BinaryS { name: "__xor__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberXorProtocol", }, MethodProto::BinaryS { name: "__or__", arg1: "Left", arg2: "Right", - pyres: true, proto: "pyo3::class::number::PyNumberOrProtocol", }, MethodProto::Binary { name: "__radd__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRAddProtocol", }, MethodProto::Binary { name: "__rsub__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRSubProtocol", }, MethodProto::Binary { name: "__rmul__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRMulProtocol", }, MethodProto::Binary { name: "__rmatmul__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRMatmulProtocol", }, MethodProto::Binary { name: "__rtruediv__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRTruedivProtocol", }, MethodProto::Binary { name: "__rfloordiv__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRFloordivProtocol", }, MethodProto::Binary { name: "__rmod__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRModProtocol", }, MethodProto::Binary { name: "__rdivmod__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRDivmodProtocol", }, MethodProto::Ternary { name: "__rpow__", arg1: "Other", arg2: "Modulo", - pyres: true, proto: "pyo3::class::number::PyNumberRPowProtocol", }, MethodProto::Binary { name: "__rlshift__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRLShiftProtocol", }, MethodProto::Binary { name: "__rrshift__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRRShiftProtocol", }, MethodProto::Binary { name: "__rand__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRAndProtocol", }, MethodProto::Binary { name: "__rxor__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberRXorProtocol", }, MethodProto::Binary { name: "__ror__", arg: "Other", - pyres: true, proto: "pyo3::class::number::PyNumberROrProtocol", }, MethodProto::Binary { name: "__iadd__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIAddProtocol", }, MethodProto::Binary { name: "__isub__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberISubProtocol", }, MethodProto::Binary { name: "__imul__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIMulProtocol", }, MethodProto::Binary { name: "__imatmul__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIMatmulProtocol", }, MethodProto::Binary { name: "__itruediv__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberITruedivProtocol", }, MethodProto::Binary { name: "__ifloordiv__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIFloordivProtocol", }, MethodProto::Binary { name: "__imod__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIModProtocol", }, MethodProto::Binary { name: "__ipow__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIPowProtocol", }, MethodProto::Binary { name: "__ilshift__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberILShiftProtocol", }, MethodProto::Binary { name: "__irshift__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIRShiftProtocol", }, MethodProto::Binary { name: "__iand__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIAndProtocol", }, MethodProto::Binary { name: "__ixor__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIXorProtocol", }, MethodProto::Binary { name: "__ior__", arg: "Other", - pyres: false, proto: "pyo3::class::number::PyNumberIOrProtocol", }, MethodProto::Unary { name: "__neg__", - pyres: true, proto: "pyo3::class::number::PyNumberNegProtocol", }, MethodProto::Unary { name: "__pos__", - pyres: true, proto: "pyo3::class::number::PyNumberPosProtocol", }, MethodProto::Unary { name: "__abs__", - pyres: true, proto: "pyo3::class::number::PyNumberAbsProtocol", }, MethodProto::Unary { name: "__invert__", - pyres: true, proto: "pyo3::class::number::PyNumberInvertProtocol", }, MethodProto::Unary { name: "__complex__", - pyres: true, proto: "pyo3::class::number::PyNumberComplexProtocol", }, MethodProto::Unary { name: "__int__", - pyres: true, proto: "pyo3::class::number::PyNumberIntProtocol", }, MethodProto::Unary { name: "__float__", - pyres: true, proto: "pyo3::class::number::PyNumberFloatProtocol", }, MethodProto::Unary { name: "__index__", - pyres: true, proto: "pyo3::class::number::PyNumberIndexProtocol", }, MethodProto::Binary { name: "__round__", arg: "NDigits", - pyres: true, proto: "pyo3::class::number::PyNumberRoundProtocol", }, ], diff --git a/pyo3-derive-backend/src/func.rs b/pyo3-derive-backend/src/func.rs index 95539d84..c46b4d17 100644 --- a/pyo3-derive-backend/src/func.rs +++ b/pyo3-derive-backend/src/func.rs @@ -15,33 +15,28 @@ pub enum MethodProto { }, Unary { name: &'static str, - pyres: bool, proto: &'static str, }, UnaryS { name: &'static str, arg: &'static str, - pyres: bool, proto: &'static str, }, Binary { name: &'static str, arg: &'static str, - pyres: bool, proto: &'static str, }, BinaryS { name: &'static str, arg1: &'static str, arg2: &'static str, - pyres: bool, proto: &'static str, }, Ternary { name: &'static str, arg1: &'static str, arg2: &'static str, - pyres: bool, proto: &'static str, }, TernaryS { @@ -49,7 +44,6 @@ pub enum MethodProto { arg1: &'static str, arg2: &'static str, arg3: &'static str, - pyres: bool, proto: &'static str, }, Quaternary { @@ -88,7 +82,7 @@ pub(crate) fn impl_method_proto( }; } - let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.output { + let ret_ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.output { ty.clone() } else { panic!("fn return type is not supported") @@ -96,9 +90,8 @@ pub(crate) fn impl_method_proto( match *meth { MethodProto::Free { .. } => unreachable!(), - MethodProto::Unary { pyres, proto, .. } => { + MethodProto::Unary { proto, .. } => { let p: syn::Path = syn::parse_str(proto).unwrap(); - let (ty, succ) = get_res_success(ty); let tmp: syn::ItemFn = syn::parse_quote! { fn test(&self) -> <#cls as #p<'p>>::Result {} @@ -106,26 +99,14 @@ pub(crate) fn impl_method_proto( sig.output = tmp.sig.output; modify_self_ty(sig); - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type Result = #ret_ty; } } } - MethodProto::UnaryS { - pyres, proto, arg, .. - } => { + MethodProto::UnaryS { proto, arg, .. } => { let p: syn::Path = syn::parse_str(proto).unwrap(); - let (ty, succ) = get_res_success(ty); let slf_name = syn::Ident::new(arg, Span::call_site()); let mut slf_ty = get_arg_ty(sig, 0); @@ -156,29 +137,14 @@ pub(crate) fn impl_method_proto( }); } - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type #slf_name = #slf_ty; - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type #slf_name = #slf_ty; - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type #slf_name = #slf_ty; + type Result = #ret_ty; } } } - MethodProto::Binary { - name, - arg, - pyres, - proto, - } => { + MethodProto::Binary { name, arg, proto } => { if sig.inputs.len() <= 1 { println!("Not enough arguments for {}", name); return TokenStream::new(); @@ -187,7 +153,6 @@ pub(crate) fn impl_method_proto( let p: syn::Path = syn::parse_str(proto).unwrap(); let arg_name = syn::Ident::new(arg, Span::call_site()); let arg_ty = get_arg_ty(sig, 1); - let (ty, succ) = get_res_success(ty); let tmp = extract_decl(syn::parse_quote! { fn test(&self,arg: <#cls as #p<'p>>::#arg_name)-> <#cls as #p<'p>>::Result {} @@ -200,20 +165,10 @@ pub(crate) fn impl_method_proto( modify_arg_ty(sig, 1, &tmp, &tmp2); modify_self_ty(sig); - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type #arg_name = #arg_ty; - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type #arg_name = #arg_ty; - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type #arg_name = #arg_ty; + type Result = #ret_ty; } } } @@ -221,7 +176,6 @@ pub(crate) fn impl_method_proto( name, arg1, arg2, - pyres, proto, } => { if sig.inputs.len() <= 1 { @@ -233,7 +187,6 @@ pub(crate) fn impl_method_proto( let arg1_ty = get_arg_ty(sig, 0); let arg2_name = syn::Ident::new(arg2, Span::call_site()); let arg2_ty = get_arg_ty(sig, 1); - let (ty, succ) = get_res_success(ty); // rewrite ty let tmp = extract_decl(syn::parse_quote! {fn test( @@ -247,22 +200,11 @@ pub(crate) fn impl_method_proto( modify_arg_ty(sig, 0, &tmp, &tmp2); modify_arg_ty(sig, 1, &tmp, &tmp2); - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type #arg1_name = #arg1_ty; + type #arg2_name = #arg2_ty; + type Result = #ret_ty; } } } @@ -270,7 +212,6 @@ pub(crate) fn impl_method_proto( name, arg1, arg2, - pyres, proto, } => { if sig.inputs.len() <= 2 { @@ -282,7 +223,6 @@ pub(crate) fn impl_method_proto( let arg1_ty = get_arg_ty(sig, 1); let arg2_name = syn::Ident::new(arg2, Span::call_site()); let arg2_ty = get_arg_ty(sig, 2); - let (ty, succ) = get_res_success(ty); // rewrite ty let tmp = extract_decl(syn::parse_quote! {fn test( @@ -299,22 +239,11 @@ pub(crate) fn impl_method_proto( modify_arg_ty(sig, 2, &tmp, &tmp2); modify_self_ty(sig); - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type #arg1_name = #arg1_ty; + type #arg2_name = #arg2_ty; + type Result = #ret_ty; } } } @@ -323,7 +252,6 @@ pub(crate) fn impl_method_proto( arg1, arg2, arg3, - pyres, proto, } => { if sig.inputs.len() <= 2 { @@ -337,7 +265,6 @@ pub(crate) fn impl_method_proto( let arg2_ty = get_arg_ty(sig, 1); let arg3_name = syn::Ident::new(arg3, Span::call_site()); let arg3_ty = get_arg_ty(sig, 2); - let (ty, succ) = get_res_success(ty); // rewrite ty let tmp = extract_decl(syn::parse_quote! {fn test( @@ -354,24 +281,12 @@ pub(crate) fn impl_method_proto( modify_arg_ty(sig, 1, &tmp, &tmp2); modify_arg_ty(sig, 2, &tmp, &tmp2); - if pyres { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type #arg3_name = #arg3_ty; - type Success = #succ; - type Result = #ty; - } - } - } else { - quote! { - impl<'p> #p<'p> for #cls { - type #arg1_name = #arg1_ty; - type #arg2_name = #arg2_ty; - type #arg3_name = #arg3_ty; - type Result = #ty; - } + quote! { + impl<'p> #p<'p> for #cls { + type #arg1_name = #arg1_ty; + type #arg2_name = #arg2_ty; + type #arg3_name = #arg3_ty; + type Result = #ret_ty; } } } @@ -393,7 +308,6 @@ pub(crate) fn impl_method_proto( let arg2_ty = get_arg_ty(sig, 2); let arg3_name = syn::Ident::new(arg3, Span::call_site()); let arg3_ty = get_arg_ty(sig, 3); - let (ty, succ) = get_res_success(ty); // rewrite ty let tmp = extract_decl(syn::parse_quote! {fn test( @@ -418,8 +332,7 @@ pub(crate) fn impl_method_proto( type #arg1_name = #arg1_ty; type #arg2_name = #arg2_ty; type #arg3_name = #arg3_ty; - type Success = #succ; - type Result = #ty; + type Result = #ret_ty; } } } @@ -460,62 +373,6 @@ fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type { ty } -// Success -fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) { - let mut result; - let mut succ; - - match ty { - syn::Type::Path(ref typath) => { - if let Some(segment) = typath.path.segments.last() { - match segment.ident.to_string().as_str() { - // check for PyResult - "PyResult" => match segment.arguments { - syn::PathArguments::AngleBracketed(ref data) => { - result = true; - succ = data.args[0].clone(); - - // check for PyResult> - if let syn::GenericArgument::Type(syn::Type::Path(ref typath)) = - data.args[0] - { - if let Some(segment) = typath.path.segments.last() { - if "Option" == segment.ident.to_string().as_str() { - // get T from Option - if let syn::PathArguments::AngleBracketed(ref data) = - segment.arguments - { - result = false; - succ = data.args[0].clone(); - } - } - } - } - } - _ => panic!("fn result type is not supported"), - }, - _ => panic!( - "fn result type has to be PyResult or (), got {:?}", - segment.ident - ), - } - } else { - panic!("fn result is not supported {:?}", typath) - } - } - _ => panic!("not supported: {:?}", ty), - }; - - // result - let res = if result { - quote! {PyResult<#succ>} - } else { - quote! {#ty} - }; - - (res, succ) -} - fn extract_decl(spec: syn::Item) -> syn::Signature { match spec { syn::Item::Fn(f) => f.sig, diff --git a/src/callback.rs b/src/callback.rs index 95487026..0220436f 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -79,19 +79,40 @@ impl IntoPyCallbackOutput<()> for () { } } -pub struct LenCallbackOutput(pub usize); - -impl IntoPyCallbackOutput for LenCallbackOutput { +impl IntoPyCallbackOutput for usize { #[inline] fn convert(self, _py: Python) -> PyResult { - if self.0 <= (isize::MAX as usize) { - Ok(self.0 as isize) + if self <= (isize::MAX as usize) { + Ok(self as isize) } else { Err(OverflowError::py_err(())) } } } +// Converters needed for `#[pyproto]` implementations + +impl IntoPyCallbackOutput for bool { + fn convert(self, _: Python) -> PyResult { + Ok(self) + } +} + +impl IntoPyCallbackOutput for usize { + fn convert(self, _: Python) -> PyResult { + Ok(self) + } +} + +impl IntoPyCallbackOutput for T +where + T: IntoPy, +{ + fn convert(self, py: Python) -> PyResult { + Ok(self.into_py(py)) + } +} + pub trait WrappingCastTo { fn wrapping_cast(self) -> T; } @@ -117,15 +138,12 @@ wrapping_cast!(i32, Py_hash_t); wrapping_cast!(isize, Py_hash_t); wrapping_cast!(i64, Py_hash_t); -pub struct HashCallbackOutput(pub T); +pub struct HashCallbackOutput(Py_hash_t); -impl IntoPyCallbackOutput for HashCallbackOutput -where - T: WrappingCastTo, -{ +impl IntoPyCallbackOutput for HashCallbackOutput { #[inline] fn convert(self, _py: Python) -> PyResult { - let hash = self.0.wrapping_cast(); + let hash = self.0; if hash == -1 { Ok(-2) } else { @@ -134,6 +152,16 @@ where } } +impl IntoPyCallbackOutput for T +where + T: WrappingCastTo, +{ + #[inline] + fn convert(self, _py: Python) -> PyResult { + Ok(HashCallbackOutput(self.wrapping_cast())) + } +} + #[doc(hidden)] #[inline] pub fn convert(py: Python, value: T) -> PyResult diff --git a/src/class/basic.rs b/src/class/basic.rs index 9af60e51..f7dd4a54 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -8,10 +8,8 @@ //! Parts of the documentation are copied from the respective methods from the //! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html) -use crate::callback::HashCallbackOutput; -use crate::{ - exceptions, ffi, FromPyObject, IntoPy, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult, -}; +use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput}; +use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult}; use std::os::raw::c_int; /// Operators for the __richcmp__ method @@ -100,45 +98,39 @@ pub trait PyObjectProtocol<'p>: PyClass { pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> { type Name: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectSetAttrProtocol<'p>: PyObjectProtocol<'p> { type Name: FromPyObject<'p>; type Value: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyObjectDelAttrProtocol<'p>: PyObjectProtocol<'p> { type Name: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyObjectStrProtocol<'p>: PyObjectProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectReprProtocol<'p>: PyObjectProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectFormatProtocol<'p>: PyObjectProtocol<'p> { type Format: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectHashProtocol<'p>: PyObjectProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectBoolProtocol<'p>: PyObjectProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectBytesProtocol<'p>: PyObjectProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } /// All FFI functions for basic protocols. @@ -180,12 +172,7 @@ impl PyObjectMethods { where T: for<'p> PyObjectHashProtocol<'p>, { - self.tp_hash = py_unary_func!( - PyObjectHashProtocol, - T::__hash__, - ffi::Py_hash_t, - HashCallbackOutput - ); + self.tp_hash = py_unary_func!(PyObjectHashProtocol, T::__hash__, ffi::Py_hash_t); } pub fn set_getattr(&mut self) where @@ -255,7 +242,7 @@ where let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); - call_ref!(slf, __getattr__, arg) + call_ref!(slf, __getattr__, arg).convert(py) }) } Some(wrap::) @@ -293,7 +280,7 @@ where let op = extract_op(op)?; let arg = arg.extract()?; - slf.try_borrow()?.__richcmp__(arg, op).into() + slf.try_borrow()?.__richcmp__(arg, op).convert(py) }) } Some(wrap::) diff --git a/src/class/buffer.rs b/src/class/buffer.rs index c7766858..f2977274 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -4,7 +4,7 @@ //! //! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) //! c-api -use crate::err::PyResult; +use crate::callback::IntoPyCallbackOutput; use crate::{ ffi::{self, PyBufferProcs}, PyCell, PyClass, PyRefMut, @@ -33,11 +33,11 @@ pub trait PyBufferProtocol<'p>: PyClass { } pub trait PyBufferGetBufferProtocol<'p>: PyBufferProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyBufferReleaseBufferProtocol<'p>: PyBufferProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } /// Set functions used by `#[pyproto]`. @@ -71,7 +71,7 @@ where { crate::callback_body!(py, { let slf = py.from_borrowed_ptr::>(slf); - T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into() + T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).convert(py) }) } Some(wrap::) @@ -87,7 +87,7 @@ where { crate::callback_body!(py, { let slf = py.from_borrowed_ptr::>(slf); - T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into() + T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).convert(py) }) } Some(wrap::) diff --git a/src/class/context.rs b/src/class/context.rs index 44ed2743..a5aa83a7 100644 --- a/src/class/context.rs +++ b/src/class/context.rs @@ -4,7 +4,7 @@ //! Trait and support implementation for context manager api //! -use crate::err::PyResult; +use crate::callback::IntoPyCallbackOutput; use crate::{PyClass, PyObject}; /// Context manager interface @@ -31,14 +31,12 @@ pub trait PyContextProtocol<'p>: PyClass { } pub trait PyContextEnterProtocol<'p>: PyContextProtocol<'p> { - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyContextExitProtocol<'p>: PyContextProtocol<'p> { type ExcType: crate::FromPyObject<'p>; type ExcValue: crate::FromPyObject<'p>; type Traceback: crate::FromPyObject<'p>; - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } diff --git a/src/class/descr.rs b/src/class/descr.rs index 83421b44..29cfdcfb 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -5,9 +5,9 @@ //! [Python information]( //! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors) -use crate::err::PyResult; +use crate::callback::IntoPyCallbackOutput; use crate::types::PyAny; -use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; +use crate::{ffi, FromPyObject, PyClass, PyObject}; use std::os::raw::c_int; /// Descriptor interface @@ -50,25 +50,24 @@ pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> { type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>; type Inst: FromPyObject<'p>; type Owner: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyDescrSetProtocol<'p>: PyDescrProtocol<'p> { type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>; type Inst: FromPyObject<'p>; type Value: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyDescrDeleteProtocol<'p>: PyDescrProtocol<'p> { type Inst: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> { type Inst: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } /// All FFI functions for description protocols. diff --git a/src/class/iter.rs b/src/class/iter.rs index 04fbd1e9..bb3e3a9d 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -30,14 +30,12 @@ pub trait PyIterProtocol<'p>: PyClass { pub trait PyIterIterProtocol<'p>: PyIterProtocol<'p> { type Receiver: TryFromPyCell<'p, Self>; - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> { type Receiver: TryFromPyCell<'p, Self>; - type Success: crate::IntoPy; - type Result: Into>>; + type Result: IntoPyCallbackOutput; } #[derive(Default)] @@ -62,20 +60,26 @@ impl PyIterMethods { where T: for<'p> PyIterNextProtocol<'p>, { - self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__, IterNextConverter); + self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__); } } -struct IterNextConverter(Option); +pub struct IterNextOutput(Option); -impl IntoPyCallbackOutput<*mut ffi::PyObject> for IterNextConverter -where - T: IntoPy, -{ - fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> { +impl IntoPyCallbackOutput<*mut ffi::PyObject> for IterNextOutput { + fn convert(self, _py: Python) -> PyResult<*mut ffi::PyObject> { match self.0 { - Some(val) => Ok(val.into_py(py).into_ptr()), + Some(o) => Ok(o.into_ptr()), None => Err(crate::exceptions::StopIteration::py_err(())), } } } + +impl IntoPyCallbackOutput for Option +where + T: IntoPy, +{ + fn convert(self, py: Python) -> PyResult { + Ok(IterNextOutput(self.map(|o| o.into_py(py)))) + } +} diff --git a/src/class/macros.rs b/src/class/macros.rs index 8141aedf..c56fe97e 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -3,31 +3,31 @@ #[macro_export] #[doc(hidden)] macro_rules! py_unary_func { - ($trait: ident, $class:ident :: $f:ident, $call:ident, $ret_type: ty $(, $conv:expr)?) => {{ + ($trait: ident, $class:ident :: $f:ident, $call:ident, $ret_type: ty) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> $ret_type where T: for<'p> $trait<'p>, { $crate::callback_body!(py, { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - $call!(slf, $f)$(.map($conv))? + $call!(slf, $f).convert(py) }) } Some(wrap::<$class>) }}; // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $ret_type:ty $(, $conv:expr)?) => { - py_unary_func!($trait, $class::$f, call_ref, $ret_type $(, $conv)?); + ($trait:ident, $class:ident :: $f:ident, $ret_type:ty) => { + py_unary_func!($trait, $class::$f, call_ref, $ret_type); }; - ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => { - py_unary_func!($trait, $class::$f, call_ref, *mut $crate::ffi::PyObject $(, $conv)?); + ($trait:ident, $class:ident :: $f:ident) => { + py_unary_func!($trait, $class::$f, call_ref, *mut $crate::ffi::PyObject); }; } #[macro_export] #[doc(hidden)] macro_rules! py_unarys_func { - ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => {{ + ($trait:ident, $class:ident :: $f:ident) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject where T: for<'p> $trait<'p>, @@ -38,7 +38,7 @@ macro_rules! py_unarys_func { >::try_from_pycell(slf) .map_err(|e| e.into())?; - $class::$f(borrow).into()$(.map($conv))? + $class::$f(borrow).convert(py) }) } Some(wrap::<$class>) @@ -49,12 +49,7 @@ macro_rules! py_unarys_func { #[doc(hidden)] macro_rules! py_len_func { ($trait:ident, $class:ident :: $f:ident) => { - py_unary_func!( - $trait, - $class::$f, - $crate::ffi::Py_ssize_t, - $crate::callback::LenCallbackOutput - ) + py_unary_func!($trait, $class::$f, $crate::ffi::Py_ssize_t) }; } @@ -62,7 +57,7 @@ macro_rules! py_len_func { #[doc(hidden)] macro_rules! py_binary_func { // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $return:ty, $call:ident $(, $conv:expr)?) => {{ + ($trait:ident, $class:ident :: $f:ident, $return:ty, $call:ident) => {{ unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return where T: for<'p> $trait<'p>, @@ -70,16 +65,16 @@ macro_rules! py_binary_func { $crate::callback_body!(py, { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg); - $call!(slf, $f, arg)$(.map($conv))? + $call!(slf, $f, arg).convert(py) }) } Some(wrap::<$class>) }}; - ($trait:ident, $class:ident :: $f:ident, $return:ty $(, $conv:expr)?) => { - py_binary_func!($trait, $class::$f, $return, call_ref $(, $conv)?) + ($trait:ident, $class:ident :: $f:ident, $return:ty) => { + py_binary_func!($trait, $class::$f, $return, call_ref) }; - ($trait:ident, $class:ident :: $f:ident $(, $conv:expr)?) => { - py_binary_func!($trait, $class::$f, *mut $crate::ffi::PyObject $(, $conv)?) + ($trait:ident, $class:ident :: $f:ident) => { + py_binary_func!($trait, $class::$f, *mut $crate::ffi::PyObject) }; } @@ -98,7 +93,7 @@ macro_rules! py_binary_num_func { let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs); let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs); - $class::$f(lhs.extract()?, rhs.extract()?).into() + $class::$f(lhs.extract()?, rhs.extract()?).convert(py) }) } Some(wrap::<$class>) @@ -121,7 +116,7 @@ macro_rules! py_binary_reversed_num_func { let slf = py.from_borrowed_ptr::<$crate::PyCell>(rhs); let arg = py.from_borrowed_ptr::<$crate::PyAny>(lhs); - $class::$f(&*slf.try_borrow()?, arg.extract()?).into() + $class::$f(&*slf.try_borrow()?, arg.extract()?).convert(py) }) } Some(wrap::<$class>) @@ -143,7 +138,7 @@ macro_rules! py_binary_self_func { $crate::callback_body!(py, { let slf_ = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::PyAny>(arg); - call_mut!(slf_, $f, arg)?; + call_mut!(slf_, $f, arg).convert(py)?; ffi::Py_INCREF(slf); Ok(slf) }) @@ -169,7 +164,7 @@ macro_rules! py_ssizearg_func { { $crate::callback_body!(py, { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - $call!(slf, $f; arg.into()) + $call!(slf, $f; arg.into()).convert(py) }) } Some(wrap::<$class>) @@ -200,7 +195,7 @@ macro_rules! py_ternarys_func { .from_borrowed_ptr::<$crate::types::PyAny>(arg2) .extract()?; - $class::$f(slf, arg1, arg2).into() + $class::$f(slf, arg1, arg2).convert(py) }) } @@ -234,7 +229,7 @@ macro_rules! py_ternary_num_func { .from_borrowed_ptr::<$crate::types::PyAny>(arg3) .extract()?; - $class::$f(arg1, arg2, arg3).into() + $class::$f(arg1, arg2, arg3).convert(py) }) } @@ -260,7 +255,7 @@ macro_rules! py_ternary_reversed_num_func { let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::PyAny>(arg3); - $class::$f(&*slf.try_borrow()?, arg1.extract()?, arg2.extract()?).into() + $class::$f(&*slf.try_borrow()?, arg1.extract()?, arg2.extract()?).convert(py) }) } Some(wrap::<$class>) @@ -284,7 +279,7 @@ macro_rules! py_dummy_ternary_self_func { $crate::callback_body!(py, { let slf_cell = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg1 = py.from_borrowed_ptr::<$crate::PyAny>(arg1); - call_mut!(slf_cell, $f, arg1)?; + call_mut!(slf_cell, $f, arg1).convert(py)?; ffi::Py_INCREF(slf); Ok(slf) }) @@ -316,7 +311,7 @@ macro_rules! py_func_set { } else { let name = py.from_borrowed_ptr::<$crate::PyAny>(name); let value = py.from_borrowed_ptr::<$crate::PyAny>(value); - call_mut!(slf, $fn_set, name, value) + call_mut!(slf, $fn_set, name, value).convert(py) } }) } @@ -341,7 +336,7 @@ macro_rules! py_func_del { let name = py .from_borrowed_ptr::<$crate::types::PyAny>(name) .extract()?; - slf.try_borrow_mut()?.$fn_del(name).into() + slf.try_borrow_mut()?.$fn_del(name).convert(py) } else { Err(PyErr::new::( "Subscript assignment not supported", @@ -369,10 +364,10 @@ macro_rules! py_func_set_del { let name = py.from_borrowed_ptr::<$crate::PyAny>(name); if value.is_null() { - call_mut!(slf, $fn_del, name) + call_mut!(slf, $fn_del, name).convert(py) } else { let value = py.from_borrowed_ptr::<$crate::PyAny>(value); - call_mut!(slf, $fn_set, name, value) + call_mut!(slf, $fn_set, name, value).convert(py) } }) } @@ -382,7 +377,7 @@ macro_rules! py_func_set_del { macro_rules! _call_impl { ($slf: expr, $fn: ident $(; $args: expr)*) => { - $slf.$fn($($args,)*).into() + $slf.$fn($($args,)*) }; ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { _call_impl!($slf, $fn $(,$raw_args)* $(;$args)* ;$raw_arg.extract()?) diff --git a/src/class/mapping.rs b/src/class/mapping.rs index 4e51ebed..d20547e1 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -3,8 +3,9 @@ //! Python Mapping Interface //! Trait and support implementation for implementing mapping support -use crate::err::{PyErr, PyResult}; -use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject}; +use crate::callback::IntoPyCallbackOutput; +use crate::err::PyErr; +use crate::{exceptions, ffi, FromPyObject, PyClass, PyObject}; /// Mapping interface #[allow(unused_variables)] @@ -49,29 +50,27 @@ pub trait PyMappingProtocol<'p>: PyClass { // the existance of a slotted method. pub trait PyMappingLenProtocol<'p>: PyMappingProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyMappingGetItemProtocol<'p>: PyMappingProtocol<'p> { type Key: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyMappingSetItemProtocol<'p>: PyMappingProtocol<'p> { type Key: FromPyObject<'p>; type Value: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyMappingDelItemProtocol<'p>: PyMappingProtocol<'p> { type Key: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } #[doc(hidden)] diff --git a/src/class/number.rs b/src/class/number.rs index 7e6e6a10..67ec984d 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -3,8 +3,8 @@ //! Python Number Interface //! Trait and support implementation for implementing number protocol -use crate::err::PyResult; -use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; +use crate::callback::IntoPyCallbackOutput; +use crate::{ffi, FromPyObject, PyClass, PyObject}; /// Number interface #[allow(unused_variables)] @@ -318,301 +318,264 @@ pub trait PyNumberProtocol<'p>: PyClass { pub trait PyNumberAddProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberSubProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberMulProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberMatmulProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberTruedivProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberFloordivProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberModProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberDivmodProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberPowProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; type Modulo: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberLShiftProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRShiftProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberAndProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberXorProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberOrProtocol<'p>: PyNumberProtocol<'p> { type Left: FromPyObject<'p>; type Right: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRAddProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRSubProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRMulProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRMatmulProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRTruedivProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRFloordivProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRModProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRDivmodProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRPowProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; type Modulo: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRLShiftProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRRShiftProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRAndProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRXorProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberROrProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberIAddProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberISubProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIMulProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIMatmulProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberITruedivProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIFloordivProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIModProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIDivmodProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIPowProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberILShiftProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIRShiftProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIAndProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIXorProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberIOrProtocol<'p>: PyNumberProtocol<'p> { type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PyNumberNegProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberPosProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberAbsProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberInvertProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberComplexProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberIntProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberFloatProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberRoundProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; type NDigits: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> { - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } #[doc(hidden)] diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 4cb3e102..83df1410 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -8,9 +8,10 @@ //! [PEP-0492](https://www.python.org/dev/peps/pep-0492/) //! +use crate::callback::IntoPyCallbackOutput; use crate::derive_utils::TryFromPyCell; use crate::err::PyResult; -use crate::{ffi, PyClass, PyObject}; +use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, Python}; /// Python Async/Await support interface. /// @@ -60,33 +61,28 @@ pub trait PyAsyncProtocol<'p>: PyClass { pub trait PyAsyncAwaitProtocol<'p>: PyAsyncProtocol<'p> { type Receiver: TryFromPyCell<'p, Self>; - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyAsyncAiterProtocol<'p>: PyAsyncProtocol<'p> { type Receiver: TryFromPyCell<'p, Self>; - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyAsyncAnextProtocol<'p>: PyAsyncProtocol<'p> { type Receiver: TryFromPyCell<'p, Self>; - type Success: crate::IntoPy; - type Result: Into>>; + type Result: IntoPyCallbackOutput; } pub trait PyAsyncAenterProtocol<'p>: PyAsyncProtocol<'p> { - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> { type ExcType: crate::FromPyObject<'p>; type ExcValue: crate::FromPyObject<'p>; type Traceback: crate::FromPyObject<'p>; - type Success: crate::IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } #[doc(hidden)] @@ -107,37 +103,34 @@ impl ffi::PyAsyncMethods { where T: for<'p> PyAsyncAnextProtocol<'p>, { - self.am_anext = anext::am_anext::(); + self.am_anext = am_anext::(); } } -mod anext { - use super::PyAsyncAnextProtocol; - use crate::callback::IntoPyCallbackOutput; - use crate::err::PyResult; - use crate::IntoPyPointer; - use crate::Python; - use crate::{ffi, IntoPy, PyObject}; +pub struct IterANextOutput(Option); - struct IterANextOutput(Option); - - impl IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput - where - T: IntoPy, - { - fn convert(self, py: Python) -> PyResult<*mut ffi::PyObject> { - match self.0 { - Some(val) => Ok(val.into_py(py).into_ptr()), - None => Err(crate::exceptions::StopAsyncIteration::py_err(())), - } +impl IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput { + fn convert(self, _py: Python) -> PyResult<*mut ffi::PyObject> { + match self.0 { + Some(o) => Ok(o.into_ptr()), + None => Err(crate::exceptions::StopAsyncIteration::py_err(())), } } +} - #[inline] - pub(super) fn am_anext() -> Option - where - T: for<'p> PyAsyncAnextProtocol<'p>, - { - py_unarys_func!(PyAsyncAnextProtocol, T::__anext__, IterANextOutput) +impl IntoPyCallbackOutput for Option +where + T: IntoPy, +{ + fn convert(self, py: Python) -> PyResult { + Ok(IterANextOutput(self.map(|o| o.into_py(py)))) } } + +#[inline] +fn am_anext() -> Option +where + T: for<'p> PyAsyncAnextProtocol<'p>, +{ + py_unarys_func!(PyAsyncAnextProtocol, T::__anext__) +} diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 3a0f605d..e85edf02 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -3,8 +3,9 @@ //! Python Sequence Interface //! Trait and support implementation for implementing sequence +use crate::callback::IntoPyCallbackOutput; use crate::conversion::{FromPyObject, IntoPy}; -use crate::err::{PyErr, PyResult}; +use crate::err::PyErr; use crate::{exceptions, ffi, PyAny, PyCell, PyClass, PyObject}; use std::os::raw::c_int; @@ -79,51 +80,52 @@ pub trait PySequenceProtocol<'p>: PyClass + Sized { // the existance of a slotted method. pub trait PySequenceLenProtocol<'p>: PySequenceProtocol<'p> { - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PySequenceGetItemProtocol<'p>: PySequenceProtocol<'p> { type Index: FromPyObject<'p> + From; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PySequenceSetItemProtocol<'p>: PySequenceProtocol<'p> { type Index: FromPyObject<'p> + From; type Value: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PySequenceDelItemProtocol<'p>: PySequenceProtocol<'p> { type Index: FromPyObject<'p> + From; - type Result: Into>; + type Result: IntoPyCallbackOutput<()>; } pub trait PySequenceContainsProtocol<'p>: PySequenceProtocol<'p> { type Item: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PySequenceConcatProtocol<'p>: PySequenceProtocol<'p> { type Other: FromPyObject<'p>; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } pub trait PySequenceRepeatProtocol<'p>: PySequenceProtocol<'p> { type Index: FromPyObject<'p> + From; - type Success: IntoPy; - type Result: Into>; + type Result: IntoPyCallbackOutput; } -pub trait PySequenceInplaceConcatProtocol<'p>: PySequenceProtocol<'p> + IntoPy { +pub trait PySequenceInplaceConcatProtocol<'p>: + PySequenceProtocol<'p> + IntoPy + 'p +{ type Other: FromPyObject<'p>; - type Result: Into>; + type Result: IntoPyCallbackOutput; } -pub trait PySequenceInplaceRepeatProtocol<'p>: PySequenceProtocol<'p> + IntoPy { +pub trait PySequenceInplaceRepeatProtocol<'p>: + PySequenceProtocol<'p> + IntoPy + 'p +{ type Index: FromPyObject<'p> + From; - type Result: Into>; + type Result: IntoPyCallbackOutput; } #[doc(hidden)] @@ -230,7 +232,7 @@ mod sq_ass_item_impl { let mut slf = slf.try_borrow_mut()?; let value = py.from_borrowed_ptr::(value); let value = value.extract()?; - slf.__setitem__(key.into(), value).into() + crate::callback::convert(py, slf.__setitem__(key.into(), value)) }) } Some(wrap::) @@ -252,7 +254,7 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); if value.is_null() { - slf.borrow_mut().__delitem__(key.into()).into() + crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into())) } else { Err(PyErr::new::(format!( "Item assignment not supported by {:?}", @@ -280,12 +282,12 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); if value.is_null() { - call_mut!(slf, __delitem__; key.into()) + call_mut!(slf, __delitem__; key.into()).convert(py) } else { let value = py.from_borrowed_ptr::(value); let mut slf_ = slf.try_borrow_mut()?; let value = value.extract()?; - slf_.__setitem__(key.into(), value).into() + slf_.__setitem__(key.into(), value).convert(py) } }) } diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index bddb7569..4e7b5add 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -1,6 +1,5 @@ use pyo3::class::PySequenceProtocol; -use pyo3::exceptions::IndexError; -use pyo3::exceptions::ValueError; +use pyo3::exceptions::{IndexError, ValueError}; use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyList}; @@ -232,3 +231,38 @@ fn test_generic_list_set() { vec![1.to_object(py), 2.to_object(py), 3.to_object(py)] ); } + +#[pyclass] +struct OptionList { + #[pyo3(get, set)] + items: Vec>, +} + +#[pyproto] +impl PySequenceProtocol for OptionList { + fn __getitem__(&self, idx: isize) -> PyResult> { + match self.items.get(idx as usize) { + Some(x) => Ok(*x), + None => Err(PyErr::new::("Index out of bounds")), + } + } +} + +#[test] +fn test_option_list_get() { + // Regression test for #798 + let gil = Python::acquire_gil(); + let py = gil.python(); + + let list = PyCell::new( + py, + OptionList { + items: vec![Some(1), None], + }, + ) + .unwrap(); + + py_assert!(py, list, "list[0] == 1"); + py_assert!(py, list, "list[1] == None"); + py_expect_exception!(py, list, "list[2]", IndexError); +}