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>
This commit is contained in:
David Hewitt 2024-03-20 22:35:08 +00:00 committed by GitHub
parent cedac43dbb
commit 870a4bb20d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 597 additions and 352 deletions

View File

@ -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<i32> {
fn __getitem__(&self, key: &Bound<'_, PyAny>) -> PyResult<i32> {
if let Ok(position) = key.extract::<i32>() {
return Ok(position);
} else if let Ok(slice) = key.downcast::<PySlice>() {
@ -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);

View File

@ -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
<details open>
<summary><small>Click to expand</small></summary>
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.
</details>
### Enable the `gil-refs` feature
<details open>
<summary><small>Click to expand</small></summary>
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<T>` smart pointers have been introduced, for example `PyTuple::new_bound` which returns `Bound<PyTuple>` 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"] }
```
</details>
### `PyTypeInfo` and `PyTryFrom` have been adjusted
<details open>
<summary><small>Click to expand</small></summary>
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| {
})
# }
```
</details>
### `Iter(A)NextOutput` are deprecated
<details open>
<summary><small>Click to expand</small></summary>
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<T>` and `Result<Option<T>, E>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration.
@ -200,20 +211,29 @@ impl PyClassAsyncIter {
}
}
```
</details>
### `PyType::name` has been renamed to `PyType::qualname`
<details open>
<summary><small>Click to expand</small></summary>
`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.
</details>
### `PyCell` has been deprecated
<details open>
<summary><small>Click to expand</small></summary>
Interactions with Python objects implemented in Rust no longer need to go though `PyCell<T>`. Instead iteractions with Python object now consistently go through `Bound<T>` or `Py<T>` 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.
</details>
### Migrating from the GIL-Refs API to `Bound<T>`
### Migrating from the GIL Refs API to `Bound<T>`
<details open>
<summary><small>Click to expand</small></summary>
To minimise breakage of code using the GIL-Refs API, the `Bound<T>` 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<T>` 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.
</details>
### Deactivating the `gil-refs` feature
<details open>
<summary><small>Click to expand</small></summary>
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::<PyBackedStr>()`, which is string data which borrows from the Python `str` object. Alternatively, use `.extract::<Cow<str>>()`, `.extract::<String>()` 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::<PyBackedStr>()`, which is string data which borrows from the Python `str` object. Alternatively, use `.extract::<Cow<str>>()` ro `.extract::<String>()` to copy the data into Rust for these versions.
</details>
## from 0.19.* to 0.20

View File

@ -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<TokenStream>,
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<TokenStream>,
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<TokenStream>,
self_e: &syn::Ident,
holders: &mut Vec<TokenStream>| {
let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders, ctx);
let rust_call = |args: Vec<TokenStream>, 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)
}
}

View File

@ -305,10 +305,11 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream>
.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(); },
]);
}
}

View File

@ -9,6 +9,53 @@ use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::Result;
pub struct Holders {
holders: Vec<syn::Ident>,
gil_refs_checkers: Vec<syn::Ident>,
}
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<TokenStream>,
holders: &mut Holders,
ctx: &Ctx,
) -> Result<(TokenStream, Vec<TokenStream>)> {
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::<Result<_>>()?;
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::<Result<_>>()?;
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<TokenStream>,
holders: &mut Holders,
ctx: &Ctx,
) -> Result<TokenStream> {
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)
}

View File

@ -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 => {

View File

@ -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<TokenStream>,
holders: &mut Holders,
ctx: &Ctx,
) -> syn::Result<TokenStream> {
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<TokenStream>,
holders: &mut Holders,
ctx: &Ctx,
) -> syn::Result<TokenStream> {
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<TokenStream>,
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<TokenStream>,
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<TokenStream>,
holders: &mut Holders,
return_mode: Option<&ReturnMode>,
ctx: &Ctx,
) -> Result<TokenStream> {
@ -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<TokenStream>,
holders: &mut Holders,
ctx: &Ctx,
) -> Result<Vec<TokenStream>> {
let mut args = Vec::with_capacity(spec.signature.arguments.len());

View File

@ -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) }
}

View File

