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:
parent
a15e4b1a11
commit
8a12970c96
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)?
|
)?
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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'"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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()`.
|
||||||
|
|
Loading…
Reference in New Issue