diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index d5d9dde1785..5e193bf4a24 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -271,7 +271,7 @@ impl<'a> Container<'a> { value: expr_path, .. }) => quote! { Ok(#self_ty { - #ident: _pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path, obj, #struct_name, #field_name)? + #ident: _pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, #field_name)? }) }, } @@ -283,7 +283,7 @@ impl<'a> Container<'a> { Some(FromPyWithAttribute { value: expr_path, .. }) => quote! ( - _pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path, obj, #struct_name, 0).map(#self_ty) + _pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, 0).map(#self_ty) ), } } @@ -303,7 +303,7 @@ impl<'a> Container<'a> { Some(FromPyWithAttribute { value: expr_path, .. }) => quote! ( - _pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path, &#ident, #struct_name, #index)? + _pyo3::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, &#ident, #struct_name, #index)? ), } }); @@ -344,7 +344,7 @@ impl<'a> Container<'a> { Some(FromPyWithAttribute { value: expr_path, .. }) => { - quote! (_pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path, &obj.#getter?, #struct_name, #field_name)?) + quote! (_pyo3::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, &obj.#getter?, #struct_name, #field_name)?) } }; diff --git a/src/impl_/frompyobject.rs b/src/impl_/frompyobject.rs index 27f0b67b3f7..5ab595ca784 100644 --- a/src/impl_/frompyobject.rs +++ b/src/impl_/frompyobject.rs @@ -2,6 +2,32 @@ use crate::types::any::PyAnyMethods; use crate::Bound; use crate::{exceptions::PyTypeError, FromPyObject, PyAny, PyErr, PyResult, Python}; +pub enum Extractor<'a, 'py, T> { + Bound(fn(&'a Bound<'py, PyAny>) -> PyResult), + GilRef(fn(&'a PyAny) -> PyResult), +} + +impl<'a, 'py, T> From) -> PyResult> for Extractor<'a, 'py, T> { + fn from(value: fn(&'a Bound<'py, PyAny>) -> PyResult) -> Self { + Self::Bound(value) + } +} + +impl<'a, T> From PyResult> for Extractor<'a, '_, T> { + fn from(value: fn(&'a PyAny) -> PyResult) -> Self { + Self::GilRef(value) + } +} + +impl<'a, 'py, T> Extractor<'a, 'py, T> { + fn call(self, obj: &'a Bound<'py, PyAny>) -> PyResult { + match self { + Extractor::Bound(f) => f(obj), + Extractor::GilRef(f) => f(obj.as_gil_ref()), + } + } +} + #[cold] pub fn failed_to_extract_enum( py: Python<'_>, @@ -61,13 +87,13 @@ where } } -pub fn extract_struct_field_with<'py, T>( - extractor: impl FnOnce(&Bound<'py, PyAny>) -> PyResult, - obj: &Bound<'py, PyAny>, +pub fn extract_struct_field_with<'a, 'py, T>( + extractor: impl Into>, + obj: &'a Bound<'py, PyAny>, struct_name: &str, field_name: &str, ) -> PyResult { - match extractor(obj) { + match extractor.into().call(obj) { Ok(value) => Ok(value), Err(err) => Err(failed_to_extract_struct_field( obj.py(), @@ -112,13 +138,13 @@ where } } -pub fn extract_tuple_struct_field_with<'py, T>( - extractor: impl FnOnce(&Bound<'py, PyAny>) -> PyResult, - obj: &Bound<'py, PyAny>, +pub fn extract_tuple_struct_field_with<'a, 'py, T>( + extractor: impl Into>, + obj: &'a Bound<'py, PyAny>, struct_name: &str, index: usize, ) -> PyResult { - match extractor(obj) { + match extractor.into().call(obj) { Ok(value) => Ok(value), Err(err) => Err(failed_to_extract_tuple_struct_field( obj.py(), diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index 531c77a60f8..5c57a954023 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -502,7 +502,7 @@ pub struct Zap { #[pyo3(item)] name: String, - #[pyo3(from_py_with = "PyAnyMethods::len", item("my_object"))] + #[pyo3(from_py_with = "Bound::<'_, PyAny>::len", item("my_object"))] some_object_length: usize, } @@ -525,7 +525,10 @@ fn test_from_py_with() { } #[derive(Debug, FromPyObject)] -pub struct ZapTuple(String, #[pyo3(from_py_with = "PyAnyMethods::len")] usize); +pub struct ZapTuple( + String, + #[pyo3(from_py_with = "Bound::<'_, PyAny>::len")] usize, +); #[test] fn test_from_py_with_tuple_struct() { @@ -560,8 +563,11 @@ fn test_from_py_with_tuple_struct_error() { #[derive(Debug, FromPyObject, PartialEq, Eq)] pub enum ZapEnum { - Zip(#[pyo3(from_py_with = "PyAnyMethods::len")] usize), - Zap(String, #[pyo3(from_py_with = "PyAnyMethods::len")] usize), + Zip(#[pyo3(from_py_with = "Bound::<'_, PyAny>::len")] usize), + Zap( + String, + #[pyo3(from_py_with = "Bound::<'_, PyAny>::len")] usize, + ), } #[test] @@ -581,7 +587,7 @@ fn test_from_py_with_enum() { #[derive(Debug, FromPyObject, PartialEq, Eq)] #[pyo3(transparent)] pub struct TransparentFromPyWith { - #[pyo3(from_py_with = "PyAnyMethods::len")] + #[pyo3(from_py_with = "Bound::<'_, PyAny>::len")] len: usize, }