@ -17,19 +17,19 @@ impl BytesExtractor {
}
#[staticmethod]
pub fn from_bytes(bytes: &PyBytes) -> PyResult<usize> {
pub fn from_bytes(bytes: &Bound<'_, PyBytes>) -> PyResult<usize> {
let byte_vec: Vec<u8> = bytes.extract()?;
Ok(byte_vec.len())
}
#[staticmethod]
pub fn from_str(string: &PyString) -> PyResult<usize> {
pub fn from_str(string: &Bound<'_, PyString>) -> PyResult<usize> {
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()
}

View File

@ -12,8 +12,11 @@ fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<Bound<'_
}
#[pyfunction]
fn get_date_tuple<'p>(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<Bound<'py, PyDelta>> {
PyDelta::new_bound(py, 0, 3600, 0, true)
fn utcoffset<'py>(&self, dt: &Bound<'py, PyDateTime>) -> PyResult<Bound<'py, PyDelta>> {
PyDelta::new_bound(dt.py(), 0, 3600, 0, true)
}
fn tzname(&self, _dt: &Bound<'_, PyDateTime>) -> String {

View File

@ -20,7 +20,7 @@ impl DictSize {
DictSize { expected }
}
fn iter_dict(&mut self, _py: Python<'_>, dict: &PyDict) -> PyResult<u32> {
fn iter_dict(&mut self, _py: Python<'_>, dict: &Bound<'_, PyDict>) -> PyResult<u32> {
let mut seen = 0u32;
for (sym, values) in dict {
seen += 1;

View File

@ -8,8 +8,8 @@ fn issue_219() {
}
#[pyfunction]
fn get_type_full_name(obj: &PyAny) -> PyResult<Cow<'_, str>> {
obj.get_type().name()
fn get_type_full_name(obj: &Bound<'_, PyAny>) -> PyResult<String> {
obj.get_type().name().map(Cow::into_owned)
}
#[pyfunction]

View File

@ -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));
}
}

View File

