update `extract_argument` to use Bound APIs (#3708)

* update `extract_argument` to use `Bound` APIs

* tidy up borrow in macros expression

* update `trybuild` output

* more concise form for `DowncastError::new`

Co-authored-by: Lily Foote <code@lilyf.org>

* use `Borrowed` instead of newtype

* use `Borrowed::from_ptr` methods in extract_argument

* update UI tests

* avoid double-negative `#[cfg]` clauses

Co-authored-by: Lily Foote <code@lilyf.org>

* review: LilyFoote, Icxolu feedback

---------

Co-authored-by: Lily Foote <code@lilyf.org>
This commit is contained in:
David Hewitt 2024-02-28 19:36:20 +00:00 committed by GitHub
parent a15e4b1a11
commit 8a12970c96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 274 additions and 144 deletions

View File

@ -1254,7 +1254,7 @@ impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a
type Holder = ::std::option::Option<pyo3::PyRef<'py, MyClass>>; type Holder = ::std::option::Option<pyo3::PyRef<'py, MyClass>>;
#[inline] #[inline]
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> { fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder) pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
} }
} }
@ -1264,7 +1264,7 @@ impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a
type Holder = ::std::option::Option<pyo3::PyRefMut<'py, MyClass>>; type Holder = ::std::option::Option<pyo3::PyRefMut<'py, MyClass>>;
#[inline] #[inline]
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> { fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
} }
} }

View File

@ -45,8 +45,8 @@ impl OrderedRichcmp {
fn bench_ordered_dunder_methods(b: &mut Bencher<'_>) { fn bench_ordered_dunder_methods(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj1 = Py::new(py, OrderedDunderMethods(0)).unwrap().into_ref(py); let obj1 = &Bound::new(py, OrderedDunderMethods(0)).unwrap().into_any();
let obj2 = Py::new(py, OrderedDunderMethods(1)).unwrap().into_ref(py); let obj2 = &Bound::new(py, OrderedDunderMethods(1)).unwrap().into_any();
b.iter(|| obj2.gt(obj1).unwrap()); b.iter(|| obj2.gt(obj1).unwrap());
}); });
@ -54,8 +54,8 @@ fn bench_ordered_dunder_methods(b: &mut Bencher<'_>) {
fn bench_ordered_richcmp(b: &mut Bencher<'_>) { fn bench_ordered_richcmp(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj1 = Py::new(py, OrderedRichcmp(0)).unwrap().into_ref(py); let obj1 = &Bound::new(py, OrderedRichcmp(0)).unwrap().into_any();
let obj2 = Py::new(py, OrderedRichcmp(1)).unwrap().into_ref(py); let obj2 = &Bound::new(py, OrderedRichcmp(1)).unwrap().into_any();
b.iter(|| obj2.gt(obj1).unwrap()); b.iter(|| obj2.gt(obj1).unwrap());
}); });

View File

@ -196,10 +196,11 @@ impl SelfType {
holders.push(quote_spanned! { *span => holders.push(quote_spanned! { *span =>
#[allow(clippy::let_unit_value)] #[allow(clippy::let_unit_value)]
let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT; let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
let mut #slf = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf);
}); });
error_mode.handle_error(quote_spanned! { *span => error_mode.handle_error(quote_spanned! { *span =>
_pyo3::impl_::extract_argument::#method::<#cls>( _pyo3::impl_::extract_argument::#method::<#cls>(
#py.from_borrowed_ptr::<_pyo3::PyAny>(#slf), &#slf,
&mut #holder, &mut #holder,
) )
}) })
@ -582,7 +583,8 @@ impl<'a> FnSpec<'a> {
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#( #holders )* #( #holders )*
#call let result = #call;
result
} }
} }
} }
@ -601,7 +603,8 @@ impl<'a> FnSpec<'a> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#arg_convert #arg_convert
#( #holders )* #( #holders )*
#call let result = #call;
result
} }
} }
} }
@ -619,7 +622,8 @@ impl<'a> FnSpec<'a> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#arg_convert #arg_convert
#( #holders )* #( #holders )*
#call let result = #call;
result
} }
} }
} }

View File

