From 870a4bb20dad883a50439b0e1b6cf64a7f15e049 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Wed, 20 Mar 2024 22:35:08 +0000 Subject: [PATCH] deprecate GIL refs in function argument (#3847) * deprecate GIL Refs in function arguments * fix deprecated gil refs in function arguments * add notes on deprecations limitations to migration guide * Apply suggestions from code review Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com> * review: Icxolu * fix proto method extract failure for option * fix gil refs in examples --------- Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com> --- examples/getitem/src/lib.rs | 9 +- guide/src/migration.md | 43 ++++++- pyo3-macros-backend/src/method.rs | 118 ++++++++---------- pyo3-macros-backend/src/module.rs | 7 +- pyo3-macros-backend/src/params.rs | 116 +++++++++++++---- pyo3-macros-backend/src/pyclass.rs | 3 +- pyo3-macros-backend/src/pymethod.rs | 94 +++++++++----- pyo3-macros-backend/src/quotes.rs | 4 +- pytests/src/buf_and_str.rs | 6 +- pytests/src/datetime.rs | 34 +++-- pytests/src/dict_iter.rs | 2 +- pytests/src/misc.rs | 4 +- pytests/src/objstore.rs | 2 +- pytests/src/pyclasses.rs | 35 +++--- src/coroutine.rs | 4 +- src/coroutine/waker.rs | 2 +- src/impl_/deprecations.rs | 4 +- src/impl_/extract_argument.rs | 25 +++- src/macros.rs | 6 +- src/pycell.rs | 2 +- src/tests/hygiene/pymethods.rs | 52 ++++---- src/types/bytearray.rs | 8 +- tests/test_arithmetics.rs | 86 ++++++------- tests/test_inheritance.rs | 4 +- tests/test_methods.rs | 81 ++++++------ tests/test_no_imports.rs | 2 +- tests/test_proto_methods.rs | 12 +- tests/test_pyfunction.rs | 6 +- tests/test_sequence.rs | 4 +- tests/test_text_signature.rs | 8 +- tests/test_variable_arguments.rs | 4 +- tests/ui/deprecations.rs | 6 + tests/ui/deprecations.stderr | 52 +++++--- tests/ui/invalid_cancel_handle.stderr | 1 + tests/ui/invalid_frozen_pyclass_borrow.stderr | 13 ++ tests/ui/invalid_pyfunctions.rs | 6 +- tests/ui/invalid_pyfunctions.stderr | 30 ++--- tests/ui/invalid_pymethod_enum.stderr | 13 ++ tests/ui/static_ref.rs | 7 +- tests/ui/static_ref.stderr | 34 +++++ 40 files changed, 597 insertions(+), 352 deletions(-) diff --git a/examples/getitem/src/lib.rs b/examples/getitem/src/lib.rs index eed60076..c3c662ab 100644 --- a/examples/getitem/src/lib.rs +++ b/examples/getitem/src/lib.rs @@ -7,7 +7,7 @@ use std::os::raw::c_long; #[derive(FromPyObject)] enum IntOrSlice<'py> { Int(i32), - Slice(&'py PySlice), + Slice(Bound<'py, PySlice>), } #[pyclass] @@ -23,7 +23,7 @@ impl ExampleContainer { ExampleContainer { max_length: 100 } } - fn __getitem__(&self, key: &PyAny) -> PyResult { + fn __getitem__(&self, key: &Bound<'_, PyAny>) -> PyResult { if let Ok(position) = key.extract::() { return Ok(position); } else if let Ok(slice) = key.downcast::() { @@ -63,7 +63,10 @@ impl ExampleContainer { match idx { IntOrSlice::Slice(slice) => { let index = slice.indices(self.max_length as c_long).unwrap(); - println!("Got a slice! {}-{}, step: {}, value: {}", index.start, index.stop, index.step, value); + println!( + "Got a slice! {}-{}, step: {}, value: {}", + index.start, index.stop, index.step, value + ); } IntOrSlice::Int(index) => { println!("Got an index! {} : value: {}", index, value); diff --git a/guide/src/migration.md b/guide/src/migration.md index 16f9616e..5a362ec4 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -4,6 +4,8 @@ This guide can help you upgrade code through breaking changes from one PyO3 vers For a detailed list of all changes, see the [CHANGELOG](changelog.md). ## from 0.20.* to 0.21 +
+Click to expand PyO3 0.21 introduces a new `Bound<'py, T>` smart pointer which replaces the existing "GIL Refs" API to interact with Python objects. For example, in PyO3 0.20 the reference `&'py PyAny` would be used to interact with Python objects. In PyO3 0.21 the updated type is `Bound<'py, PyAny>`. Making this change moves Rust ownership semantics out of PyO3's internals and into user code. This change fixes [a known soundness edge case of interaction with gevent](https://github.com/PyO3/pyo3/issues/3668) as well as improves CPU and [memory performance](https://github.com/PyO3/pyo3/issues/1056). For a full history of discussion see https://github.com/PyO3/pyo3/issues/3382. @@ -17,8 +19,11 @@ The recommended steps to update to PyO3 0.21 is as follows: 3. Disable the `gil-refs` feature and migrate off the deprecated APIs The following sections are laid out in this order. +
### Enable the `gil-refs` feature +
+Click to expand To make the transition for the PyO3 ecosystem away from the GIL Refs API as smooth as possible, in PyO3 0.21 no APIs consuming or producing GIL Refs have been altered. Instead, variants using `Bound` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound` is the replacement form of `PyTuple::new`. The GIL Ref APIs have been deprecated, but to make migration easier it is possible to disable these deprecation warnings by enabling the `gil-refs` feature. @@ -41,8 +46,11 @@ After: [dependencies] pyo3 = { version = "0.21", features = ["gil-refs"] } ``` +
### `PyTypeInfo` and `PyTryFrom` have been adjusted +
+Click to expand The `PyTryFrom` trait has aged poorly, its `try_from` method now conflicts with `TryFrom::try_from` in the 2021 edition prelude. A lot of its functionality was also duplicated with `PyTypeInfo`. @@ -81,8 +89,11 @@ Python::with_gil(|py| { }) # } ``` +
### `Iter(A)NextOutput` are deprecated +
+Click to expand The `__next__` and `__anext__` magic methods can now return any type convertible into Python objects directly just like all other `#[pymethods]`. The `IterNextOutput` used by `__next__` and `IterANextOutput` used by `__anext__` are subsequently deprecated. Most importantly, this change allows returning an awaitable from `__anext__` without non-sensically wrapping it into `Yield` or `Some`. Only the return types `Option` and `Result, E>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration. @@ -200,20 +211,29 @@ impl PyClassAsyncIter { } } ``` +
### `PyType::name` has been renamed to `PyType::qualname` +
+Click to expand `PyType::name` has been renamed to `PyType::qualname` to indicate that it does indeed return the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name), matching the `__qualname__` attribute. The newly added `PyType::name` yields the full name including the module name now which corresponds to `__module__.__name__` on the level of attributes. +
### `PyCell` has been deprecated +
+Click to expand Interactions with Python objects implemented in Rust no longer need to go though `PyCell`. Instead iteractions with Python object now consistently go through `Bound` or `Py` independently of whether `T` is native Python object or a `#[pyclass]` implemented in Rust. Use `Bound::new` or `Py::new` respectively to create and `Bound::borrow(_mut)` / `Py::borrow(_mut)` to borrow the Rust object. +
-### Migrating from the GIL-Refs API to `Bound` +### Migrating from the GIL Refs API to `Bound` +
+Click to expand -To minimise breakage of code using the GIL-Refs API, the `Bound` smart pointer has been introduced by adding complements to all functions which accept or return GIL Refs. This allows code to migrate by replacing the deprecated APIs with the new ones. +To minimise breakage of code using the GIL Refs API, the `Bound` smart pointer has been introduced by adding complements to all functions which accept or return GIL Refs. This allows code to migrate by replacing the deprecated APIs with the new ones. -To identify what to migrate, temporarily switch off the `gil-refs` feature to see deprecation warnings on all uses of APIs accepting and producing GIL Refs. Over one or more PRs it should be possible to follow the deprecation hints to update code. Depending on your development environment, switching off the `gil-refs` feature may introduce [some very targeted breakages](#deactivating-the-gil-refs-feature), so you may need to fixup those first. +To identify what to migrate, temporarily switch off the `gil-refs` feature to see deprecation warnings on [almost](#cases-where-pyo3-cannot-emit-gil-ref-deprecation-warnings) all uses of APIs accepting and producing GIL Refs . Over one or more PRs it should be possible to follow the deprecation hints to update code. Depending on your development environment, switching off the `gil-refs` feature may introduce [some very targeted breakages](#deactivating-the-gil-refs-feature), so you may need to fixup those first. For example, the following APIs have gained updated variants: - `PyList::new`, `PyTyple::new` and similar constructors have replacements `PyList::new_bound`, `PyTuple::new_bound` etc. @@ -276,7 +296,19 @@ impl<'py> FromPyObject<'py> for MyType { The expectation is that in 0.22 `extract_bound` will have the default implementation removed and in 0.23 `extract` will be removed. +#### Cases where PyO3 cannot emit GIL Ref deprecation warnings + +Despite a large amount of deprecations warnings produced by PyO3 to aid with the transition from GIL Refs to the Bound API, there are a few cases where PyO3 cannot automatically warn on uses of GIL Refs. It is worth checking for these cases manually after the deprecation warnings have all been addressed: + +- Individual implementations of the `FromPyObject` trait cannot be deprecated, so PyO3 cannot warn about uses of code patterns like `.extract<&PyAny>()` which produce a GIL Ref. +- GIL Refs in `#[pyfunction]` arguments emit a warning, but if the GIL Ref is wrapped inside another container such as `Option<&PyAny>` or `Vec<&PyAny>` then PyO3 cannot warn against this. +- The `wrap_pyfunction!(function)(py)` deferred argument form of the `wrap_pyfunction` macro taking `py: Python<'py>` produces a GIL Ref, and due to limitations in type inference PyO3 cannot warn against this specific case. + +
+ ### Deactivating the `gil-refs` feature +
+Click to expand As a final step of migration, deactivating the `gil-refs` feature will set up code for best performance and is intended to set up a forward-compatible API for PyO3 0.22. @@ -284,9 +316,10 @@ At this point code which needed to manage GIL Ref memory can safely remove uses There is one notable API removed when this feature is disabled. `FromPyObject` trait implementations for types which borrow directly from the input data cannot be implemented by PyO3 without GIL Refs (while the migration is ongoing). These types are `&str`, `Cow<'_, str>`, `&[u8]`, `Cow<'_, u8>`. -To ease pain during migration, these types instead implement a new temporary trait `FromPyObjectBound` which is the expected future form of `FromPyObject`. The new temporary trait ensures is that `obj.extract::<&str>()` continues to work (with the new constraint that the extracted value now depends on the input `obj` lifetime), as well for these types in `#[pyfunction]` arguments. +To ease pain during migration, these types instead implement a new temporary trait `FromPyObjectBound` which is the expected future form of `FromPyObject`. The new temporary trait ensures is that `obj.extract::<&str>()` continues to work, as well for these types in `#[pyfunction]` arguments. -An unfortunate final point here is that PyO3 cannot offer this new implementation for `&str` on `abi3` builds for Python older than 3.10. On code which needs `abi3` builds for these older Python versions, many cases of `.extract::<&str>()` may need to be replaced with `.extract::()`, which is string data which borrows from the Python `str` object. Alternatively, use `.extract::>()`, `.extract::()` to copy the data into Rust for these versions. +An unfortunate final point here is that PyO3 cannot offer this new implementation for `&str` on `abi3` builds for Python older than 3.10. On code which needs `abi3` builds for these older Python versions, many cases of `.extract::<&str>()` may need to be replaced with `.extract::()`, which is string data which borrows from the Python `str` object. Alternatively, use `.extract::>()` ro `.extract::()` to copy the data into Rust for these versions. +
## from 0.19.* to 0.20 diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 93a54395..1a46d333 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -8,7 +8,7 @@ use crate::utils::Ctx; use crate::{ attributes::{TextSignatureAttribute, TextSignatureAttributeValue}, deprecations::{Deprecation, Deprecations}, - params::impl_arg_params, + params::{impl_arg_params, Holders}, pyfunction::{ FunctionSignature, PyFunctionArgPyO3Attributes, PyFunctionOptions, SignatureAttribute, }, @@ -108,7 +108,7 @@ impl FnType { &self, cls: Option<&syn::Type>, error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> TokenStream { let Ctx { pyo3_path } = ctx; @@ -186,7 +186,7 @@ impl SelfType { &self, cls: &syn::Type, error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> TokenStream { // Due to use of quote_spanned in this function, need to bind these idents to the @@ -201,17 +201,12 @@ impl SelfType { } else { syn::Ident::new("extract_pyclass_ref", *span) }; - let holder = syn::Ident::new(&format!("holder_{}", holders.len()), *span); + let holder = holders.push_holder(*span); let pyo3_path = pyo3_path.to_tokens_spanned(*span); - holders.push(quote_spanned! { *span => - #[allow(clippy::let_unit_value)] - let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT; - let mut #slf = #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf); - }); error_mode.handle_error( quote_spanned! { *span => #pyo3_path::impl_::extract_argument::#method::<#cls>( - &#slf, + #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).0, &mut #holder, ) }, @@ -519,10 +514,8 @@ impl<'a> FnSpec<'a> { ); } - let rust_call = |args: Vec, - self_e: &syn::Ident, - holders: &mut Vec| { - let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx); + let rust_call = |args: Vec, holders: &mut Holders| { + let mut self_arg = || self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx); let call = if self.asyncness.is_some() { let throw_callback = if cancel_handle.is_some() { @@ -537,35 +530,30 @@ impl<'a> FnSpec<'a> { }; let future = match self.tp { FnType::Fn(SelfType::Receiver { mutable: false, .. }) => { - holders.pop().unwrap(); // does not actually use holder created by `self_arg` - quote! {{ - #self_e = #pyo3_path::impl_::deprecations::GilRefs::<()>::new(); let __guard = #pyo3_path::impl_::coroutine::RefGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?; async move { function(&__guard, #(#args),*).await } }} } FnType::Fn(SelfType::Receiver { mutable: true, .. }) => { - holders.pop().unwrap(); // does not actually use holder created by `self_arg` - quote! {{ - #self_e = #pyo3_path::impl_::deprecations::GilRefs::<()>::new(); let mut __guard = #pyo3_path::impl_::coroutine::RefMutGuard::<#cls>::new(&#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf))?; async move { function(&mut __guard, #(#args),*).await } }} } _ => { + let self_arg = self_arg(); if self_arg.is_empty() { - quote! {{ - #self_e = #pyo3_path::impl_::deprecations::GilRefs::<()>::new(); - function(#(#args),*) - }} + quote! { function(#(#args),*) } } else { - quote! { function({ - let (self_arg, e) = #pyo3_path::impl_::deprecations::inspect_type(#self_arg); - #self_e = e; - self_arg - }, #(#args),*) } + let self_checker = holders.push_gil_refs_checker(self_arg.span()); + quote! { + function( + // NB #self_arg includes a comma, so none inserted here + #pyo3_path::impl_::deprecations::inspect_type(#self_arg &#self_checker), + #(#args),* + ) + } } } }; @@ -586,24 +574,22 @@ impl<'a> FnSpec<'a> { }}; } call - } else if self_arg.is_empty() { - quote! {{ - #self_e = #pyo3_path::impl_::deprecations::GilRefs::<()>::new(); - function(#(#args),*) - }} } else { - quote! { - function({ - let (self_arg, e) = #pyo3_path::impl_::deprecations::inspect_type(#self_arg); - #self_e = e; - self_arg - }, #(#args),*) + let self_arg = self_arg(); + if self_arg.is_empty() { + quote! { function(#(#args),*) } + } else { + let self_checker = holders.push_gil_refs_checker(self_arg.span()); + quote! { + function( + // NB #self_arg includes a comma, so none inserted here + #pyo3_path::impl_::deprecations::inspect_type(#self_arg &#self_checker), + #(#args),* + ) + } } }; - ( - quotes::map_result_into_ptr(quotes::ok_wrap(call, ctx), ctx), - self_arg.span(), - ) + quotes::map_result_into_ptr(quotes::ok_wrap(call, ctx), ctx) }; let func_name = &self.name; @@ -613,10 +599,9 @@ impl<'a> FnSpec<'a> { quote!(#func_name) }; - let self_e = syn::Ident::new("self_e", Span::call_site()); Ok(match self.convention { CallingConvention::Noargs => { - let mut holders = Vec::new(); + let mut holders = Holders::new(); let args = self .signature .arguments @@ -631,8 +616,9 @@ impl<'a> FnSpec<'a> { } }) .collect(); - let (call, self_arg_span) = rust_call(args, &self_e, &mut holders); - let function_arg = quote_spanned! { self_arg_span => #self_e.function_arg(); }; + let call = rust_call(args, &mut holders); + let check_gil_refs = holders.check_gil_refs(); + let init_holders = holders.init_holders(ctx); quote! { unsafe fn #ident<'py>( @@ -641,19 +627,19 @@ impl<'a> FnSpec<'a> { ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> { let _slf_ref = &_slf; let function = #rust_name; // Shadow the function name to avoid #3017 - let #self_e; - #( #holders )* + #init_holders let result = #call; - #function_arg + #check_gil_refs result } } } CallingConvention::Fastcall => { - let mut holders = Vec::new(); + let mut holders = Holders::new(); let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders, ctx)?; - let (call, self_arg_span) = rust_call(args, &self_e, &mut holders); - let function_arg = quote_spanned! { self_arg_span => #self_e.function_arg(); }; + let call = rust_call(args, &mut holders); + let init_holders = holders.init_holders(ctx); + let check_gil_refs = holders.check_gil_refs(); quote! { unsafe fn #ident<'py>( @@ -665,20 +651,20 @@ impl<'a> FnSpec<'a> { ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> { let _slf_ref = &_slf; let function = #rust_name; // Shadow the function name to avoid #3017 - let #self_e; #arg_convert - #( #holders )* + #init_holders let result = #call; - #function_arg + #check_gil_refs result } } } CallingConvention::Varargs => { - let mut holders = Vec::new(); + let mut holders = Holders::new(); let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx)?; - let (call, self_arg_span) = rust_call(args, &self_e, &mut holders); - let function_arg = quote_spanned! { self_arg_span => #self_e.function_arg(); }; + let call = rust_call(args, &mut holders); + let init_holders = holders.init_holders(ctx); + let check_gil_refs = holders.check_gil_refs(); quote! { unsafe fn #ident<'py>( @@ -689,22 +675,23 @@ impl<'a> FnSpec<'a> { ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> { let _slf_ref = &_slf; let function = #rust_name; // Shadow the function name to avoid #3017 - let #self_e; #arg_convert - #( #holders )* + #init_holders let result = #call; - #function_arg + #check_gil_refs result } } } CallingConvention::TpNew => { - let mut holders = Vec::new(); + let mut holders = Holders::new(); let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx)?; let self_arg = self .tp .self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx); let call = quote! { #rust_name(#self_arg #(#args),*) }; + let init_holders = holders.init_holders(ctx); + let check_gil_refs = holders.check_gil_refs(); quote! { unsafe fn #ident( py: #pyo3_path::Python<'_>, @@ -716,9 +703,10 @@ impl<'a> FnSpec<'a> { let _slf_ref = &_slf; let function = #rust_name; // Shadow the function name to avoid #3017 #arg_convert - #( #holders )* + #init_holders let result = #call; let initializer: #pyo3_path::PyClassInitializer::<#cls> = result.convert(py)?; + #check_gil_refs #pyo3_path::impl_::pymethods::tp_new_impl(py, initializer, _slf) } } diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index 2515d519..dc2b3bfc 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -305,10 +305,11 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result .filter_map(|param| { if let syn::FnArg::Typed(pat_type) = param { if let syn::Pat::Ident(pat_ident) = &*pat_type.pat { - let ident = &pat_ident.ident; + let ident: &syn::Ident = &pat_ident.ident; return Some([ - parse_quote! { let (#ident, e) = #pyo3_path::impl_::deprecations::inspect_type(#ident); }, - parse_quote_spanned! { pat_type.span() => e.function_arg(); }, + parse_quote!{ let check_gil_refs = #pyo3_path::impl_::deprecations::GilRefs::new(); }, + parse_quote! { let #ident = #pyo3_path::impl_::deprecations::inspect_type(#ident, &check_gil_refs); }, + parse_quote_spanned! { pat_type.span() => check_gil_refs.function_arg(); }, ]); } } diff --git a/pyo3-macros-backend/src/params.rs b/pyo3-macros-backend/src/params.rs index ae1661fe..cab28698 100644 --- a/pyo3-macros-backend/src/params.rs +++ b/pyo3-macros-backend/src/params.rs @@ -9,6 +9,53 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::Result; +pub struct Holders { + holders: Vec, + gil_refs_checkers: Vec, +} + +impl Holders { + pub fn new() -> Self { + Holders { + holders: Vec::new(), + gil_refs_checkers: Vec::new(), + } + } + + pub fn push_holder(&mut self, span: Span) -> syn::Ident { + let holder = syn::Ident::new(&format!("holder_{}", self.holders.len()), span); + self.holders.push(holder.clone()); + holder + } + + pub fn push_gil_refs_checker(&mut self, span: Span) -> syn::Ident { + let gil_refs_checker = syn::Ident::new( + &format!("gil_refs_checker_{}", self.gil_refs_checkers.len()), + span, + ); + self.gil_refs_checkers.push(gil_refs_checker.clone()); + gil_refs_checker + } + + pub fn init_holders(&self, ctx: &Ctx) -> TokenStream { + let Ctx { pyo3_path } = ctx; + let holders = &self.holders; + let gil_refs_checkers = &self.gil_refs_checkers; + quote! { + #[allow(clippy::let_unit_value)] + #(let mut #holders = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT;)* + #(let #gil_refs_checkers = #pyo3_path::impl_::deprecations::GilRefs::new();)* + } + } + + pub fn check_gil_refs(&self) -> TokenStream { + self.gil_refs_checkers + .iter() + .map(|e| quote_spanned! { e.span() => #e.function_arg(); }) + .collect() + } +} + /// Return true if the argument list is simply (*args, **kwds). pub fn is_forwarded_args(signature: &FunctionSignature<'_>) -> bool { matches!( @@ -26,11 +73,22 @@ pub fn is_forwarded_args(signature: &FunctionSignature<'_>) -> bool { ) } +fn check_arg_for_gil_refs( + tokens: TokenStream, + gil_refs_checker: syn::Ident, + ctx: &Ctx, +) -> TokenStream { + let Ctx { pyo3_path } = ctx; + quote! { + #pyo3_path::impl_::deprecations::inspect_type(#tokens, &#gil_refs_checker) + } +} + pub fn impl_arg_params( spec: &FnSpec<'_>, self_: Option<&syn::Type>, fastcall: bool, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> Result<(TokenStream, Vec)> { let args_array = syn::Ident::new("output", Span::call_site()); @@ -61,7 +119,15 @@ pub fn impl_arg_params( .arguments .iter() .enumerate() - .map(|(i, arg)| impl_arg_param(arg, i, &mut 0, &args_array, holders, ctx)) + .map(|(i, arg)| { + impl_arg_param(arg, i, &mut 0, &args_array, holders, ctx).map(|tokens| { + check_arg_for_gil_refs( + tokens, + holders.push_gil_refs_checker(arg.ty.span()), + ctx, + ) + }) + }) .collect::>()?; return Ok(( quote! { @@ -101,7 +167,11 @@ pub fn impl_arg_params( .arguments .iter() .enumerate() - .map(|(i, arg)| impl_arg_param(arg, i, &mut option_pos, &args_array, holders, ctx)) + .map(|(i, arg)| { + impl_arg_param(arg, i, &mut option_pos, &args_array, holders, ctx).map(|tokens| { + check_arg_for_gil_refs(tokens, holders.push_gil_refs_checker(arg.ty.span()), ctx) + }) + }) .collect::>()?; let args_handler = if spec.signature.python_signature.varargs.is_some() { @@ -169,7 +239,7 @@ fn impl_arg_param( pos: usize, option_pos: &mut usize, args_array: &syn::Ident, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> Result { let Ctx { pyo3_path } = ctx; @@ -192,21 +262,12 @@ fn impl_arg_param( let name = arg.name; let name_str = name.to_string(); - let mut push_holder = || { - let holder = syn::Ident::new(&format!("holder_{}", holders.len()), arg.ty.span()); - holders.push(quote_arg_span! { - #[allow(clippy::let_unit_value)] - let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT; - }); - holder - }; - if arg.is_varargs { ensure_spanned!( arg.optional.is_none(), arg.name.span() => "args cannot be optional" ); - let holder = push_holder(); + let holder = holders.push_holder(arg.ty.span()); return Ok(quote_arg_span! { #pyo3_path::impl_::extract_argument::extract_argument( &_args, @@ -219,7 +280,7 @@ fn impl_arg_param( arg.optional.is_some(), arg.name.span() => "kwargs must be Option<_>" ); - let holder = push_holder(); + let holder = holders.push_holder(arg.name.span()); return Ok(quote_arg_span! { #pyo3_path::impl_::extract_argument::extract_optional_argument( _kwargs.as_deref(), @@ -254,12 +315,14 @@ fn impl_arg_param( let from_py_with = syn::Ident::new(&format!("from_py_with_{}", pos), Span::call_site()); if let Some(default) = default { quote_arg_span! { - #[allow(clippy::redundant_closure)] #pyo3_path::impl_::extract_argument::from_py_with_with_default( #arg_value.as_deref(), #name_str, #from_py_with as fn(_) -> _, - || #default + #[allow(clippy::redundant_closure)] + { + || #default + } )? } } else { @@ -272,29 +335,33 @@ fn impl_arg_param( } } } else if arg.optional.is_some() { - let holder = push_holder(); + let holder = holders.push_holder(arg.name.span()); quote_arg_span! { - #[allow(clippy::redundant_closure)] #pyo3_path::impl_::extract_argument::extract_optional_argument( #arg_value.as_deref(), &mut #holder, #name_str, - || #default + #[allow(clippy::redundant_closure)] + { + || #default + } )? } } else if let Some(default) = default { - let holder = push_holder(); + let holder = holders.push_holder(arg.name.span()); quote_arg_span! { - #[allow(clippy::redundant_closure)] #pyo3_path::impl_::extract_argument::extract_argument_with_default( #arg_value.as_deref(), &mut #holder, #name_str, - || #default + #[allow(clippy::redundant_closure)] + { + || #default + } )? } } else { - let holder = push_holder(); + let holder = holders.push_holder(arg.name.span()); quote_arg_span! { #pyo3_path::impl_::extract_argument::extract_argument( &#pyo3_path::impl_::extract_argument::unwrap_required_argument(#arg_value), @@ -303,5 +370,6 @@ fn impl_arg_param( )? } }; + Ok(tokens) } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 27a7ee96..5122d205 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -736,10 +736,11 @@ fn impl_simple_enum( fn __pyo3__richcmp__( &self, py: #pyo3_path::Python, - other: &#pyo3_path::PyAny, + other: &#pyo3_path::Bound<'_, #pyo3_path::PyAny>, op: #pyo3_path::basic::CompareOp ) -> #pyo3_path::PyResult<#pyo3_path::PyObject> { use #pyo3_path::conversion::ToPyObject; + use #pyo3_path::types::PyAnyMethods; use ::core::result::Result::*; match op { #pyo3_path::basic::CompareOp::Eq => { diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 1c19112d..4e6f46d9 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use crate::attributes::{NameAttribute, RenamingRule}; use crate::method::{CallingConvention, ExtractErrorMode}; +use crate::params::Holders; use crate::utils::Ctx; use crate::utils::PythonDoc; use crate::{ @@ -510,7 +511,7 @@ fn impl_call_setter( cls: &syn::Type, spec: &FnSpec<'_>, self_type: &SelfType, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> syn::Result { let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); @@ -544,7 +545,7 @@ pub fn impl_py_setter_def( let Ctx { pyo3_path } = ctx; let python_name = property_type.null_terminated_python_name()?; let doc = property_type.doc(); - let mut holders = Vec::new(); + let mut holders = Holders::new(); let setter_impl = match property_type { PropertyType::Descriptor { field_index, field, .. @@ -596,6 +597,8 @@ pub fn impl_py_setter_def( } } + let init_holders = holders.init_holders(ctx); + let check_gil_refs = holders.check_gil_refs(); let associated_method = quote! { #cfg_attrs unsafe fn #wrapper_ident( @@ -609,8 +612,10 @@ pub fn impl_py_setter_def( #pyo3_path::exceptions::PyAttributeError::new_err("can't delete attribute") })?; let _val = #pyo3_path::FromPyObject::extract_bound(_value.into())?; - #( #holders )* - #pyo3_path::callback::convert(py, #setter_impl) + #init_holders + let result = #setter_impl; + #check_gil_refs + #pyo3_path::callback::convert(py, result) } }; @@ -635,7 +640,7 @@ fn impl_call_getter( cls: &syn::Type, spec: &FnSpec<'_>, self_type: &SelfType, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> syn::Result { let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); @@ -665,7 +670,7 @@ pub fn impl_py_getter_def( let python_name = property_type.null_terminated_python_name()?; let doc = property_type.doc(); - let mut holders = Vec::new(); + let mut holders = Holders::new(); let body = match property_type { PropertyType::Descriptor { field_index, field, .. @@ -731,14 +736,17 @@ pub fn impl_py_getter_def( } } + let init_holders = holders.init_holders(ctx); + let check_gil_refs = holders.check_gil_refs(); let associated_method = quote! { #cfg_attrs unsafe fn #wrapper_ident( py: #pyo3_path::Python<'_>, _slf: *mut #pyo3_path::ffi::PyObject ) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> { - #( #holders )* + #init_holders let result = #body; + #check_gil_refs result } }; @@ -966,7 +974,7 @@ impl Ty { ident: &syn::Ident, arg: &FnArg<'_>, extract_error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> TokenStream { let Ctx { pyo3_path } = ctx; @@ -976,7 +984,9 @@ impl Ty { extract_error_mode, holders, &name_str, - quote! { #ident },ctx + quote! { #ident }, + arg.ty.span(), + ctx ), Ty::MaybeNullObject => extract_object( extract_error_mode, @@ -988,32 +998,40 @@ impl Ty { } else { #ident } - },ctx + }, + arg.ty.span(), + ctx ), Ty::NonNullObject => extract_object( extract_error_mode, holders, &name_str, - quote! { #ident.as_ptr() },ctx + quote! { #ident.as_ptr() }, + arg.ty.span(), + ctx ), Ty::IPowModulo => extract_object( extract_error_mode, holders, &name_str, - quote! { #ident.as_ptr() },ctx + quote! { #ident.as_ptr() }, + arg.ty.span(), + ctx ), Ty::CompareOp => extract_error_mode.handle_error( quote! { #pyo3_path::class::basic::CompareOp::from_raw(#ident) .ok_or_else(|| #pyo3_path::exceptions::PyValueError::new_err("invalid comparison operator")) - },ctx + }, + ctx ), Ty::PySsizeT => { let ty = arg.ty; extract_error_mode.handle_error( quote! { ::std::convert::TryInto::<#ty>::try_into(#ident).map_err(|e| #pyo3_path::exceptions::PyValueError::new_err(e.to_string())) - },ctx + }, + ctx ) } // Just pass other types through unmodified @@ -1024,27 +1042,28 @@ impl Ty { fn extract_object( extract_error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, name: &str, source_ptr: TokenStream, + span: Span, ctx: &Ctx, ) -> TokenStream { let Ctx { pyo3_path } = ctx; - let holder = syn::Ident::new(&format!("holder_{}", holders.len()), Span::call_site()); - holders.push(quote! { - #[allow(clippy::let_unit_value)] - let mut #holder = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT; - }); - extract_error_mode.handle_error( + let holder = holders.push_holder(Span::call_site()); + let gil_refs_checker = holders.push_gil_refs_checker(span); + let extracted = extract_error_mode.handle_error( quote! { #pyo3_path::impl_::extract_argument::extract_argument( - &#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr), + #pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0, &mut #holder, #name ) }, ctx, - ) + ); + quote! { + #pyo3_path::impl_::deprecations::inspect_type(#extracted, &#gil_refs_checker) + } } enum ReturnMode { @@ -1054,13 +1073,15 @@ enum ReturnMode { } impl ReturnMode { - fn return_call_output(&self, call: TokenStream, ctx: &Ctx) -> TokenStream { + fn return_call_output(&self, call: TokenStream, ctx: &Ctx, holders: &Holders) -> TokenStream { let Ctx { pyo3_path } = ctx; + let check_gil_refs = holders.check_gil_refs(); match self { ReturnMode::Conversion(conversion) => { let conversion = TokenGeneratorCtx(*conversion, ctx); quote! { let _result: #pyo3_path::PyResult<#conversion> = #pyo3_path::callback::convert(py, #call); + #check_gil_refs #pyo3_path::callback::convert(py, _result) } } @@ -1070,12 +1091,14 @@ impl ReturnMode { quote! { let _result = #call; use #pyo3_path::impl_::pymethods::{#traits}; + #check_gil_refs (&_result).#tag().convert(py, _result) } } ReturnMode::ReturnSelf => quote! { let _result: #pyo3_path::PyResult<()> = #pyo3_path::callback::convert(py, #call); _result?; + #check_gil_refs #pyo3_path::ffi::Py_XINCREF(_raw_slf); ::std::result::Result::Ok(_raw_slf) }, @@ -1176,7 +1199,7 @@ impl SlotDef { .collect(); let wrapper_ident = format_ident!("__pymethod_{}__", method_name); let ret_ty = ret_ty.ffi_type(ctx); - let mut holders = Vec::new(); + let mut holders = Holders::new(); let body = generate_method_body( cls, spec, @@ -1187,6 +1210,7 @@ impl SlotDef { ctx, )?; let name = spec.name; + let holders = holders.init_holders(ctx); let associated_method = quote! { unsafe fn #wrapper_ident( py: #pyo3_path::Python<'_>, @@ -1195,7 +1219,7 @@ impl SlotDef { ) -> #pyo3_path::PyResult<#ret_ty> { let function = #cls::#name; // Shadow the method name to avoid #3017 let _slf = _raw_slf; - #( #holders )* + #holders #body } }; @@ -1229,7 +1253,7 @@ fn generate_method_body( spec: &FnSpec<'_>, arguments: &[Ty], extract_error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, return_mode: Option<&ReturnMode>, ctx: &Ctx, ) -> Result { @@ -1241,9 +1265,14 @@ fn generate_method_body( let args = extract_proto_arguments(spec, arguments, extract_error_mode, holders, ctx)?; let call = quote! { #cls::#rust_name(#self_arg #(#args),*) }; Ok(if let Some(return_mode) = return_mode { - return_mode.return_call_output(call, ctx) + return_mode.return_call_output(call, ctx, holders) } else { - quote! { #pyo3_path::callback::convert(py, #call) } + let check_gil_refs = holders.check_gil_refs(); + quote! { + let result = #call; + #check_gil_refs; + #pyo3_path::callback::convert(py, result) + } }) } @@ -1294,7 +1323,7 @@ impl SlotFragmentDef { let arg_idents: &Vec<_> = &(0..arguments.len()) .map(|i| format_ident!("arg{}", i)) .collect(); - let mut holders = Vec::new(); + let mut holders = Holders::new(); let body = generate_method_body( cls, spec, @@ -1305,6 +1334,7 @@ impl SlotFragmentDef { ctx, )?; let ret_ty = ret_ty.ffi_type(ctx); + let holders = holders.init_holders(ctx); Ok(quote! { impl #cls { unsafe fn #wrapper_ident( @@ -1313,7 +1343,7 @@ impl SlotFragmentDef { #(#arg_idents: #arg_types),* ) -> #pyo3_path::PyResult<#ret_ty> { let _slf = _raw_slf; - #( #holders )* + #holders #body } } @@ -1412,7 +1442,7 @@ fn extract_proto_arguments( spec: &FnSpec<'_>, proto_args: &[Ty], extract_error_mode: ExtractErrorMode, - holders: &mut Vec, + holders: &mut Holders, ctx: &Ctx, ) -> Result> { let mut args = Vec::with_capacity(spec.signature.arguments.len()); diff --git a/pyo3-macros-backend/src/quotes.rs b/pyo3-macros-backend/src/quotes.rs index 0219cb9a..ceef23fb 100644 --- a/pyo3-macros-backend/src/quotes.rs +++ b/pyo3-macros-backend/src/quotes.rs @@ -19,7 +19,5 @@ pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream { pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream { let Ctx { pyo3_path } = ctx; - quote! { - #pyo3_path::impl_::wrap::map_result_into_ptr(py, #result) - } + quote! { #pyo3_path::impl_::wrap::map_result_into_ptr(py, #result) } } diff --git a/pytests/src/buf_and_str.rs b/pytests/src/buf_and_str.rs index e9651e0c..879d76af 100644 --- a/pytests/src/buf_and_str.rs +++ b/pytests/src/buf_and_str.rs @@ -17,19 +17,19 @@ impl BytesExtractor { } #[staticmethod] - pub fn from_bytes(bytes: &PyBytes) -> PyResult { + pub fn from_bytes(bytes: &Bound<'_, PyBytes>) -> PyResult { let byte_vec: Vec = bytes.extract()?; Ok(byte_vec.len()) } #[staticmethod] - pub fn from_str(string: &PyString) -> PyResult { + pub fn from_str(string: &Bound<'_, PyString>) -> PyResult { let rust_string: String = string.extract()?; Ok(rust_string.len()) } #[staticmethod] - pub fn from_str_lossy(string: &PyString) -> usize { + pub fn from_str_lossy(string: &Bound<'_, PyString>) -> usize { let rust_string_lossy: String = string.to_string_lossy().to_string(); rust_string_lossy.len() } diff --git a/pytests/src/datetime.rs b/pytests/src/datetime.rs index aeb57240..d0de99ae 100644 --- a/pytests/src/datetime.rs +++ b/pytests/src/datetime.rs @@ -12,8 +12,11 @@ fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult(py: Python<'p>, d: &PyDate) -> Bound<'p, PyTuple> { - PyTuple::new_bound(py, [d.get_year(), d.get_month() as i32, d.get_day() as i32]) +fn get_date_tuple<'py>(d: &Bound<'py, PyDate>) -> Bound<'py, PyTuple> { + PyTuple::new_bound( + d.py(), + [d.get_year(), d.get_month() as i32, d.get_day() as i32], + ) } #[pyfunction] @@ -48,9 +51,9 @@ fn time_with_fold<'py>( } #[pyfunction] -fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> { +fn get_time_tuple<'py>(dt: &Bound<'py, PyTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( - py, + dt.py(), [ dt.get_hour() as u32, dt.get_minute() as u32, @@ -61,9 +64,9 @@ fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> { } #[pyfunction] -fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> { +fn get_time_tuple_fold<'py>(dt: &Bound<'py, PyTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( - py, + dt.py(), [ dt.get_hour() as u32, dt.get_minute() as u32, @@ -123,9 +126,9 @@ fn make_datetime<'py>( } #[pyfunction] -fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> { +fn get_datetime_tuple<'py>(dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( - py, + dt.py(), [ dt.get_year(), dt.get_month() as i32, @@ -139,12 +142,9 @@ fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'py, PyDateTime>) -> Boun } #[pyfunction] -fn get_datetime_tuple_fold<'py>( - py: Python<'py>, - dt: &Bound<'py, PyDateTime>, -) -> Bound<'py, PyTuple> { +fn get_datetime_tuple_fold<'py>(dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( - py, + dt.py(), [ dt.get_year(), dt.get_month() as i32, @@ -187,12 +187,8 @@ impl TzClass { TzClass {} } - fn utcoffset<'py>( - &self, - py: Python<'py>, - _dt: &Bound<'py, PyDateTime>, - ) -> PyResult> { - PyDelta::new_bound(py, 0, 3600, 0, true) + fn utcoffset<'py>(&self, dt: &Bound<'py, PyDateTime>) -> PyResult> { + PyDelta::new_bound(dt.py(), 0, 3600, 0, true) } fn tzname(&self, _dt: &Bound<'_, PyDateTime>) -> String { diff --git a/pytests/src/dict_iter.rs b/pytests/src/dict_iter.rs index 985c9297..c312fbb5 100644 --- a/pytests/src/dict_iter.rs +++ b/pytests/src/dict_iter.rs @@ -20,7 +20,7 @@ impl DictSize { DictSize { expected } } - fn iter_dict(&mut self, _py: Python<'_>, dict: &PyDict) -> PyResult { + fn iter_dict(&mut self, _py: Python<'_>, dict: &Bound<'_, PyDict>) -> PyResult { let mut seen = 0u32; for (sym, values) in dict { seen += 1; diff --git a/pytests/src/misc.rs b/pytests/src/misc.rs index 3b893ccd..7704098b 100644 --- a/pytests/src/misc.rs +++ b/pytests/src/misc.rs @@ -8,8 +8,8 @@ fn issue_219() { } #[pyfunction] -fn get_type_full_name(obj: &PyAny) -> PyResult> { - obj.get_type().name() +fn get_type_full_name(obj: &Bound<'_, PyAny>) -> PyResult { + obj.get_type().name().map(Cow::into_owned) } #[pyfunction] diff --git a/pytests/src/objstore.rs b/pytests/src/objstore.rs index 440f29fa..9a005c0e 100644 --- a/pytests/src/objstore.rs +++ b/pytests/src/objstore.rs @@ -13,7 +13,7 @@ impl ObjStore { ObjStore::default() } - fn push(&mut self, py: Python<'_>, obj: &PyAny) { + fn push(&mut self, py: Python<'_>, obj: &Bound<'_, PyAny>) { self.obj.push(obj.to_object(py)); } } diff --git a/pytests/src/pyclasses.rs b/pytests/src/pyclasses.rs index 1f3baec2..8e957c77 100644 --- a/pytests/src/pyclasses.rs +++ b/pytests/src/pyclasses.rs @@ -57,22 +57,27 @@ impl AssertingBaseClass { } } -#[pyclass(subclass)] -#[derive(Clone, Debug)] -struct AssertingBaseClassGilRef; +#[allow(deprecated)] +mod deprecated { + use super::*; -#[pymethods] -impl AssertingBaseClassGilRef { - #[new] - #[classmethod] - fn new(cls: &PyType, expected_type: &PyType) -> PyResult { - if !cls.is(expected_type) { - return Err(PyValueError::new_err(format!( - "{:?} != {:?}", - cls, expected_type - ))); + #[pyclass(subclass)] + #[derive(Clone, Debug)] + pub struct AssertingBaseClassGilRef; + + #[pymethods] + impl AssertingBaseClassGilRef { + #[new] + #[classmethod] + fn new(cls: &PyType, expected_type: &PyType) -> PyResult { + if !cls.is(expected_type) { + return Err(PyValueError::new_err(format!( + "{:?} != {:?}", + cls, expected_type + ))); + } + Ok(Self) } - Ok(Self) } } @@ -84,7 +89,7 @@ pub fn pyclasses(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - m.add_class::()?; + m.add_class::()?; m.add_class::()?; Ok(()) } diff --git a/src/coroutine.rs b/src/coroutine.rs index b24197ad..f2feab4a 100644 --- a/src/coroutine.rs +++ b/src/coroutine.rs @@ -15,7 +15,7 @@ use crate::{ exceptions::{PyAttributeError, PyRuntimeError, PyStopIteration}, panic::PanicException, types::{string::PyStringMethods, PyIterator, PyString}, - IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, + Bound, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, }; pub(crate) mod cancel; @@ -143,7 +143,7 @@ impl Coroutine { } } - fn send(&mut self, py: Python<'_>, _value: &PyAny) -> PyResult { + fn send(&mut self, py: Python<'_>, _value: &Bound<'_, PyAny>) -> PyResult { self.poll(py, None) } diff --git a/src/coroutine/waker.rs b/src/coroutine/waker.rs index b524b6d7..fc7c54e1 100644 --- a/src/coroutine/waker.rs +++ b/src/coroutine/waker.rs @@ -97,7 +97,7 @@ impl LoopAndFuture { /// Future can be cancelled by the event loop before being waken. /// See #[pyfunction(crate = "crate")] -fn release_waiter(future: &PyAny) -> PyResult<()> { +fn release_waiter(future: &Bound<'_, PyAny>) -> PyResult<()> { let done = future.call_method0(intern!(future.py(), "done"))?; if !done.extract::()? { future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?; diff --git a/src/impl_/deprecations.rs b/src/impl_/deprecations.rs index 0c749ff9..459eba91 100644 --- a/src/impl_/deprecations.rs +++ b/src/impl_/deprecations.rs @@ -5,8 +5,8 @@ use crate::{PyResult, Python}; #[deprecated(since = "0.20.0", note = "use `#[new]` instead of `#[__new__]`")] pub const PYMETHODS_NEW_DEPRECATED_FORM: () = (); -pub fn inspect_type(t: T) -> (T, GilRefs) { - (t, GilRefs::new()) +pub fn inspect_type(t: T, _: &GilRefs) -> T { + t } pub fn inspect_fn(f: fn(A) -> PyResult, _: &GilRefs) -> fn(A) -> PyResult { diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index 82285b3d..4dcef02c 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -41,14 +41,27 @@ impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py> for &'a Bound<'py, T> where T: PyTypeCheck, { - type Holder = Option<&'a Bound<'py, T>>; + type Holder = Option<()>; #[inline] - fn extract( - obj: &'a Bound<'py, PyAny>, - holder: &'a mut Option<&'a Bound<'py, T>>, - ) -> PyResult { - Ok(holder.insert(obj.downcast()?)) + fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut Option<()>) -> PyResult { + obj.downcast().map_err(Into::into) + } +} + +impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py> for Option<&'a Bound<'py, T>> +where + T: PyTypeCheck, +{ + type Holder = (); + + #[inline] + fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult { + if obj.is_none() { + Ok(None) + } else { + Ok(Some(obj.downcast()?)) + } } } diff --git a/src/macros.rs b/src/macros.rs index db409515..648bac18 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -144,8 +144,10 @@ macro_rules! wrap_pyfunction { }; ($function:path, $py_or_module:expr) => {{ use $function as wrapped_pyfunction; - let (py_or_module, e) = $crate::impl_::deprecations::inspect_type($py_or_module); - e.is_python(); + let check_gil_refs = $crate::impl_::deprecations::GilRefs::new(); + let py_or_module = + $crate::impl_::deprecations::inspect_type($py_or_module, &check_gil_refs); + check_gil_refs.is_python(); $crate::impl_::pyfunction::WrapPyFunctionArg::wrap_pyfunction( py_or_module, &wrapped_pyfunction::DEF, diff --git a/src/pycell.rs b/src/pycell.rs index 636fb90c..ccc55a75 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -149,12 +149,12 @@ //! //! It is better to write that function like this: //! ```rust +//! # #![allow(deprecated)] //! # use pyo3::prelude::*; //! # #[pyclass] //! # pub struct Number { //! # inner: u32, //! # } -//! # #[allow(deprecated)] //! #[pyfunction] //! fn swap_numbers(a: &PyCell, b: &PyCell) { //! // Check that the pointers are unequal diff --git a/src/tests/hygiene/pymethods.rs b/src/tests/hygiene/pymethods.rs index 2ffaf686..020f983b 100644 --- a/src/tests/hygiene/pymethods.rs +++ b/src/tests/hygiene/pymethods.rs @@ -28,7 +28,7 @@ impl Dummy { } fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String { - ::std::panic!("unimplemented isn't hygienic before 1.50") + ::std::unimplemented!() } fn __lt__(&self, other: &Self) -> bool { @@ -63,12 +63,12 @@ impl Dummy { // Customizing attribute access ////////////////////// - fn __getattr__(&self, name: ::std::string::String) -> &crate::PyAny { - ::std::panic!("unimplemented isn't hygienic before 1.50") + fn __getattr__(&self, name: ::std::string::String) -> &crate::Bound<'_, crate::PyAny> { + ::std::unimplemented!() } - fn __getattribute__(&self, name: ::std::string::String) -> &crate::PyAny { - ::std::panic!("unimplemented isn't hygienic before 1.50") + fn __getattribute__(&self, name: ::std::string::String) -> &crate::Bound<'_, crate::PyAny> { + ::std::unimplemented!() } fn __setattr__(&mut self, name: ::std::string::String, value: ::std::string::String) {} @@ -85,17 +85,27 @@ impl Dummy { fn __get__( &self, - instance: &crate::PyAny, - owner: &crate::PyAny, - ) -> crate::PyResult<&crate::PyAny> { - ::std::panic!("unimplemented isn't hygienic before 1.50") + instance: &crate::Bound<'_, crate::PyAny>, + owner: &crate::Bound<'_, crate::PyAny>, + ) -> crate::PyResult<&crate::Bound<'_, crate::PyAny>> { + ::std::unimplemented!() } - fn __set__(&self, instance: &crate::PyAny, owner: &crate::PyAny) {} + fn __set__( + &self, + instance: &crate::Bound<'_, crate::PyAny>, + owner: &crate::Bound<'_, crate::PyAny>, + ) { + } - fn __delete__(&self, instance: &crate::PyAny) {} + fn __delete__(&self, instance: &crate::Bound<'_, crate::PyAny>) {} - fn __set_name__(&self, owner: &crate::PyAny, name: &crate::PyAny) {} + fn __set_name__( + &self, + owner: &crate::Bound<'_, crate::PyAny>, + name: &crate::Bound<'_, crate::PyAny>, + ) { + } ////////////////////// // Implementing Descriptors @@ -323,9 +333,9 @@ impl Dummy { fn __exit__( &mut self, - exc_type: &crate::PyAny, - exc_value: &crate::PyAny, - traceback: &crate::PyAny, + exc_type: &crate::Bound<'_, crate::PyAny>, + exc_value: &crate::Bound<'_, crate::PyAny>, + traceback: &crate::Bound<'_, crate::PyAny>, ) { } @@ -361,9 +371,9 @@ impl Dummy { fn __aexit__( &mut self, - exc_type: &crate::PyAny, - exc_value: &crate::PyAny, - traceback: &crate::PyAny, + exc_type: &crate::Bound<'_, crate::PyAny>, + exc_value: &crate::Bound<'_, crate::PyAny>, + traceback: &crate::Bound<'_, crate::PyAny>, ) { } @@ -378,10 +388,10 @@ impl Dummy { #[pyo3(signature = (*_args, **_kwds))] fn __call__( &self, - _args: &crate::types::PyTuple, - _kwds: ::std::option::Option<&crate::types::PyDict>, + _args: &crate::Bound<'_, crate::types::PyTuple>, + _kwds: ::std::option::Option<&crate::Bound<'_, crate::types::PyDict>>, ) -> crate::PyResult { - ::std::panic!("unimplemented isn't hygienic before 1.50") + ::std::unimplemented!() } #[new] fn new(a: u8) -> Self { diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 55cda1a3..72275199 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -171,7 +171,7 @@ impl PyByteArray { /// use pyo3::types::PyByteArray; /// /// #[pyfunction] - /// fn a_valid_function(bytes: &PyByteArray) -> PyResult<()> { + /// fn a_valid_function(bytes: &Bound<'_, PyByteArray>) -> PyResult<()> { /// let section = { /// // SAFETY: We promise to not let the interpreter regain control /// // or invoke any PyO3 APIs while using the slice. @@ -224,7 +224,7 @@ impl PyByteArray { /// /// # #[allow(dead_code)] /// #[pyfunction] - /// fn bug(py: Python<'_>, bytes: &PyByteArray) { + /// fn bug(py: Python<'_>, bytes: &Bound<'_, PyByteArray>) { /// let slice = unsafe { bytes.as_bytes() }; /// /// // This explicitly yields control back to the Python interpreter... @@ -333,7 +333,7 @@ pub trait PyByteArrayMethods<'py>: crate::sealed::Sealed { /// use pyo3::types::PyByteArray; /// /// #[pyfunction] - /// fn a_valid_function(bytes: &PyByteArray) -> PyResult<()> { + /// fn a_valid_function(bytes: &Bound<'_, PyByteArray>) -> PyResult<()> { /// let section = { /// // SAFETY: We promise to not let the interpreter regain control /// // or invoke any PyO3 APIs while using the slice. @@ -386,7 +386,7 @@ pub trait PyByteArrayMethods<'py>: crate::sealed::Sealed { /// /// # #[allow(dead_code)] /// #[pyfunction] - /// fn bug(py: Python<'_>, bytes: &PyByteArray) { + /// fn bug(py: Python<'_>, bytes: &Bound<'_, PyByteArray>) { /// let slice = unsafe { bytes.as_bytes() }; /// /// // This explicitly yields control back to the Python interpreter... diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 88f9ca44..da8d72ea 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -166,43 +166,43 @@ impl BinaryArithmetic { "BA" } - fn __add__(&self, rhs: &PyAny) -> String { + fn __add__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA + {:?}", rhs) } - fn __sub__(&self, rhs: &PyAny) -> String { + fn __sub__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA - {:?}", rhs) } - fn __mul__(&self, rhs: &PyAny) -> String { + fn __mul__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA * {:?}", rhs) } - fn __truediv__(&self, rhs: &PyAny) -> String { + fn __truediv__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA / {:?}", rhs) } - fn __lshift__(&self, rhs: &PyAny) -> String { + fn __lshift__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA << {:?}", rhs) } - fn __rshift__(&self, rhs: &PyAny) -> String { + fn __rshift__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA >> {:?}", rhs) } - fn __and__(&self, rhs: &PyAny) -> String { + fn __and__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA & {:?}", rhs) } - fn __xor__(&self, rhs: &PyAny) -> String { + fn __xor__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA ^ {:?}", rhs) } - fn __or__(&self, rhs: &PyAny) -> String { + fn __or__(&self, rhs: &Bound<'_, PyAny>) -> String { format!("BA | {:?}", rhs) } - fn __pow__(&self, rhs: &PyAny, mod_: Option) -> String { + fn __pow__(&self, rhs: &Bound<'_, PyAny>, mod_: Option) -> String { format!("BA ** {:?} (mod: {:?})", rhs, mod_) } } @@ -257,39 +257,39 @@ struct RhsArithmetic {} #[pymethods] impl RhsArithmetic { - fn __radd__(&self, other: &PyAny) -> String { + fn __radd__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} + RA", other) } - fn __rsub__(&self, other: &PyAny) -> String { + fn __rsub__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} - RA", other) } - fn __rmul__(&self, other: &PyAny) -> String { + fn __rmul__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} * RA", other) } - fn __rlshift__(&self, other: &PyAny) -> String { + fn __rlshift__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} << RA", other) } - fn __rrshift__(&self, other: &PyAny) -> String { + fn __rrshift__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} >> RA", other) } - fn __rand__(&self, other: &PyAny) -> String { + fn __rand__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} & RA", other) } - fn __rxor__(&self, other: &PyAny) -> String { + fn __rxor__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} ^ RA", other) } - fn __ror__(&self, other: &PyAny) -> String { + fn __ror__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} | RA", other) } - fn __rpow__(&self, other: &PyAny, _mod: Option<&PyAny>) -> String { + fn __rpow__(&self, other: &Bound<'_, PyAny>, _mod: Option<&PyAny>) -> String { format!("{:?} ** RA", other) } } @@ -334,91 +334,91 @@ impl LhsAndRhs { // "BA" // } - fn __add__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __add__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} + {:?}", lhs, rhs) } - fn __sub__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __sub__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} - {:?}", lhs, rhs) } - fn __mul__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __mul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} * {:?}", lhs, rhs) } - fn __lshift__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __lshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} << {:?}", lhs, rhs) } - fn __rshift__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __rshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} >> {:?}", lhs, rhs) } - fn __and__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __and__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} & {:?}", lhs, rhs) } - fn __xor__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __xor__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} ^ {:?}", lhs, rhs) } - fn __or__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __or__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} | {:?}", lhs, rhs) } - fn __pow__(lhs: PyRef<'_, Self>, rhs: &PyAny, _mod: Option) -> String { + fn __pow__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { format!("{:?} ** {:?}", lhs, rhs) } - fn __matmul__(lhs: PyRef<'_, Self>, rhs: &PyAny) -> String { + fn __matmul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} @ {:?}", lhs, rhs) } - fn __radd__(&self, other: &PyAny) -> String { + fn __radd__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} + RA", other) } - fn __rsub__(&self, other: &PyAny) -> String { + fn __rsub__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} - RA", other) } - fn __rmul__(&self, other: &PyAny) -> String { + fn __rmul__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} * RA", other) } - fn __rlshift__(&self, other: &PyAny) -> String { + fn __rlshift__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} << RA", other) } - fn __rrshift__(&self, other: &PyAny) -> String { + fn __rrshift__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} >> RA", other) } - fn __rand__(&self, other: &PyAny) -> String { + fn __rand__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} & RA", other) } - fn __rxor__(&self, other: &PyAny) -> String { + fn __rxor__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} ^ RA", other) } - fn __ror__(&self, other: &PyAny) -> String { + fn __ror__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} | RA", other) } - fn __rpow__(&self, other: &PyAny, _mod: Option<&PyAny>) -> String { + fn __rpow__(&self, other: &Bound<'_, PyAny>, _mod: Option<&PyAny>) -> String { format!("{:?} ** RA", other) } - fn __rmatmul__(&self, other: &PyAny) -> String { + fn __rmatmul__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} @ RA", other) } - fn __rtruediv__(&self, other: &PyAny) -> String { + fn __rtruediv__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} / RA", other) } - fn __rfloordiv__(&self, other: &PyAny) -> String { + fn __rfloordiv__(&self, other: &Bound<'_, PyAny>) -> String { format!("{:?} // RA", other) } } @@ -461,7 +461,7 @@ impl RichComparisons { "RC" } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> String { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> String { match op { CompareOp::Lt => format!("{} < {:?}", self.__repr__(), other), CompareOp::Le => format!("{} <= {:?}", self.__repr__(), other), @@ -482,7 +482,7 @@ impl RichComparisons2 { "RC2" } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyObject { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyObject { match op { CompareOp::Eq => true.into_py(other.py()), CompareOp::Ne => false.into_py(other.py()), diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 1209713e..fc9a7699 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -41,7 +41,7 @@ impl BaseClass { fn base_method(&self, x: usize) -> usize { x * self.val1 } - fn base_set(&mut self, fn_: &pyo3::PyAny) -> PyResult<()> { + fn base_set(&mut self, fn_: &Bound<'_, PyAny>) -> PyResult<()> { let value: usize = fn_.call0()?.extract()?; self.val1 = value; Ok(()) @@ -264,7 +264,7 @@ mod inheriting_native_type { #[pymethods] impl CustomException { #[new] - fn new(_exc_arg: &PyAny) -> Self { + fn new(_exc_arg: &Bound<'_, PyAny>) -> Self { CustomException { context: "Hello :)", } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 7ca06b27..fa681337 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -112,12 +112,8 @@ struct ClassMethodWithArgs {} #[pymethods] impl ClassMethodWithArgs { #[classmethod] - fn method(cls: &Bound<'_, PyType>, input: &PyString) -> PyResult { - Ok(format!( - "{}.method({})", - cls.as_gil_ref().qualname()?, - input - )) + fn method(cls: &Bound<'_, PyType>, input: &Bound<'_, PyString>) -> PyResult { + Ok(format!("{}.method({})", cls.qualname()?, input)) } } @@ -215,8 +211,13 @@ impl MethSignature { test } #[pyo3(signature = (*args, **kwargs))] - fn get_kwargs(&self, py: Python<'_>, args: &PyTuple, kwargs: Option<&PyDict>) -> PyObject { - [args.into(), kwargs.to_object(py)].to_object(py) + fn get_kwargs( + &self, + py: Python<'_>, + args: &Bound<'_, PyTuple>, + kwargs: Option<&Bound<'_, PyDict>>, + ) -> PyObject { + [args.to_object(py), kwargs.to_object(py)].to_object(py) } #[pyo3(signature = (a, *args, **kwargs))] @@ -224,10 +225,10 @@ impl MethSignature { &self, py: Python<'_>, a: i32, - args: &PyTuple, - kwargs: Option<&PyDict>, + args: &Bound<'_, PyTuple>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyObject { - [a.to_object(py), args.into(), kwargs.to_object(py)].to_object(py) + [a.to_object(py), args.to_object(py), kwargs.to_object(py)].to_object(py) } #[pyo3(signature = (a, b, /))] @@ -270,7 +271,7 @@ impl MethSignature { &self, py: Python<'_>, a: i32, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyObject { [a.to_object(py), kwargs.to_object(py)].to_object(py) } @@ -280,7 +281,7 @@ impl MethSignature { &self, py: Python<'_>, a: i32, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyObject { [a.to_object(py), kwargs.to_object(py)].to_object(py) } @@ -301,7 +302,12 @@ impl MethSignature { } #[pyo3(signature = (*args, a))] - fn get_args_and_required_keyword(&self, py: Python<'_>, args: &PyTuple, a: i32) -> PyObject { + fn get_args_and_required_keyword( + &self, + py: Python<'_>, + args: &Bound<'_, PyTuple>, + a: i32, + ) -> PyObject { (args, a).to_object(py) } @@ -316,7 +322,7 @@ impl MethSignature { } #[pyo3(signature = (a, **kwargs))] - fn get_pos_kw(&self, py: Python<'_>, a: i32, kwargs: Option<&PyDict>) -> PyObject { + fn get_pos_kw(&self, py: Python<'_>, a: i32, kwargs: Option<&Bound<'_, PyDict>>) -> PyObject { [a.to_object(py), kwargs.to_object(py)].to_object(py) } @@ -697,7 +703,8 @@ struct MethodWithLifeTime {} #[pymethods] impl MethodWithLifeTime { - fn set_to_list<'py>(&self, py: Python<'py>, set: &'py PySet) -> PyResult> { + fn set_to_list<'py>(&self, set: &Bound<'py, PySet>) -> PyResult> { + let py = set.py(); let mut items = vec![]; for _ in 0..set.len() { items.push(set.pop().unwrap()); @@ -1028,45 +1035,45 @@ issue_1506!( fn issue_1506( &self, _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } fn issue_1506_mut( &mut self, _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } fn issue_1506_custom_receiver( _slf: Py, _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } fn issue_1506_custom_receiver_explicit( _slf: Py, _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } #[new] fn issue_1506_new( _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) -> Self { Issue1506 {} } @@ -1082,9 +1089,9 @@ issue_1506!( #[staticmethod] fn issue_1506_static( _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } @@ -1092,9 +1099,9 @@ issue_1506!( fn issue_1506_class( _cls: &Bound<'_, PyType>, _py: Python<'_>, - _arg: &PyAny, - _args: &PyTuple, - _kwargs: Option<&PyDict>, + _arg: &Bound<'_, PyAny>, + _args: &Bound<'_, PyTuple>, + _kwargs: Option<&Bound<'_, PyDict>>, ) { } } diff --git a/tests/test_no_imports.rs b/tests/test_no_imports.rs index 4c77cc8e..35c978b0 100644 --- a/tests/test_no_imports.rs +++ b/tests/test_no_imports.rs @@ -49,7 +49,7 @@ impl BasicClass { const OKAY: bool = true; #[new] - fn new(arg: &pyo3::PyAny) -> pyo3::PyResult { + fn new(arg: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult { if let Ok(v) = arg.extract::() { Ok(Self { v, diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index a1cfde2b..dd707990 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -28,7 +28,7 @@ impl ExampleClass { } } - fn __setattr__(&mut self, attr: &str, value: &PyAny) -> PyResult<()> { + fn __setattr__(&mut self, attr: &str, value: &Bound<'_, PyAny>) -> PyResult<()> { if attr == "special_custom_attr" { self.custom_attr = Some(value.extract()?); Ok(()) @@ -528,7 +528,7 @@ struct GetItem {} #[pymethods] impl GetItem { - fn __getitem__(&self, idx: &PyAny) -> PyResult<&'static str> { + fn __getitem__(&self, idx: &Bound<'_, PyAny>) -> PyResult<&'static str> { if let Ok(slice) = idx.downcast::() { let indices = slice.indices(1000)?; if indices.start == 100 && indices.stop == 200 && indices.step == 1 { @@ -767,18 +767,18 @@ impl DescrCounter { /// Each access will increase the count fn __get__<'a>( mut slf: PyRefMut<'a, Self>, - _instance: &PyAny, - _owner: Option<&PyType>, + _instance: &Bound<'_, PyAny>, + _owner: Option<&Bound<'_, PyType>>, ) -> PyRefMut<'a, Self> { slf.count += 1; slf } /// Allow assigning a new counter to the descriptor, copying the count across - fn __set__(&self, _instance: &PyAny, new_value: &mut Self) { + fn __set__(&self, _instance: &Bound<'_, PyAny>, new_value: &mut Self) { new_value.count = self.count; } /// Delete to reset the counter - fn __delete__(&mut self, _instance: &PyAny) { + fn __delete__(&mut self, _instance: &Bound<'_, PyAny>) { self.count = 0; } } diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index 5221b0f8..8a57e270 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -77,12 +77,14 @@ assert a, array.array("i", [2, 4, 6, 8]) #[cfg(not(any(Py_LIMITED_API, PyPy)))] #[pyfunction] -fn function_with_pyfunction_arg(fun: &PyFunction) -> PyResult<&PyAny> { +fn function_with_pyfunction_arg<'py>(fun: &Bound<'py, PyFunction>) -> PyResult> { fun.call((), None) } #[pyfunction] -fn function_with_pycfunction_arg(fun: &PyCFunction) -> PyResult<&PyAny> { +fn function_with_pycfunction_arg<'py>( + fun: &Bound<'py, PyCFunction>, +) -> PyResult> { fun.call((), None) } diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 363c27f5..4e128a0b 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -60,8 +60,8 @@ impl ByteSequence { } } - fn __contains__(&self, other: &PyAny) -> bool { - match u8::extract(other) { + fn __contains__(&self, other: &Bound<'_, PyAny>) -> bool { + match other.extract::() { Ok(x) => self.elements.contains(&x), Err(_) => false, } diff --git a/tests/test_text_signature.rs b/tests/test_text_signature.rs index 32a78346..a9f5a041 100644 --- a/tests/test_text_signature.rs +++ b/tests/test_text_signature.rs @@ -128,10 +128,10 @@ fn test_auto_test_signature_function() { fn my_function_4( a: i32, b: Option, - args: &PyTuple, + args: &Bound<'_, PyTuple>, c: i32, d: i32, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) { let _ = (a, b, args, c, d, kwargs); } @@ -218,10 +218,10 @@ fn test_auto_test_signature_method() { &self, a: i32, b: Option, - args: &PyTuple, + args: &Bound<'_, PyTuple>, c: i32, d: i32, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) { let _ = (a, b, args, c, d, kwargs); } diff --git a/tests/test_variable_arguments.rs b/tests/test_variable_arguments.rs index 8d3cd5d3..3724689d 100644 --- a/tests/test_variable_arguments.rs +++ b/tests/test_variable_arguments.rs @@ -13,13 +13,13 @@ struct MyClass {} impl MyClass { #[staticmethod] #[pyo3(signature = (*args))] - fn test_args(args: &PyTuple) -> &PyTuple { + fn test_args(args: Bound<'_, PyTuple>) -> Bound<'_, PyTuple> { args } #[staticmethod] #[pyo3(signature = (**kwargs))] - fn test_kwargs(kwargs: Option<&PyDict>) -> Option<&PyDict> { + fn test_kwargs(kwargs: Option>) -> Option> { kwargs } } diff --git a/tests/ui/deprecations.rs b/tests/ui/deprecations.rs index cbaba2fa..f623215f 100644 --- a/tests/ui/deprecations.rs +++ b/tests/ui/deprecations.rs @@ -23,6 +23,9 @@ impl MyClass { fn method_gil_ref(_slf: &PyCell) {} fn method_bound(_slf: &Bound<'_, Self>) {} + + #[staticmethod] + fn static_method_gil_ref(_any: &PyAny) {} } fn main() {} @@ -89,6 +92,9 @@ fn pyfunction_from_py_with( ) { } +#[pyfunction] +fn pyfunction_gil_ref(_any: &PyAny) {} + #[derive(Debug, FromPyObject)] pub struct Zap { #[pyo3(item)] diff --git a/tests/ui/deprecations.stderr b/tests/ui/deprecations.stderr index d5da8572..b133b214 100644 --- a/tests/ui/deprecations.stderr +++ b/tests/ui/deprecations.stderr @@ -29,57 +29,69 @@ error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg` | ^ error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg`: use `&Bound<'_, T>` instead for this function argument - --> tests/ui/deprecations.rs:38:43 + --> tests/ui/deprecations.rs:28:36 | -38 | fn pyfunction_with_module_gil_ref(module: &PyModule) -> PyResult<&str> { +28 | fn static_method_gil_ref(_any: &PyAny) {} + | ^ + +error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg`: use `&Bound<'_, T>` instead for this function argument + --> tests/ui/deprecations.rs:41:43 + | +41 | fn pyfunction_with_module_gil_ref(module: &PyModule) -> PyResult<&str> { | ^ error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg`: use `&Bound<'_, T>` instead for this function argument - --> tests/ui/deprecations.rs:48:19 + --> tests/ui/deprecations.rs:51:19 | -48 | fn module_gil_ref(m: &PyModule) -> PyResult<()> { +51 | fn module_gil_ref(m: &PyModule) -> PyResult<()> { | ^ error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg`: use `&Bound<'_, T>` instead for this function argument - --> tests/ui/deprecations.rs:54:57 + --> tests/ui/deprecations.rs:57:57 | -54 | fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, m: &PyModule) -> PyResult<()> { +57 | fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, m: &PyModule) -> PyResult<()> { | ^ error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor - --> tests/ui/deprecations.rs:87:27 + --> tests/ui/deprecations.rs:90:27 | -87 | #[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32, +90 | #[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32, | ^^^^^^^^^^^^^^^^^ -error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor - --> tests/ui/deprecations.rs:97:27 +error: use of deprecated method `pyo3::deprecations::GilRefs::::function_arg`: use `&Bound<'_, T>` instead for this function argument + --> tests/ui/deprecations.rs:96:29 | -97 | #[pyo3(from_py_with = "PyAny::len", item("my_object"))] - | ^^^^^^^^^^^^ +96 | fn pyfunction_gil_ref(_any: &PyAny) {} + | ^ error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor - --> tests/ui/deprecations.rs:107:27 + --> tests/ui/deprecations.rs:103:27 | -107 | #[pyo3(from_py_with = "PyAny::len")] usize, +103 | #[pyo3(from_py_with = "PyAny::len", item("my_object"))] | ^^^^^^^^^^^^ error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor - --> tests/ui/deprecations.rs:113:31 + --> tests/ui/deprecations.rs:113:27 | -113 | Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32), +113 | #[pyo3(from_py_with = "PyAny::len")] usize, + | ^^^^^^^^^^^^ + +error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor + --> tests/ui/deprecations.rs:119:31 + | +119 | Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32), | ^^^^^^^^^^^^^^^^^ error: use of deprecated method `pyo3::deprecations::GilRefs::::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor - --> tests/ui/deprecations.rs:120:27 + --> tests/ui/deprecations.rs:126:27 | -120 | #[pyo3(from_py_with = "extract_gil_ref")] +126 | #[pyo3(from_py_with = "extract_gil_ref")] | ^^^^^^^^^^^^^^^^^ error: use of deprecated method `pyo3::deprecations::GilRefs::>::is_python`: use `wrap_pyfunction_bound!` instead - --> tests/ui/deprecations.rs:133:13 + --> tests/ui/deprecations.rs:139:13 | -133 | let _ = wrap_pyfunction!(double, py); +139 | let _ = wrap_pyfunction!(double, py); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `wrap_pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/invalid_cancel_handle.stderr b/tests/ui/invalid_cancel_handle.stderr index 9e617bca..04f55ede 100644 --- a/tests/ui/invalid_cancel_handle.stderr +++ b/tests/ui/invalid_cancel_handle.stderr @@ -58,6 +58,7 @@ error[E0277]: the trait bound `CancelHandle: Clone` is not satisfied | ^^^^ the trait `Clone` is not implemented for `CancelHandle` | = help: the following other types implement trait `PyFunctionArgument<'a, 'py>`: + Option<&'a pyo3::Bound<'py, T>> &'a pyo3::Bound<'py, T> &'a pyo3::coroutine::Coroutine &'a mut pyo3::coroutine::Coroutine diff --git a/tests/ui/invalid_frozen_pyclass_borrow.stderr b/tests/ui/invalid_frozen_pyclass_borrow.stderr index 3acfbeb1..52a0623f 100644 --- a/tests/ui/invalid_frozen_pyclass_borrow.stderr +++ b/tests/ui/invalid_frozen_pyclass_borrow.stderr @@ -16,6 +16,19 @@ note: required by a bound in `extract_pyclass_ref_mut` | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:9:1 + | +9 | #[pymethods] + | ^^^^^^^^^^^^ expected `False`, found `True` + | +note: required by a bound in `PyRefMut` + --> src/pycell.rs + | + | pub struct PyRefMut<'p, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_frozen_pyclass_borrow.rs:15:31 | diff --git a/tests/ui/invalid_pyfunctions.rs b/tests/ui/invalid_pyfunctions.rs index fba7d8b5..eaa241c0 100644 --- a/tests/ui/invalid_pyfunctions.rs +++ b/tests/ui/invalid_pyfunctions.rs @@ -1,4 +1,5 @@ use pyo3::prelude::*; +use pyo3::types::PyString; #[pyfunction] fn generic_function(value: T) {} @@ -19,7 +20,10 @@ fn function_with_required_after_option(_opt: Option, _x: i32) {} fn pass_module_but_no_arguments<'py>() {} #[pyfunction(pass_module)] -fn first_argument_not_module<'py>(string: &str, module: &'py PyModule) -> PyResult<&'py str> { +fn first_argument_not_module<'a, 'py>( + string: &str, + module: &'a Bound<'_, PyModule>, +) -> PyResult> { module.name() } diff --git a/tests/ui/invalid_pyfunctions.stderr b/tests/ui/invalid_pyfunctions.stderr index 6576997a..8ae40243 100644 --- a/tests/ui/invalid_pyfunctions.stderr +++ b/tests/ui/invalid_pyfunctions.stderr @@ -1,45 +1,45 @@ error: Python functions cannot have generic type parameters - --> tests/ui/invalid_pyfunctions.rs:4:21 + --> tests/ui/invalid_pyfunctions.rs:5:21 | -4 | fn generic_function(value: T) {} +5 | fn generic_function(value: T) {} | ^ error: Python functions cannot have `impl Trait` arguments - --> tests/ui/invalid_pyfunctions.rs:7:36 + --> tests/ui/invalid_pyfunctions.rs:8:36 | -7 | fn impl_trait_function(impl_trait: impl AsRef) {} +8 | fn impl_trait_function(impl_trait: impl AsRef) {} | ^^^^ error: wildcard argument names are not supported - --> tests/ui/invalid_pyfunctions.rs:10:22 + --> tests/ui/invalid_pyfunctions.rs:11:22 | -10 | fn wildcard_argument(_: i32) {} +11 | fn wildcard_argument(_: i32) {} | ^ error: destructuring in arguments is not supported - --> tests/ui/invalid_pyfunctions.rs:13:26 + --> tests/ui/invalid_pyfunctions.rs:14:26 | -13 | fn destructured_argument((a, b): (i32, i32)) {} +14 | fn destructured_argument((a, b): (i32, i32)) {} | ^^^^^^ error: required arguments after an `Option<_>` argument are ambiguous = help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters - --> tests/ui/invalid_pyfunctions.rs:16:63 + --> tests/ui/invalid_pyfunctions.rs:17:63 | -16 | fn function_with_required_after_option(_opt: Option, _x: i32) {} +17 | fn function_with_required_after_option(_opt: Option, _x: i32) {} | ^^^ error: expected `&PyModule` or `Py` as first argument with `pass_module` - --> tests/ui/invalid_pyfunctions.rs:19:37 + --> tests/ui/invalid_pyfunctions.rs:20:37 | -19 | fn pass_module_but_no_arguments<'py>() {} +20 | fn pass_module_but_no_arguments<'py>() {} | ^^ error[E0277]: the trait bound `&str: From>` is not satisfied - --> tests/ui/invalid_pyfunctions.rs:22:43 + --> tests/ui/invalid_pyfunctions.rs:24:13 | -22 | fn first_argument_not_module<'py>(string: &str, module: &'py PyModule) -> PyResult<&'py str> { - | ^ the trait `From>` is not implemented for `&str` +24 | string: &str, + | ^ the trait `From>` is not implemented for `&str` | = help: the following other types implement trait `From`: > diff --git a/tests/ui/invalid_pymethod_enum.stderr b/tests/ui/invalid_pymethod_enum.stderr index bb327dcc..6cf6fe89 100644 --- a/tests/ui/invalid_pymethod_enum.stderr +++ b/tests/ui/invalid_pymethod_enum.stderr @@ -9,3 +9,16 @@ note: required by a bound in `extract_pyclass_ref_mut` | | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_pymethod_enum.rs:9:1 + | +9 | #[pymethods] + | ^^^^^^^^^^^^ expected `False`, found `True` + | +note: required by a bound in `PyRefMut` + --> src/pycell.rs + | + | pub struct PyRefMut<'p, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/static_ref.rs b/tests/ui/static_ref.rs index a426536d..e8015db2 100644 --- a/tests/ui/static_ref.rs +++ b/tests/ui/static_ref.rs @@ -2,7 +2,12 @@ use pyo3::prelude::*; use pyo3::types::PyList; #[pyfunction] -fn static_ref(list: &'static PyList) -> usize { +fn static_ref(list: &'static Bound<'_, PyList>) -> usize { + list.len() +} + +#[pyfunction] +fn static_py(list: &Bound<'static, PyList>) -> usize { list.len() } diff --git a/tests/ui/static_ref.stderr b/tests/ui/static_ref.stderr index 2dd3342e..50b054f6 100644 --- a/tests/ui/static_ref.stderr +++ b/tests/ui/static_ref.stderr @@ -8,3 +8,37 @@ error: lifetime may not live long enough | cast requires that `'py` must outlive `'static` | = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0597]: `holder_0` does not live long enough + --> tests/ui/static_ref.rs:5:15 + | +4 | #[pyfunction] + | ------------- + | | | + | | `holder_0` dropped here while still borrowed + | binding `holder_0` declared here + | argument requires that `holder_0` is borrowed for `'static` +5 | fn static_ref(list: &'static Bound<'_, PyList>) -> usize { + | ^^^^^^^ borrowed value does not live long enough + +error[E0716]: temporary value dropped while borrowed + --> tests/ui/static_ref.rs:5:21 + | +4 | #[pyfunction] + | ------------- + | | | + | | temporary value is freed at the end of this statement + | argument requires that borrow lasts for `'static` +5 | fn static_ref(list: &'static Bound<'_, PyList>) -> usize { + | ^ creates a temporary value which is freed while still in use + +error: lifetime may not live long enough + --> tests/ui/static_ref.rs:9:1 + | +9 | #[pyfunction] + | ^^^^^^^^^^^^^ + | | + | lifetime `'py` defined here + | cast requires that `'py` must outlive `'static` + | + = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)