@ -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<Self> {
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<Self> {
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::<EmptyClass>()?;
m.add_class::<PyClassIter>()?;
m.add_class::<AssertingBaseClass>()?;
m.add_class::<AssertingBaseClassGilRef>()?;
m.add_class::<deprecated::AssertingBaseClassGilRef>()?;
m.add_class::<ClassWithoutConstructor>()?;
Ok(())
}

View File

@ -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<PyObject> {
fn send(&mut self, py: Python<'_>, _value: &Bound<'_, PyAny>) -> PyResult<PyObject> {
self.poll(py, None)
}

View File

@ -97,7 +97,7 @@ impl LoopAndFuture {
/// Future can be cancelled by the event loop before being waken.
/// See <https://github.com/python/cpython/blob/main/Lib/asyncio/tasks.py#L452C5-L452C5>
#[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::<bool>()? {
future.call_method1(intern!(future.py(), "set_result"), (future.py().None(),))?;

View File

@ -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) -> (T, GilRefs<T>) {
(t, GilRefs::new())
pub fn inspect_type<T>(t: T, _: &GilRefs<T>) -> T {
t
}
pub fn inspect_fn<A, T>(f: fn(A) -> PyResult<T>, _: &GilRefs<A>) -> fn(A) -> PyResult<T> {

View File

@ -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<Self> {
Ok(holder.insert(obj.downcast()?))
fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut Option<()>) -> PyResult<Self> {
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<Self> {
if obj.is_none() {
Ok(None)
} else {
Ok(Some(obj.downcast()?))
}
}
}

View File

@ -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,

View File

@ -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<Number>, b: &PyCell<Number>) {
//! // Check that the pointers are unequal

View File

@ -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<i32> {
::std::panic!("unimplemented isn't hygienic before 1.50")
::std::unimplemented!()
}
#[new]
fn new(a: u8) -> Self {

View File

@ -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...

View File

@ -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<u32>) -> String {
fn __pow__(&self, rhs: &Bound<'_, PyAny>, mod_: Option<u32>) -> 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<usize>) -> String {
fn __pow__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option<usize>) -> 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()),

View File

@ -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 :)",
}

View File

@ -112,12 +112,8 @@ struct ClassMethodWithArgs {}
#[pymethods]
impl ClassMethodWithArgs {
#[classmethod]
fn method(cls: &Bound<'_, PyType>, input: &PyString) -> PyResult<String> {
Ok(format!(
"{}.method({})",
cls.as_gil_ref().qualname()?,
input
))
fn method(cls: &Bound<'_, PyType>, input: &Bound<'_, PyString>) -> PyResult<String> {
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<Bound<'py, PyList>> {
fn set_to_list<'py>(&self, set: &Bound<'py, PySet>) -> PyResult<Bound<'py, PyList>> {
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<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_explicit(
_slf: Py<Issue1506>,
_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>>,
) {
}
}

View File

@ -49,7 +49,7 @@ impl BasicClass {
const OKAY: bool = true;
#[new]
fn new(arg: &pyo3::PyAny) -> pyo3::PyResult<Self> {
fn new(arg: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<Self> {
if let Ok(v) = arg.extract::<usize>() {
Ok(Self {
v,

View File

@ -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::<PySlice>() {
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;
}
}

View File

@ -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<Bound<'py, PyAny>> {
fun.call((), None)
}
#[pyfunction]
fn function_with_pycfunction_arg(fun: &PyCFunction) -> PyResult<&PyAny> {
fn function_with_pycfunction_arg<'py>(
fun: &Bound<'py, PyCFunction>,
) -> PyResult<Bound<'py, PyAny>> {
fun.call((), None)
}

View File

@ -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::<u8>() {
Ok(x) => self.elements.contains(&x),
Err(_) => false,
}

View File

@ -128,10 +128,10 @@ fn test_auto_test_signature_function() {
fn my_function_4(
a: i32,
b: Option<i32>,
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<i32>,
args: &PyTuple,
args: &Bound<'_, PyTuple>,
c: i32,
d: i32,
kwargs: Option<&PyDict>,
kwargs: Option<&Bound<'_, PyDict>>,
) {
let _ = (a, b, args, c, d, kwargs);
}

View File

@ -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<Bound<'_, PyDict>>) -> Option<Bound<'_, PyDict>> {
kwargs
}
}

View File

@ -23,6 +23,9 @@ impl MyClass {
fn method_gil_ref(_slf: &PyCell<Self>) {}
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)]

View File

@ -29,57 +29,69 @@ error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<T>::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::<pyo3::Python<'_>>::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)

View File

@ -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

View File

@ -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<Frozen = False>>(
| ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut`
error[E0271]: type mismatch resolving `<Foo as PyClass>::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<Frozen = False>> {
| ^^^^^^^^^^^^^^ 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 `<Foo as PyClass>::Frozen == False`
--> tests/ui/invalid_frozen_pyclass_borrow.rs:15:31
|

View File

@ -1,4 +1,5 @@
use pyo3::prelude::*;
use pyo3::types::PyString;
#[pyfunction]
fn generic_function<T>(value: T) {}
@ -19,7 +20,10 @@ fn function_with_required_after_option(_opt: Option<i32>, _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<Bound<'py, PyString>> {
module.name()
}

View File

@ -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<T>(value: T) {}
5 | fn generic_function<T>(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<PyAny>) {}
8 | fn impl_trait_function(impl_trait: impl AsRef<PyAny>) {}
| ^^^^
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<i32>, _x: i32) {}
17 | fn function_with_required_after_option(_opt: Option<i32>, _x: i32) {}
| ^^^
error: expected `&PyModule` or `Py<PyModule>` 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<BoundRef<'_, '_, pyo3::prelude::PyModule>>` 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<BoundRef<'_, '_, pyo3::prelude::PyModule>>` is not implemented for `&str`
24 | string: &str,
| ^ the trait `From<BoundRef<'_, '_, pyo3::prelude::PyModule>>` is not implemented for `&str`
|
= help: the following other types implement trait `From<T>`:
<String as From<char>>

View File

@ -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<Frozen = False>>(
| ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut`
error[E0271]: type mismatch resolving `<ComplexEnum as PyClass>::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<Frozen = False>> {
| ^^^^^^^^^^^^^^ 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)

View File

@ -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()
}

View File

@ -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)