@ -44,8 +44,8 @@ pub fn impl_arg_params(
.collect::<Result<_>>()?; .collect::<Result<_>>()?;
return Ok(( return Ok((
quote! { quote! {
let _args = py.from_borrowed_ptr::<_pyo3::types::PyTuple>(_args); let _args = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &_args);
let _kwargs: ::std::option::Option<&_pyo3::types::PyDict> = py.from_borrowed_ptr_or_opt(_kwargs); let _kwargs = _pyo3::impl_::pymethods::BoundRef::ref_from_ptr_or_opt(py, &_kwargs);
}, },
arg_convert, arg_convert,
)); ));
@ -180,7 +180,7 @@ fn impl_arg_param(
let holder = push_holder(); let holder = push_holder();
return Ok(quote_arg_span! { return Ok(quote_arg_span! {
_pyo3::impl_::extract_argument::extract_argument( _pyo3::impl_::extract_argument::extract_argument(
_args, &_args,
&mut #holder, &mut #holder,
#name_str #name_str
)? )?
@ -193,7 +193,7 @@ fn impl_arg_param(
let holder = push_holder(); let holder = push_holder();
return Ok(quote_arg_span! { return Ok(quote_arg_span! {
_pyo3::impl_::extract_argument::extract_optional_argument( _pyo3::impl_::extract_argument::extract_optional_argument(
_kwargs.map(::std::convert::AsRef::as_ref), _kwargs.as_deref(),
&mut #holder, &mut #holder,
#name_str, #name_str,
|| ::std::option::Option::None || ::std::option::Option::None
@ -217,7 +217,7 @@ fn impl_arg_param(
quote_arg_span! { quote_arg_span! {
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::from_py_with_with_default( _pyo3::impl_::extract_argument::from_py_with_with_default(
#arg_value.map(_pyo3::PyNativeType::as_borrowed).as_deref(), #arg_value.as_deref(),
#name_str, #name_str,
#expr_path as fn(_) -> _, #expr_path as fn(_) -> _,
|| #default || #default
@ -226,7 +226,7 @@ fn impl_arg_param(
} else { } else {
quote_arg_span! { quote_arg_span! {
_pyo3::impl_::extract_argument::from_py_with( _pyo3::impl_::extract_argument::from_py_with(
&_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value).as_borrowed(), &_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value),
#name_str, #name_str,
#expr_path as fn(_) -> _, #expr_path as fn(_) -> _,
)? )?
@ -237,7 +237,7 @@ fn impl_arg_param(
quote_arg_span! { quote_arg_span! {
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::extract_optional_argument( _pyo3::impl_::extract_argument::extract_optional_argument(
#arg_value, #arg_value.as_deref(),
&mut #holder, &mut #holder,
#name_str, #name_str,
|| #default || #default
@ -248,7 +248,7 @@ fn impl_arg_param(
quote_arg_span! { quote_arg_span! {
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
_pyo3::impl_::extract_argument::extract_argument_with_default( _pyo3::impl_::extract_argument::extract_argument_with_default(
#arg_value, #arg_value.as_deref(),
&mut #holder, &mut #holder,
#name_str, #name_str,
|| #default || #default
@ -258,7 +258,7 @@ fn impl_arg_param(
let holder = push_holder(); let holder = push_holder();
quote_arg_span! { quote_arg_span! {
_pyo3::impl_::extract_argument::extract_argument( _pyo3::impl_::extract_argument::extract_argument(
_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value), &_pyo3::impl_::extract_argument::unwrap_required_argument(#arg_value),
&mut #holder, &mut #holder,
#name_str #name_str
)? )?

View File

@ -1378,7 +1378,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>; type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>;
#[inline] #[inline]
fn extract(obj: &'py _pyo3::PyAny, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> { fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder) _pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
} }
} }
@ -1390,7 +1390,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>; type Holder = ::std::option::Option<_pyo3::PyRef<'py, #cls>>;
#[inline] #[inline]
fn extract(obj: &'py _pyo3::PyAny, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> { fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder) _pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
} }
} }
@ -1400,7 +1400,7 @@ impl<'a> PyClassImplsBuilder<'a> {
type Holder = ::std::option::Option<_pyo3::PyRefMut<'py, #cls>>; type Holder = ::std::option::Option<_pyo3::PyRefMut<'py, #cls>>;
#[inline] #[inline]
fn extract(obj: &'py _pyo3::PyAny, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> { fn extract(obj: &'a _pyo3::Bound<'py, _pyo3::PyAny>, holder: &'a mut Self::Holder) -> _pyo3::PyResult<Self> {
_pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) _pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
} }
} }

View File

@ -698,7 +698,8 @@ pub fn impl_py_getter_def(
_slf: *mut _pyo3::ffi::PyObject _slf: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
#( #holders )* #( #holders )*
#body let result = #body;
result
} }
}; };
@ -930,39 +931,31 @@ impl Ty {
extract_error_mode, extract_error_mode,
holders, holders,
&name_str, &name_str,
quote! { quote! { #ident },
py.from_borrowed_ptr::<_pyo3::PyAny>(#ident)
},
), ),
Ty::MaybeNullObject => extract_object( Ty::MaybeNullObject => extract_object(
extract_error_mode, extract_error_mode,
holders, holders,
&name_str, &name_str,
quote! { quote! {
py.from_borrowed_ptr::<_pyo3::PyAny>(
if #ident.is_null() { if #ident.is_null() {
_pyo3::ffi::Py_None() _pyo3::ffi::Py_None()
} else { } else {
#ident #ident
} }
)
}, },
), ),
Ty::NonNullObject => extract_object( Ty::NonNullObject => extract_object(
extract_error_mode, extract_error_mode,
holders, holders,
&name_str, &name_str,
quote! { quote! { #ident.as_ptr() },
py.from_borrowed_ptr::<_pyo3::PyAny>(#ident.as_ptr())
},
), ),
Ty::IPowModulo => extract_object( Ty::IPowModulo => extract_object(
extract_error_mode, extract_error_mode,
holders, holders,
&name_str, &name_str,
quote! { quote! { #ident.as_ptr() },
#ident.to_borrowed_any(py)
},
), ),
Ty::CompareOp => extract_error_mode.handle_error( Ty::CompareOp => extract_error_mode.handle_error(
quote! { quote! {
@ -988,7 +981,7 @@ fn extract_object(
extract_error_mode: ExtractErrorMode, extract_error_mode: ExtractErrorMode,
holders: &mut Vec<TokenStream>, holders: &mut Vec<TokenStream>,
name: &str, name: &str,
source: TokenStream, source_ptr: TokenStream,
) -> TokenStream { ) -> TokenStream {
let holder = syn::Ident::new(&format!("holder_{}", holders.len()), Span::call_site()); let holder = syn::Ident::new(&format!("holder_{}", holders.len()), Span::call_site());
holders.push(quote! { holders.push(quote! {
@ -997,7 +990,7 @@ fn extract_object(
}); });
extract_error_mode.handle_error(quote! { extract_error_mode.handle_error(quote! {
_pyo3::impl_::extract_argument::extract_argument( _pyo3::impl_::extract_argument::extract_argument(
#source, &_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr),
&mut #holder, &mut #holder,
#name #name
) )

View File

@ -4,62 +4,66 @@ use pyo3::types::{PyDict, PyTuple};
#[pyfunction(signature = ())] #[pyfunction(signature = ())]
fn none() {} fn none() {}
type Any<'py> = Bound<'py, PyAny>;
type Dict<'py> = Bound<'py, PyDict>;
type Tuple<'py> = Bound<'py, PyTuple>;
#[pyfunction(signature = (a, b = None, *, c = None))] #[pyfunction(signature = (a, b = None, *, c = None))]
fn simple<'a>( fn simple<'py>(
a: &'a PyAny, a: Any<'py>,
b: Option<&'a PyAny>, b: Option<Any<'py>>,
c: Option<&'a PyAny>, c: Option<Any<'py>>,
) -> (&'a PyAny, Option<&'a PyAny>, Option<&'a PyAny>) { ) -> (Any<'py>, Option<Any<'py>>, Option<Any<'py>>) {
(a, b, c) (a, b, c)
} }
#[pyfunction(signature = (a, b = None, *args, c = None))] #[pyfunction(signature = (a, b = None, *args, c = None))]
fn simple_args<'a>( fn simple_args<'py>(
a: &'a PyAny, a: Any<'py>,
b: Option<&'a PyAny>, b: Option<Any<'py>>,
args: &'a PyTuple, args: Tuple<'py>,
c: Option<&'a PyAny>, c: Option<Any<'py>>,
) -> (&'a PyAny, Option<&'a PyAny>, &'a PyTuple, Option<&'a PyAny>) { ) -> (Any<'py>, Option<Any<'py>>, Tuple<'py>, Option<Any<'py>>) {
(a, b, args, c) (a, b, args, c)
} }
#[pyfunction(signature = (a, b = None, c = None, **kwargs))] #[pyfunction(signature = (a, b = None, c = None, **kwargs))]
fn simple_kwargs<'a>( fn simple_kwargs<'py>(
a: &'a PyAny, a: Any<'py>,
b: Option<&'a PyAny>, b: Option<Any<'py>>,
c: Option<&'a PyAny>, c: Option<Any<'py>>,
kwargs: Option<&'a PyDict>, kwargs: Option<Dict<'py>>,
) -> ( ) -> (
&'a PyAny, Any<'py>,
Option<&'a PyAny>, Option<Any<'py>>,
Option<&'a PyAny>, Option<Any<'py>>,
Option<&'a PyDict>, Option<Dict<'py>>,
) { ) {
(a, b, c, kwargs) (a, b, c, kwargs)
} }
#[pyfunction(signature = (a, b = None, *args, c = None, **kwargs))] #[pyfunction(signature = (a, b = None, *args, c = None, **kwargs))]
fn simple_args_kwargs<'a>( fn simple_args_kwargs<'py>(
a: &'a PyAny, a: Any<'py>,
b: Option<&'a PyAny>, b: Option<Any<'py>>,
args: &'a PyTuple, args: Tuple<'py>,
c: Option<&'a PyAny>, c: Option<Any<'py>>,
kwargs: Option<&'a PyDict>, kwargs: Option<Dict<'py>>,
) -> ( ) -> (
&'a PyAny, Any<'py>,
Option<&'a PyAny>, Option<Any<'py>>,
&'a PyTuple, Tuple<'py>,
Option<&'a PyAny>, Option<Any<'py>>,
Option<&'a PyDict>, Option<Dict<'py>>,
) { ) {
(a, b, args, c, kwargs) (a, b, args, c, kwargs)
} }
#[pyfunction(signature = (*args, **kwargs))] #[pyfunction(signature = (*args, **kwargs))]
fn args_kwargs<'a>( fn args_kwargs<'py>(
args: &'a PyTuple, args: Tuple<'py>,
kwargs: Option<&'a PyDict>, kwargs: Option<Dict<'py>>,
) -> (&'a PyTuple, Option<&'a PyDict>) { ) -> (Tuple<'py>, Option<Dict<'py>>) {
(args, kwargs) (args, kwargs)
} }

View File

@ -2,10 +2,16 @@ use crate::{
exceptions::PyTypeError, exceptions::PyTypeError,
ffi, ffi,
pyclass::boolean_struct::False, pyclass::boolean_struct::False,
types::{PyDict, PyString, PyTuple}, types::{any::PyAnyMethods, dict::PyDictMethods, tuple::PyTupleMethods, PyDict, PyTuple},
Bound, FromPyObject, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, PyTypeCheck, Python, Borrowed, Bound, FromPyObject, PyAny, PyClass, PyErr, PyRef, PyRefMut, PyResult, PyTypeCheck,
Python,
}; };
/// Helper type used to keep implementation more concise.
///
/// (Function argument extraction borrows input arguments.)
type PyArg<'py> = Borrowed<'py, 'py, PyAny>;
/// A trait which is used to help PyO3 macros extract function arguments. /// A trait which is used to help PyO3 macros extract function arguments.
/// ///
/// `#[pyclass]` structs need to extract as `PyRef<T>` and `PyRefMut<T>` /// `#[pyclass]` structs need to extract as `PyRef<T>` and `PyRefMut<T>`
@ -16,7 +22,7 @@ use crate::{
/// There exists a trivial blanket implementation for `T: FromPyObject` with `Holder = ()`. /// There exists a trivial blanket implementation for `T: FromPyObject` with `Holder = ()`.
pub trait PyFunctionArgument<'a, 'py>: Sized + 'a { pub trait PyFunctionArgument<'a, 'py>: Sized + 'a {
type Holder: FunctionArgumentHolder; type Holder: FunctionArgumentHolder;
fn extract(obj: &'py PyAny, holder: &'a mut Self::Holder) -> PyResult<Self>; fn extract(obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> PyResult<Self>;
} }
impl<'a, 'py, T> PyFunctionArgument<'a, 'py> for T impl<'a, 'py, T> PyFunctionArgument<'a, 'py> for T
@ -26,20 +32,23 @@ where
type Holder = (); type Holder = ();
#[inline] #[inline]
fn extract(obj: &'py PyAny, _: &'a mut ()) -> PyResult<Self> { fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult<Self> {
obj.extract() obj.extract()
} }
} }
impl<'a, 'py, T> PyFunctionArgument<'a, 'py> for &'a Bound<'py, T> impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py> for &'a Bound<'py, T>
where where
T: PyTypeCheck, T: PyTypeCheck,
{ {
type Holder = Option<Bound<'py, T>>; type Holder = Option<&'a Bound<'py, T>>;
#[inline] #[inline]
fn extract(obj: &'py PyAny, holder: &'a mut Option<Bound<'py, T>>) -> PyResult<Self> { fn extract(
Ok(&*holder.insert(obj.extract()?)) obj: &'a Bound<'py, PyAny>,
holder: &'a mut Option<&'a Bound<'py, T>>,
) -> PyResult<Self> {
Ok(holder.insert(obj.downcast()?))
} }
} }
@ -59,7 +68,7 @@ impl<T> FunctionArgumentHolder for Option<T> {
#[inline] #[inline]
pub fn extract_pyclass_ref<'a, 'py: 'a, T: PyClass>( pub fn extract_pyclass_ref<'a, 'py: 'a, T: PyClass>(
obj: &'py PyAny, obj: &'a Bound<'py, PyAny>,
holder: &'a mut Option<PyRef<'py, T>>, holder: &'a mut Option<PyRef<'py, T>>,
) -> PyResult<&'a T> { ) -> PyResult<&'a T> {
Ok(&*holder.insert(obj.extract()?)) Ok(&*holder.insert(obj.extract()?))
@ -67,7 +76,7 @@ pub fn extract_pyclass_ref<'a, 'py: 'a, T: PyClass>(
#[inline] #[inline]
pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass<Frozen = False>>( pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass<Frozen = False>>(
obj: &'py PyAny, obj: &'a Bound<'py, PyAny>,
holder: &'a mut Option<PyRefMut<'py, T>>, holder: &'a mut Option<PyRefMut<'py, T>>,
) -> PyResult<&'a mut T> { ) -> PyResult<&'a mut T> {
Ok(&mut *holder.insert(obj.extract()?)) Ok(&mut *holder.insert(obj.extract()?))
@ -76,7 +85,7 @@ pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass<Frozen = False>>(
/// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument. /// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument.
#[doc(hidden)] #[doc(hidden)]
pub fn extract_argument<'a, 'py, T>( pub fn extract_argument<'a, 'py, T>(
obj: &'py PyAny, obj: &'a Bound<'py, PyAny>,
holder: &'a mut T::Holder, holder: &'a mut T::Holder,
arg_name: &str, arg_name: &str,
) -> PyResult<T> ) -> PyResult<T>
@ -93,7 +102,7 @@ where
/// does not implement `PyFunctionArgument` for `T: PyClass`. /// does not implement `PyFunctionArgument` for `T: PyClass`.
#[doc(hidden)] #[doc(hidden)]
pub fn extract_optional_argument<'a, 'py, T>( pub fn extract_optional_argument<'a, 'py, T>(
obj: Option<&'py PyAny>, obj: Option<&'a Bound<'py, PyAny>>,
holder: &'a mut T::Holder, holder: &'a mut T::Holder,
arg_name: &str, arg_name: &str,
default: fn() -> Option<T>, default: fn() -> Option<T>,
@ -117,7 +126,7 @@ where
/// Alternative to [`extract_argument`] used when the argument has a default value provided by an annotation. /// Alternative to [`extract_argument`] used when the argument has a default value provided by an annotation.
#[doc(hidden)] #[doc(hidden)]
pub fn extract_argument_with_default<'a, 'py, T>( pub fn extract_argument_with_default<'a, 'py, T>(
obj: Option<&'py PyAny>, obj: Option<&'a Bound<'py, PyAny>>,
holder: &'a mut T::Holder, holder: &'a mut T::Holder,
arg_name: &str, arg_name: &str,
default: fn() -> T, default: fn() -> T,
@ -165,7 +174,6 @@ pub fn from_py_with_with_default<'a, 'py, T>(
#[doc(hidden)] #[doc(hidden)]
#[cold] #[cold]
pub fn argument_extraction_error(py: Python<'_>, arg_name: &str, error: PyErr) -> PyErr { pub fn argument_extraction_error(py: Python<'_>, arg_name: &str, error: PyErr) -> PyErr {
use crate::types::any::PyAnyMethods;
if error if error
.get_type_bound(py) .get_type_bound(py)
.is(&py.get_type_bound::<PyTypeError>()) .is(&py.get_type_bound::<PyTypeError>())
@ -189,7 +197,7 @@ pub fn argument_extraction_error(py: Python<'_>, arg_name: &str, error: PyErr) -
/// `argument` must not be `None` /// `argument` must not be `None`
#[doc(hidden)] #[doc(hidden)]
#[inline] #[inline]
pub unsafe fn unwrap_required_argument(argument: Option<&PyAny>) -> &PyAny { pub unsafe fn unwrap_required_argument(argument: Option<PyArg<'_>>) -> PyArg<'_> {
match argument { match argument {
Some(value) => value, Some(value) => value,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -236,7 +244,7 @@ impl FunctionDescription {
args: *const *mut ffi::PyObject, args: *const *mut ffi::PyObject,
nargs: ffi::Py_ssize_t, nargs: ffi::Py_ssize_t,
kwnames: *mut ffi::PyObject, kwnames: *mut ffi::PyObject,
output: &mut [Option<&'py PyAny>], output: &mut [Option<PyArg<'py>>],
) -> PyResult<(V::Varargs, K::Varkeywords)> ) -> PyResult<(V::Varargs, K::Varkeywords)>
where where
V: VarargsHandler<'py>, V: VarargsHandler<'py>,
@ -253,8 +261,10 @@ impl FunctionDescription {
); );
// Handle positional arguments // Handle positional arguments
// Safety: Option<&PyAny> has the same memory layout as `*mut ffi::PyObject` // Safety:
let args: *const Option<&PyAny> = args.cast(); // - Option<PyArg> has the same memory layout as `*mut ffi::PyObject`
// - we both have the GIL and can borrow these input references for the `'py` lifetime.
let args: *const Option<PyArg<'py>> = args.cast();
let positional_args_provided = nargs as usize; let positional_args_provided = nargs as usize;
let remaining_positional_args = if args.is_null() { let remaining_positional_args = if args.is_null() {
debug_assert_eq!(positional_args_provided, 0); debug_assert_eq!(positional_args_provided, 0);
@ -274,13 +284,20 @@ impl FunctionDescription {
// Handle keyword arguments // Handle keyword arguments
let mut varkeywords = K::Varkeywords::default(); let mut varkeywords = K::Varkeywords::default();
if let Some(kwnames) = py.from_borrowed_ptr_or_opt::<PyTuple>(kwnames) {
// Safety: &PyAny has the same memory layout as `*mut ffi::PyObject` // Safety: kwnames is known to be a pointer to a tuple, or null
let kwargs = // - we both have the GIL and can borrow this input reference for the `'py` lifetime.
::std::slice::from_raw_parts((args as *const &PyAny).offset(nargs), kwnames.len()); let kwnames: Option<Borrowed<'_, '_, PyTuple>> =
Borrowed::from_ptr_or_opt(py, kwnames).map(|kwnames| kwnames.downcast_unchecked());
if let Some(kwnames) = kwnames {
// Safety: PyArg has the same memory layout as `*mut ffi::PyObject`
let kwargs = ::std::slice::from_raw_parts(
(args as *const PyArg<'py>).offset(nargs),
kwnames.len(),
);
self.handle_kwargs::<K, _>( self.handle_kwargs::<K, _>(
kwnames.iter().zip(kwargs.iter().copied()), kwnames.iter_borrowed().zip(kwargs.iter().copied()),
&mut varkeywords, &mut varkeywords,
num_positional_parameters, num_positional_parameters,
output, output,
@ -312,14 +329,20 @@ impl FunctionDescription {
py: Python<'py>, py: Python<'py>,
args: *mut ffi::PyObject, args: *mut ffi::PyObject,
kwargs: *mut ffi::PyObject, kwargs: *mut ffi::PyObject,
output: &mut [Option<&'py PyAny>], output: &mut [Option<PyArg<'py>>],
) -> PyResult<(V::Varargs, K::Varkeywords)> ) -> PyResult<(V::Varargs, K::Varkeywords)>
where where
V: VarargsHandler<'py>, V: VarargsHandler<'py>,
K: VarkeywordsHandler<'py>, K: VarkeywordsHandler<'py>,
{ {
let args = py.from_borrowed_ptr::<PyTuple>(args); // Safety:
let kwargs: ::std::option::Option<&PyDict> = py.from_borrowed_ptr_or_opt(kwargs); // - `args` is known to be a tuple
// - `kwargs` is known to be a dict or null
// - we both have the GIL and can borrow these input references for the `'py` lifetime.
let args: Borrowed<'py, 'py, PyTuple> =
Borrowed::from_ptr(py, args).downcast_unchecked::<PyTuple>();
let kwargs: Option<Borrowed<'py, 'py, PyDict>> =
Borrowed::from_ptr_or_opt(py, kwargs).map(|kwargs| kwargs.downcast_unchecked());
let num_positional_parameters = self.positional_parameter_names.len(); let num_positional_parameters = self.positional_parameter_names.len();
@ -331,17 +354,26 @@ impl FunctionDescription {
); );
// Copy positional arguments into output // Copy positional arguments into output
for (i, arg) in args.iter().take(num_positional_parameters).enumerate() { for (i, arg) in args
.iter_borrowed()
.take(num_positional_parameters)
.enumerate()
{
output[i] = Some(arg); output[i] = Some(arg);
} }
// If any arguments remain, push them to varargs (if possible) or error // If any arguments remain, push them to varargs (if possible) or error
let varargs = V::handle_varargs_tuple(args, self)?; let varargs = V::handle_varargs_tuple(&args, self)?;
// Handle keyword arguments // Handle keyword arguments
let mut varkeywords = K::Varkeywords::default(); let mut varkeywords = K::Varkeywords::default();
if let Some(kwargs) = kwargs { if let Some(kwargs) = kwargs {
self.handle_kwargs::<K, _>(kwargs, &mut varkeywords, num_positional_parameters, output)? self.handle_kwargs::<K, _>(
kwargs.iter_borrowed(),
&mut varkeywords,
num_positional_parameters,
output,
)?
} }
// Once all inputs have been processed, check that all required arguments have been provided. // Once all inputs have been processed, check that all required arguments have been provided.
@ -358,11 +390,11 @@ impl FunctionDescription {
kwargs: I, kwargs: I,
varkeywords: &mut K::Varkeywords, varkeywords: &mut K::Varkeywords,
num_positional_parameters: usize, num_positional_parameters: usize,
output: &mut [Option<&'py PyAny>], output: &mut [Option<PyArg<'py>>],
) -> PyResult<()> ) -> PyResult<()>
where where
K: VarkeywordsHandler<'py>, K: VarkeywordsHandler<'py>,
I: IntoIterator<Item = (&'py PyAny, &'py PyAny)>, I: IntoIterator<Item = (PyArg<'py>, PyArg<'py>)>,
{ {
debug_assert_eq!( debug_assert_eq!(
num_positional_parameters, num_positional_parameters,
@ -374,11 +406,21 @@ impl FunctionDescription {
); );
let mut positional_only_keyword_arguments = Vec::new(); let mut positional_only_keyword_arguments = Vec::new();
for (kwarg_name_py, value) in kwargs { for (kwarg_name_py, value) in kwargs {
// All keyword arguments should be UTF-8 strings, but we'll check, just in case. // Safety: All keyword arguments should be UTF-8 strings, but if it's not, `.to_str()`
// If it isn't, then it will be handled below as a varkeyword (which may raise an // will return an error anyway.
// error if this function doesn't accept **kwargs). Rust source is always UTF-8 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
// and so all argument names in `#[pyfunction]` signature must be UTF-8. let kwarg_name =
if let Ok(kwarg_name) = kwarg_name_py.downcast::<PyString>()?.to_str() { unsafe { kwarg_name_py.downcast_unchecked::<crate::types::PyString>() }.to_str();
#[cfg(all(not(Py_3_10), Py_LIMITED_API))]
let kwarg_name = kwarg_name_py.extract::<crate::pybacked::PyBackedStr>();
if let Ok(kwarg_name_owned) = kwarg_name {
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
let kwarg_name = kwarg_name_owned;
#[cfg(all(not(Py_3_10), Py_LIMITED_API))]
let kwarg_name: &str = &kwarg_name_owned;
// Try to place parameter in keyword only parameters // Try to place parameter in keyword only parameters
if let Some(i) = self.find_keyword_parameter_in_keyword_only(kwarg_name) { if let Some(i) = self.find_keyword_parameter_in_keyword_only(kwarg_name) {
if output[i + num_positional_parameters] if output[i + num_positional_parameters]
@ -397,7 +439,7 @@ impl FunctionDescription {
// kwarg to conflict with a postional-only argument - the value // kwarg to conflict with a postional-only argument - the value
// will go into **kwargs anyway. // will go into **kwargs anyway.
if K::handle_varkeyword(varkeywords, kwarg_name_py, value, self).is_err() { if K::handle_varkeyword(varkeywords, kwarg_name_py, value, self).is_err() {
positional_only_keyword_arguments.push(kwarg_name); positional_only_keyword_arguments.push(kwarg_name_owned);
} }
} else if output[i].replace(value).is_some() { } else if output[i].replace(value).is_some() {
return Err(self.multiple_values_for_argument(kwarg_name)); return Err(self.multiple_values_for_argument(kwarg_name));
@ -410,6 +452,11 @@ impl FunctionDescription {
} }
if !positional_only_keyword_arguments.is_empty() { if !positional_only_keyword_arguments.is_empty() {
#[cfg(all(not(Py_3_10), Py_LIMITED_API))]
let positional_only_keyword_arguments: Vec<_> = positional_only_keyword_arguments
.iter()
.map(std::ops::Deref::deref)
.collect();
return Err(self.positional_only_keyword_arguments(&positional_only_keyword_arguments)); return Err(self.positional_only_keyword_arguments(&positional_only_keyword_arguments));
} }
@ -436,7 +483,7 @@ impl FunctionDescription {
#[inline] #[inline]
fn ensure_no_missing_required_positional_arguments( fn ensure_no_missing_required_positional_arguments(
&self, &self,
output: &[Option<&PyAny>], output: &[Option<PyArg<'_>>],
positional_args_provided: usize, positional_args_provided: usize,
) -> PyResult<()> { ) -> PyResult<()> {
if positional_args_provided < self.required_positional_parameters { if positional_args_provided < self.required_positional_parameters {
@ -452,7 +499,7 @@ impl FunctionDescription {
#[inline] #[inline]
fn ensure_no_missing_required_keyword_arguments( fn ensure_no_missing_required_keyword_arguments(
&self, &self,
output: &[Option<&PyAny>], output: &[Option<PyArg<'_>>],
) -> PyResult<()> { ) -> PyResult<()> {
let keyword_output = &output[self.positional_parameter_names.len()..]; let keyword_output = &output[self.positional_parameter_names.len()..];
for (param, out) in self.keyword_only_parameters.iter().zip(keyword_output) { for (param, out) in self.keyword_only_parameters.iter().zip(keyword_output) {
@ -497,11 +544,11 @@ impl FunctionDescription {
} }
#[cold] #[cold]
fn unexpected_keyword_argument(&self, argument: &PyAny) -> PyErr { fn unexpected_keyword_argument(&self, argument: PyArg<'_>) -> PyErr {
PyTypeError::new_err(format!( PyTypeError::new_err(format!(
"{} got an unexpected keyword argument '{}'", "{} got an unexpected keyword argument '{}'",
self.full_name(), self.full_name(),
argument argument.as_any()
)) ))
} }
@ -534,7 +581,7 @@ impl FunctionDescription {
} }
#[cold] #[cold]
fn missing_required_keyword_arguments(&self, keyword_outputs: &[Option<&PyAny>]) -> PyErr { fn missing_required_keyword_arguments(&self, keyword_outputs: &[Option<PyArg<'_>>]) -> PyErr {
debug_assert_eq!(self.keyword_only_parameters.len(), keyword_outputs.len()); debug_assert_eq!(self.keyword_only_parameters.len(), keyword_outputs.len());
let missing_keyword_only_arguments: Vec<_> = self let missing_keyword_only_arguments: Vec<_> = self
@ -555,7 +602,7 @@ impl FunctionDescription {
} }
#[cold] #[cold]
fn missing_required_positional_arguments(&self, output: &[Option<&PyAny>]) -> PyErr { fn missing_required_positional_arguments(&self, output: &[Option<PyArg<'_>>]) -> PyErr {
let missing_positional_arguments: Vec<_> = self let missing_positional_arguments: Vec<_> = self
.positional_parameter_names .positional_parameter_names
.iter() .iter()
@ -575,14 +622,14 @@ pub trait VarargsHandler<'py> {
/// Called by `FunctionDescription::extract_arguments_fastcall` with any additional arguments. /// Called by `FunctionDescription::extract_arguments_fastcall` with any additional arguments.
fn handle_varargs_fastcall( fn handle_varargs_fastcall(
py: Python<'py>, py: Python<'py>,
varargs: &[Option<&PyAny>], varargs: &[Option<PyArg<'py>>],
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<Self::Varargs>; ) -> PyResult<Self::Varargs>;
/// Called by `FunctionDescription::extract_arguments_tuple_dict` with the original tuple. /// Called by `FunctionDescription::extract_arguments_tuple_dict` with the original tuple.
/// ///
/// Additional arguments are those in the tuple slice starting from `function_description.positional_parameter_names.len()`. /// Additional arguments are those in the tuple slice starting from `function_description.positional_parameter_names.len()`.
fn handle_varargs_tuple( fn handle_varargs_tuple(
args: &'py PyTuple, args: &Bound<'py, PyTuple>,
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<Self::Varargs>; ) -> PyResult<Self::Varargs>;
} }
@ -596,7 +643,7 @@ impl<'py> VarargsHandler<'py> for NoVarargs {
#[inline] #[inline]
fn handle_varargs_fastcall( fn handle_varargs_fastcall(
_py: Python<'py>, _py: Python<'py>,
varargs: &[Option<&PyAny>], varargs: &[Option<PyArg<'py>>],
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<Self::Varargs> { ) -> PyResult<Self::Varargs> {
let extra_arguments = varargs.len(); let extra_arguments = varargs.len();
@ -610,7 +657,7 @@ impl<'py> VarargsHandler<'py> for NoVarargs {
#[inline] #[inline]
fn handle_varargs_tuple( fn handle_varargs_tuple(
args: &'py PyTuple, args: &Bound<'py, PyTuple>,
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<Self::Varargs> { ) -> PyResult<Self::Varargs> {
let positional_parameter_count = function_description.positional_parameter_names.len(); let positional_parameter_count = function_description.positional_parameter_names.len();
@ -627,19 +674,19 @@ impl<'py> VarargsHandler<'py> for NoVarargs {
pub struct TupleVarargs; pub struct TupleVarargs;
impl<'py> VarargsHandler<'py> for TupleVarargs { impl<'py> VarargsHandler<'py> for TupleVarargs {
type Varargs = &'py PyTuple; type Varargs = Bound<'py, PyTuple>;
#[inline] #[inline]
fn handle_varargs_fastcall( fn handle_varargs_fastcall(
py: Python<'py>, py: Python<'py>,
varargs: &[Option<&PyAny>], varargs: &[Option<PyArg<'py>>],
_function_description: &FunctionDescription, _function_description: &FunctionDescription,
) -> PyResult<Self::Varargs> { ) -> PyResult<Self::Varargs> {
Ok(PyTuple::new_bound(py, varargs).into_gil_ref()) Ok(PyTuple::new_bound(py, varargs))
} }
#[inline] #[inline]
fn handle_varargs_tuple( fn handle_varargs_tuple(
args: &'py PyTuple, args: &Bound<'py, PyTuple>,
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<Self::Varargs> { ) -> PyResult<Self::Varargs> {
let positional_parameters = function_description.positional_parameter_names.len(); let positional_parameters = function_description.positional_parameter_names.len();
@ -652,8 +699,8 @@ pub trait VarkeywordsHandler<'py> {
type Varkeywords: Default; type Varkeywords: Default;
fn handle_varkeyword( fn handle_varkeyword(
varkeywords: &mut Self::Varkeywords, varkeywords: &mut Self::Varkeywords,
name: &'py PyAny, name: PyArg<'py>,
value: &'py PyAny, value: PyArg<'py>,
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<()>; ) -> PyResult<()>;
} }
@ -666,8 +713,8 @@ impl<'py> VarkeywordsHandler<'py> for NoVarkeywords {
#[inline] #[inline]
fn handle_varkeyword( fn handle_varkeyword(
_varkeywords: &mut Self::Varkeywords, _varkeywords: &mut Self::Varkeywords,
name: &'py PyAny, name: PyArg<'py>,
_value: &'py PyAny, _value: PyArg<'py>,
function_description: &FunctionDescription, function_description: &FunctionDescription,
) -> PyResult<()> { ) -> PyResult<()> {
Err(function_description.unexpected_keyword_argument(name)) Err(function_description.unexpected_keyword_argument(name))
@ -678,28 +725,29 @@ impl<'py> VarkeywordsHandler<'py> for NoVarkeywords {
pub struct DictVarkeywords; pub struct DictVarkeywords;
impl<'py> VarkeywordsHandler<'py> for DictVarkeywords { impl<'py> VarkeywordsHandler<'py> for DictVarkeywords {
type Varkeywords = Option<&'py PyDict>; type Varkeywords = Option<Bound<'py, PyDict>>;
#[inline] #[inline]
fn handle_varkeyword( fn handle_varkeyword(
varkeywords: &mut Self::Varkeywords, varkeywords: &mut Self::Varkeywords,
name: &'py PyAny, name: PyArg<'py>,
value: &'py PyAny, value: PyArg<'py>,
_function_description: &FunctionDescription, _function_description: &FunctionDescription,
) -> PyResult<()> { ) -> PyResult<()> {
varkeywords varkeywords
.get_or_insert_with(|| PyDict::new_bound(name.py()).into_gil_ref()) .get_or_insert_with(|| PyDict::new_bound(name.py()))
.set_item(name, value) .set_item(name, value)
} }
} }
fn push_parameter_list(msg: &mut String, parameter_names: &[&str]) { fn push_parameter_list(msg: &mut String, parameter_names: &[&str]) {
let len = parameter_names.len();
for (i, parameter) in parameter_names.iter().enumerate() { for (i, parameter) in parameter_names.iter().enumerate() {
if i != 0 { if i != 0 {
if parameter_names.len() > 2 { if len > 2 {
msg.push(','); msg.push(',');
} }
if i == parameter_names.len() - 1 { if i == len - 1 {
msg.push_str(" and ") msg.push_str(" and ")
} else { } else {
msg.push(' ') msg.push(' ')
@ -778,7 +826,7 @@ mod tests {
}; };
assert_eq!( assert_eq!(
err.to_string(), err.to_string(),
"TypeError: 'int' object cannot be converted to 'PyString'" "TypeError: example() got an unexpected keyword argument '1'"
); );
}) })
} }

View File

@ -38,14 +38,15 @@ pub type ipowfunc = unsafe extern "C" fn(
impl IPowModulo { impl IPowModulo {
#[cfg(Py_3_8)] #[cfg(Py_3_8)]
#[inline] #[inline]
pub fn to_borrowed_any(self, py: Python<'_>) -> &PyAny { pub fn as_ptr(self) -> *mut ffi::PyObject {
unsafe { py.from_borrowed_ptr::<PyAny>(self.0) } self.0
} }
#[cfg(not(Py_3_8))] #[cfg(not(Py_3_8))]
#[inline] #[inline]
pub fn to_borrowed_any(self, py: Python<'_>) -> &PyAny { pub fn as_ptr(self) -> *mut ffi::PyObject {
unsafe { py.from_borrowed_ptr::<PyAny>(ffi::Py_None()) } // Safety: returning a borrowed pointer to Python `None` singleton
unsafe { ffi::Py_None() }
} }
} }
@ -560,3 +561,11 @@ impl<T> From<BoundRef<'_, '_, T>> for Py<T> {
bound.0.clone().unbind() bound.0.clone().unbind()
} }
} }
impl<'py, T> std::ops::Deref for BoundRef<'_, 'py, T> {
type Target = Bound<'py, T>;
#[inline]
fn deref(&self) -> &Self::Target {
self.0
}
}

View File

@ -524,6 +524,16 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> {
} }
} }
impl<'a, 'py> Borrowed<'a, 'py, PyDict> {
/// Iterates over the contents of this dictionary without incrementing reference counts.
///
/// # Safety
/// It must be known that this dictionary will not be modified during iteration.
pub(crate) unsafe fn iter_borrowed(self) -> BorrowedDictIter<'a, 'py> {
BorrowedDictIter::new(self)
}
}
fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t { fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
#[cfg(any(not(Py_3_8), PyPy, Py_LIMITED_API))] #[cfg(any(not(Py_3_8), PyPy, Py_LIMITED_API))]
unsafe { unsafe {
@ -662,6 +672,64 @@ impl<'py> IntoIterator for Bound<'py, PyDict> {
} }
} }
mod borrowed_iter {
use super::*;
/// Variant of the above which is used to iterate the items of the dictionary
/// without incrementing reference counts. This is only safe if it's known
/// that the dictionary will not be modified during iteration.
pub struct BorrowedDictIter<'a, 'py> {
dict: Borrowed<'a, 'py, PyDict>,
ppos: ffi::Py_ssize_t,
len: ffi::Py_ssize_t,
}
impl<'a, 'py> Iterator for BorrowedDictIter<'a, 'py> {
type Item = (Borrowed<'a, 'py, PyAny>, Borrowed<'a, 'py, PyAny>);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut key: *mut ffi::PyObject = std::ptr::null_mut();
let mut value: *mut ffi::PyObject = std::ptr::null_mut();
// Safety: self.dict lives sufficiently long that the pointer is not dangling
if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
!= 0
{
let py = self.dict.py();
self.len -= 1;
// Safety:
// - PyDict_Next returns borrowed values
// - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
Some(unsafe { (key.assume_borrowed(py), value.assume_borrowed(py)) })
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl ExactSizeIterator for BorrowedDictIter<'_, '_> {
fn len(&self) -> usize {
self.len as usize
}
}
impl<'a, 'py> BorrowedDictIter<'a, 'py> {
pub(super) fn new(dict: Borrowed<'a, 'py, PyDict>) -> Self {
let len = dict_len(&dict);
BorrowedDictIter { dict, ppos: 0, len }
}
}
}
pub(crate) use borrowed_iter::BorrowedDictIter;
/// Conversion trait that allows a sequence of tuples to be converted into `PyDict` /// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
/// Primary use case for this trait is `call` and `call_method` methods as keywords argument. /// Primary use case for this trait is `call` and `call_method` methods as keywords argument.
pub trait IntoPyDict: Sized { pub trait IntoPyDict: Sized {

View File

@ -354,7 +354,7 @@ impl<'py> PyStringMethods<'py> for Bound<'py, PyString> {
impl<'a> Borrowed<'a, '_, PyString> { impl<'a> Borrowed<'a, '_, PyString> {
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))] #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn to_str(self) -> PyResult<&'a str> { pub(crate) fn to_str(self) -> PyResult<&'a str> {
// PyUnicode_AsUTF8AndSize only available on limited API starting with 3.10. // PyUnicode_AsUTF8AndSize only available on limited API starting with 3.10.
let mut size: ffi::Py_ssize_t = 0; let mut size: ffi::Py_ssize_t = 0;
let data: *const u8 = let data: *const u8 =

View File

@ -411,7 +411,7 @@ impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
} }
fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> { fn iter_borrowed<'a>(&'a self) -> BorrowedTupleIterator<'a, 'py> {
BorrowedTupleIterator::new(self.as_borrowed()) self.as_borrowed().iter_borrowed()
} }
fn to_list(&self) -> Bound<'py, PyList> { fn to_list(&self) -> Bound<'py, PyList> {
@ -433,6 +433,10 @@ impl<'a, 'py> Borrowed<'a, 'py, PyTuple> {
unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> { unsafe fn get_borrowed_item_unchecked(self, index: usize) -> Borrowed<'a, 'py, PyAny> {
ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t).assume_borrowed(self.py()) ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t).assume_borrowed(self.py())
} }
pub(crate) fn iter_borrowed(self) -> BorrowedTupleIterator<'a, 'py> {
BorrowedTupleIterator::new(self)
}
} }
/// Used by `PyTuple::iter()`. /// Used by `PyTuple::iter()`.