Merge pull request #3232 from davidhewitt/remove-0-18-deprecations

remove all functionality deprecated in PyO3 0.18
This commit is contained in:
David Hewitt 2023-06-13 21:49:45 +00:00 committed by GitHub
commit 0b4187a672
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 100 additions and 1304 deletions

View File

@ -185,89 +185,6 @@ fn increment(x: u64, amount: Option<u64>) -> u64 {
To help avoid confusion, PyO3 requires `#[pyo3(signature = (...))]` when an `Option<T>` argument is surrounded by arguments which aren't `Option<T>`.
## Deprecated form
The `#[pyfunction]` macro can take the argument specification directly, but this method is deprecated in PyO3 0.18 because the `#[pyo3(signature)]` option offers a simpler syntax and better validation.
The `#[pymethods]` macro has an `#[args]` attribute which accepts the deprecated form.
Below are the same examples as above, but using the deprecated syntax:
```rust
# #![allow(deprecated)]
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyfunction(kwds = "**")]
fn num_kwds(kwds: Option<&PyDict>) -> usize {
kwds.map_or(0, |dict| dict.len())
}
#[pymodule]
fn module_with_functions(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
Ok(())
}
```
The following parameters can be passed to the `#[pyfunction]` attribute:
* `"/"`: positional-only arguments separator, each parameter defined before `"/"` is a
positional-only parameter.
Corresponds to python's `def meth(arg1, arg2, ..., /, argN..)`.
* `"*"`: var arguments separator, each parameter defined after `"*"` is a keyword-only parameter.
Corresponds to python's `def meth(*, arg1.., arg2=..)`.
* `args="*"`: "args" is var args, corresponds to Python's `def meth(*args)`. Type of the `args`
parameter has to be `&PyTuple`.
* `kwargs="**"`: "kwargs" receives keyword arguments, corresponds to Python's `def meth(**kwargs)`.
The type of the `kwargs` parameter has to be `Option<&PyDict>`.
* `arg="Value"`: arguments with default value. Corresponds to Python's `def meth(arg=Value)`.
If the `arg` argument is defined after var arguments, it is treated as a keyword-only argument.
Note that `Value` has to be valid rust code, PyO3 just inserts it into the generated
code unmodified.
Example:
```rust
# #![allow(deprecated)]
# use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
#
# #[pyclass]
# struct MyClass {
# num: i32,
# }
#[pymethods]
impl MyClass {
#[new]
#[args(num = "-1")]
fn new(num: i32) -> Self {
MyClass { num }
}
#[args(num = "10", py_args = "*", name = "\"Hello\"", py_kwargs = "**")]
fn method(
&mut self,
num: i32,
py_args: &PyTuple,
name: &str,
py_kwargs: Option<&PyDict>,
) -> String {
let num_before = self.num;
self.num = num;
format!(
"num={} (was previously={}), py_args={:?}, name={}, py_kwargs={:?} ",
num, num_before, py_args, name, py_kwargs,
)
}
fn make_change(&mut self, num: i32) -> PyResult<String> {
self.num = num;
Ok(format!("num={}", self.num))
}
}
```
## Making the function signature available to Python
The function signature is exposed to Python via the `__text_signature__` attribute. PyO3 automatically generates this for every `#[pyfunction]` and all `#[pymethods]` directly from the Rust function, taking into account any override done with the `#[pyo3(signature = (...))]` option.

View File

@ -9,6 +9,59 @@ For a detailed list of all changes, see the [CHANGELOG](changelog.md).
PyO3 0.20 has increased minimum Rust version to 1.56. This enables use of newer language features and simplifies maintenance of the project.
### Required arguments are no longer accepted after optional arguments
[Trailing `Option<T>` arguments](./function/signature.md#trailing-optional-arguments) have an automatic default of `None`. To avoid unwanted changes when modifying function signatures, in PyO3 0.18 it was deprecated to have a required argument after an `Option<T>` argument without using `#[pyo3(signature = (...))]` to specify the intended defaults. In PyO3 0.20, this becomes a hard error.
Before:
```rust,ignore
#[pyfunction]
fn x_or_y(x: Option<u64>, y: u64) -> u64 {
x.unwrap_or(y)
}
```
After:
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
#[pyfunction]
#[pyo3(signature = (x, y))] // both x and y have no defaults and are required
fn x_or_y(x: Option<u64>, y: u64) -> u64 {
x.unwrap_or(y)
}
```
### Remove deprecated function forms
In PyO3 0.18 the `#[args]` attribute for `#[pymethods]`, and directly specifying the function signature in `#[pyfunction]`, was deprecated. This functionality has been removed in PyO3 0.20.
Before:
```rust,ignore
#[pyfunction]
#[pyo3(a, b = "0", "/")]
fn add(a: u64, b: u64) -> u64 {
a + b
}
```
After:
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
#[pyfunction]
#[pyo3(signature = (a, b=0, /))]
fn add(a: u64, b: u64) -> u64 {
a + b
}
```
## from 0.18.* to 0.19
### Access to `Python` inside `__traverse__` implementations are now forbidden

View File

@ -0,0 +1 @@
Remove all functionality deprecated in PyO3 0.18, including `#[args]` attribute for `#[pymethods]`.

View File

@ -3,18 +3,12 @@ use quote::{quote_spanned, ToTokens};
pub enum Deprecation {
PyClassTextSignature,
PyFunctionArguments,
PyMethodArgsAttribute,
RequiredArgumentAfterOption,
}
impl Deprecation {
fn ident(&self, span: Span) -> syn::Ident {
let string = match self {
Deprecation::PyClassTextSignature => "PYCLASS_TEXT_SIGNATURE",
Deprecation::PyFunctionArguments => "PYFUNCTION_ARGUMENTS",
Deprecation::PyMethodArgsAttribute => "PYMETHODS_ARGS_ATTRIBUTE",
Deprecation::RequiredArgumentAfterOption => "REQUIRED_ARGUMENT_AFTER_OPTION",
};
syn::Ident::new(string, span)
}

View File

@ -1,7 +1,6 @@
use crate::attributes::{TextSignatureAttribute, TextSignatureAttributeValue};
use crate::deprecations::{Deprecation, Deprecations};
use crate::params::impl_arg_params;
use crate::pyfunction::{DeprecatedArgs, FunctionSignature, PyFunctionArgPyO3Attributes};
use crate::pyfunction::{FunctionSignature, PyFunctionArgPyO3Attributes};
use crate::pyfunction::{PyFunctionOptions, SignatureAttribute};
use crate::utils::{self, PythonDoc};
use proc_macro2::{Span, TokenStream};
@ -236,7 +235,6 @@ pub struct FnSpec<'a> {
pub python_name: syn::Ident,
pub signature: FunctionSignature<'a>,
pub output: syn::Type,
pub deprecations: Deprecations,
pub convention: CallingConvention,
pub text_signature: Option<TextSignatureAttribute>,
pub unsafety: Option<syn::Token![unsafe]>,
@ -281,16 +279,14 @@ impl<'a> FnSpec<'a> {
let PyFunctionOptions {
text_signature,
name,
mut deprecations,
signature,
..
} = options;
let MethodAttributes {
ty: fn_type_attr,
deprecated_args,
mut python_name,
} = parse_method_attributes(meth_attrs, name.map(|name| name.value.0), &mut deprecations)?;
} = parse_method_attributes(meth_attrs, name.map(|name| name.value.0))?;
let (fn_type, skip_first_arg, fixed_convention) =
Self::parse_fn_type(sig, fn_type_attr, &mut python_name)?;
@ -314,15 +310,9 @@ impl<'a> FnSpec<'a> {
};
let signature = if let Some(signature) = signature {
ensure_spanned!(
deprecated_args.is_none(),
signature.kw.span() => "cannot define both function signature and legacy arguments"
);
FunctionSignature::from_arguments_and_attribute(arguments, signature)?
} else if let Some(deprecated_args) = deprecated_args {
FunctionSignature::from_arguments_and_deprecated_args(arguments, deprecated_args)?
} else {
FunctionSignature::from_arguments(arguments, &mut deprecations)
FunctionSignature::from_arguments(arguments)?
};
let convention =
@ -335,7 +325,6 @@ impl<'a> FnSpec<'a> {
python_name,
signature,
output: ty,
deprecations,
text_signature,
unsafety: sig.unsafety,
})
@ -423,7 +412,6 @@ impl<'a> FnSpec<'a> {
ident: &proc_macro2::Ident,
cls: Option<&syn::Type>,
) -> Result<TokenStream> {
let deprecations = &self.deprecations;
let self_conversion = self.tp.self_conversion(cls, ExtractErrorMode::Raise);
let self_arg = self.tp.self_arg();
let py = syn::Ident::new("_py", Span::call_site());
@ -457,7 +445,6 @@ impl<'a> FnSpec<'a> {
_slf: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#deprecations
#self_conversion
#call
}
@ -475,7 +462,6 @@ impl<'a> FnSpec<'a> {
_kwnames: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#deprecations
#self_conversion
#arg_convert
#call
@ -493,7 +479,6 @@ impl<'a> FnSpec<'a> {
_kwargs: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017
#deprecations
#self_conversion
#arg_convert
#call
@ -518,7 +503,6 @@ impl<'a> FnSpec<'a> {
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
use _pyo3::callback::IntoPyCallbackOutput;
let function = #rust_name; // Shadow the function name to avoid #3017
#deprecations
#arg_convert
let result = #call;
let initializer: _pyo3::PyClassInitializer::<#cls> = result.convert(#py)?;
@ -636,17 +620,14 @@ impl<'a> FnSpec<'a> {
#[derive(Debug)]
struct MethodAttributes {
ty: Option<MethodTypeAttribute>,
deprecated_args: Option<DeprecatedArgs>,
python_name: Option<syn::Ident>,
}
fn parse_method_attributes(
attrs: &mut Vec<syn::Attribute>,
mut python_name: Option<syn::Ident>,
deprecations: &mut Deprecations,
) -> Result<MethodAttributes> {
let mut new_attrs = Vec::new();
let mut deprecated_args = None;
let mut ty: Option<MethodTypeAttribute> = None;
macro_rules! set_compound_ty {
@ -754,13 +735,6 @@ fn parse_method_attributes(
))
}
};
} else if path.is_ident("args") {
ensure_spanned!(
deprecated_args.is_none(),
nested.span() => "args may only be specified once"
);
deprecations.push(Deprecation::PyMethodArgsAttribute, path.span());
deprecated_args = Some(DeprecatedArgs::from_meta(&nested)?);
} else {
new_attrs.push(attr)
}
@ -771,11 +745,7 @@ fn parse_method_attributes(
*attrs = new_attrs;
Ok(MethodAttributes {
ty,
deprecated_args,
python_name,
})
Ok(MethodAttributes { ty, python_name })
}
const IMPL_TRAIT_ERR: &str = "Python functions cannot have `impl Trait` arguments";

View File

@ -3,19 +3,17 @@ use crate::{
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
FromPyWithAttribute, NameAttribute, TextSignatureAttribute,
},
deprecations::{Deprecation, Deprecations},
method::{self, CallingConvention, FnArg},
pymethod::check_generic,
utils::{ensure_not_async_fn, get_pyo3_crate},
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{ext::IdentExt, spanned::Spanned, NestedMeta, Result};
use syn::{ext::IdentExt, spanned::Spanned, Result};
use syn::{
parse::{Parse, ParseBuffer, ParseStream},
parse::{Parse, ParseStream},
token::Comma,
};
use syn::{punctuated::Punctuated, Path};
mod signature;
@ -67,181 +65,12 @@ impl PyFunctionArgPyO3Attributes {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Argument {
PosOnlyArgsSeparator,
VarArgsSeparator,
VarArgs(syn::Path),
KeywordArgs(syn::Path),
PosOnlyArg(syn::Path, Option<String>),
Arg(syn::Path, Option<String>),
Kwarg(syn::Path, Option<String>),
}
#[derive(Debug, Default)]
pub struct DeprecatedArgs {
pub arguments: Vec<Argument>,
has_kw: bool,
has_posonly_args: bool,
has_varargs: bool,
has_kwargs: bool,
}
// Deprecated parsing mode for the signature
impl syn::parse::Parse for DeprecatedArgs {
fn parse(input: &ParseBuffer<'_>) -> syn::Result<Self> {
let attr = Punctuated::<NestedMeta, syn::Token![,]>::parse_terminated(input)?;
Self::from_meta(&attr)
}
}
impl DeprecatedArgs {
pub fn from_meta<'a>(iter: impl IntoIterator<Item = &'a NestedMeta>) -> syn::Result<Self> {
let mut slf = DeprecatedArgs::default();
for item in iter {
slf.add_item(item)?
}
Ok(slf)
}
pub fn add_item(&mut self, item: &NestedMeta) -> syn::Result<()> {
match item {
NestedMeta::Meta(syn::Meta::Path(ident)) => self.add_work(item, ident)?,
NestedMeta::Meta(syn::Meta::NameValue(nv)) => {
self.add_name_value(item, nv)?;
}
NestedMeta::Lit(lit) => {
self.add_literal(item, lit)?;
}
NestedMeta::Meta(syn::Meta::List(list)) => bail_spanned!(
list.span() => "list is not supported as argument"
),
}
Ok(())
}
fn add_literal(&mut self, item: &NestedMeta, lit: &syn::Lit) -> syn::Result<()> {
match lit {
syn::Lit::Str(lits) if lits.value() == "*" => {
// "*"
self.vararg_is_ok(item)?;
self.has_varargs = true;
self.arguments.push(Argument::VarArgsSeparator);
Ok(())
}
syn::Lit::Str(lits) if lits.value() == "/" => {
// "/"
self.posonly_arg_is_ok(item)?;
self.has_posonly_args = true;
// any arguments _before_ this become positional-only
self.arguments.iter_mut().for_each(|a| {
if let Argument::Arg(path, name) = a {
*a = Argument::PosOnlyArg(path.clone(), name.clone());
} else {
unreachable!();
}
});
self.arguments.push(Argument::PosOnlyArgsSeparator);
Ok(())
}
_ => bail_spanned!(item.span() => "expected \"/\" or \"*\""),
}
}
fn add_work(&mut self, item: &NestedMeta, path: &Path) -> syn::Result<()> {
ensure_spanned!(
!(self.has_kw || self.has_kwargs),
item.span() => "positional argument or varargs(*) not allowed after keyword arguments"
);
if self.has_varargs {
self.arguments.push(Argument::Kwarg(path.clone(), None));
} else {
self.arguments.push(Argument::Arg(path.clone(), None));
}
Ok(())
}
fn posonly_arg_is_ok(&self, item: &NestedMeta) -> syn::Result<()> {
ensure_spanned!(
!(self.has_posonly_args || self.has_kwargs || self.has_varargs),
item.span() => "/ is not allowed after /, varargs(*), or kwargs(**)"
);
Ok(())
}
fn vararg_is_ok(&self, item: &NestedMeta) -> syn::Result<()> {
ensure_spanned!(
!(self.has_kwargs || self.has_varargs),
item.span() => "* is not allowed after varargs(*) or kwargs(**)"
);
Ok(())
}
fn kw_arg_is_ok(&self, item: &NestedMeta) -> syn::Result<()> {
ensure_spanned!(
!self.has_kwargs,
item.span() => "keyword argument or kwargs(**) is not allowed after kwargs(**)"
);
Ok(())
}
fn add_nv_common(
&mut self,
item: &NestedMeta,
name: &syn::Path,
value: String,
) -> syn::Result<()> {
self.kw_arg_is_ok(item)?;
if self.has_varargs {
// kw only
self.arguments
.push(Argument::Kwarg(name.clone(), Some(value)));
} else {
self.has_kw = true;
self.arguments
.push(Argument::Arg(name.clone(), Some(value)));
}
Ok(())
}
fn add_name_value(&mut self, item: &NestedMeta, nv: &syn::MetaNameValue) -> syn::Result<()> {
match &nv.lit {
syn::Lit::Str(litstr) => {
if litstr.value() == "*" {
// args="*"
self.vararg_is_ok(item)?;
self.has_varargs = true;
self.arguments.push(Argument::VarArgs(nv.path.clone()));
} else if litstr.value() == "**" {
// kwargs="**"
self.kw_arg_is_ok(item)?;
self.has_kwargs = true;
self.arguments.push(Argument::KeywordArgs(nv.path.clone()));
} else {
self.add_nv_common(item, &nv.path, litstr.value())?;
}
}
syn::Lit::Int(litint) => {
self.add_nv_common(item, &nv.path, format!("{}", litint))?;
}
syn::Lit::Bool(litb) => {
self.add_nv_common(item, &nv.path, format!("{}", litb.value))?;
}
_ => bail_spanned!(nv.lit.span() => "expected a string literal"),
};
Ok(())
}
}
#[derive(Default)]
pub struct PyFunctionOptions {
pub pass_module: Option<attributes::kw::pass_module>,
pub name: Option<NameAttribute>,
pub deprecated_args: Option<DeprecatedArgs>,
pub signature: Option<SignatureAttribute>,
pub text_signature: Option<TextSignatureAttribute>,
pub deprecations: Deprecations,
pub krate: Option<CrateAttribute>,
}
@ -264,12 +93,7 @@ impl Parse for PyFunctionOptions {
// TODO needs duplicate check?
options.krate = Some(input.parse()?);
} else {
// If not recognised attribute, this is "legacy" pyfunction syntax #[pyfunction(a, b)]
options
.deprecations
.push(Deprecation::PyFunctionArguments, input.span());
options.deprecated_args = Some(input.parse()?);
break;
return Err(lookahead.error());
}
}
@ -359,10 +183,8 @@ pub fn impl_wrap_pyfunction(
let PyFunctionOptions {
pass_module,
name,
deprecated_args,
signature,
text_signature,
mut deprecations,
krate,
} = options;
@ -392,15 +214,9 @@ pub fn impl_wrap_pyfunction(
};
let signature = if let Some(signature) = signature {
ensure_spanned!(
deprecated_args.is_none(),
signature.kw.span() => "cannot define both function signature and legacy arguments"
);
FunctionSignature::from_arguments_and_attribute(arguments, signature)?
} else if let Some(deprecated_args) = deprecated_args {
FunctionSignature::from_arguments_and_deprecated_args(arguments, deprecated_args)?
} else {
FunctionSignature::from_arguments(arguments, &mut deprecations)
FunctionSignature::from_arguments(arguments)?
};
let ty = method::get_return_info(&func.sig.output);
@ -412,7 +228,6 @@ pub fn impl_wrap_pyfunction(
python_name,
signature,
output: ty,
deprecations,
text_signature,
unsafety: func.sig.unsafety,
};

View File

@ -1,5 +1,3 @@
use std::cmp::max;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{
@ -12,13 +10,9 @@ use syn::{
use crate::{
attributes::{kw, KeywordAttribute},
deprecations::{Deprecation, Deprecations},
method::FnArg,
pyfunction::Argument,
};
use super::DeprecatedArgs;
pub struct Signature {
paren_token: syn::token::Paren,
pub items: Punctuated<SignatureItem, Token![,]>,
@ -430,121 +424,8 @@ impl<'a> FunctionSignature<'a> {
})
}
/// The difference to `from_arguments_and_signature` is that deprecated args allowed entries to be:
/// - missing
/// - out of order
pub fn from_arguments_and_deprecated_args(
mut arguments: Vec<FnArg<'a>>,
deprecated_args: DeprecatedArgs,
) -> syn::Result<Self> {
let mut varargs = None;
let mut kwargs = None;
let mut keyword_only_parameters = Vec::new();
fn first_n_argument_names(arguments: &[FnArg<'_>], count: usize) -> Vec<String> {
arguments
.iter()
.filter_map(|fn_arg| {
if fn_arg.py {
None
} else {
Some(fn_arg.name.unraw().to_string())
}
})
.take(count)
.collect()
}
// Record highest counts observed based off argument positions
let mut positional_only_arguments_count = None;
let mut positional_arguments_count = None;
let mut required_positional_parameters = 0;
let args_iter = arguments.iter_mut().filter(|arg| !arg.py); // Python<'_> arguments don't show on the Python side.
for (i, fn_arg) in args_iter.enumerate() {
if let Some(argument) = deprecated_args
.arguments
.iter()
.find(|argument| match argument {
Argument::PosOnlyArg(path, _)
| Argument::Arg(path, _)
| Argument::Kwarg(path, _)
| Argument::VarArgs(path)
| Argument::KeywordArgs(path) => path.get_ident() == Some(fn_arg.name),
_ => false,
})
{
match argument {
Argument::PosOnlyArg(_, default) | Argument::Arg(_, default) => {
if let Some(default) = default {
fn_arg.default = Some(syn::parse_str(default)?);
} else if fn_arg.optional.is_none() {
// Option<_> arguments always have an implicit None default with the old
// `#[args]`
required_positional_parameters = i + 1;
}
if matches!(argument, Argument::PosOnlyArg(_, _)) {
positional_only_arguments_count = Some(i + 1);
}
positional_arguments_count = Some(i + 1);
}
Argument::Kwarg(_, default) => {
fn_arg.default = default.as_deref().map(syn::parse_str).transpose()?;
keyword_only_parameters.push((fn_arg.name.to_string(), default.is_none()));
}
Argument::PosOnlyArgsSeparator => {}
Argument::VarArgsSeparator => {}
Argument::VarArgs(path) => {
fn_arg.is_varargs = true;
if let Some(ident) = path.get_ident() {
varargs = Some(ident.to_string());
} else {
bail_spanned!(path.span() => "expected ident for *args");
};
}
Argument::KeywordArgs(path) => {
fn_arg.is_kwargs = true;
if let Some(ident) = path.get_ident() {
kwargs = Some(ident.to_string());
} else {
bail_spanned!(path.span() => "expected ident for **kwargs");
};
}
}
} else {
// Assume this is a required positional parameter
required_positional_parameters = i + 1;
positional_arguments_count = Some(i + 1);
}
}
// fix up state based on observations above
let positional_only_parameters = positional_only_arguments_count.unwrap_or(0);
let positional_parameters = first_n_argument_names(
&arguments,
max(
positional_arguments_count.unwrap_or(0),
positional_only_arguments_count.unwrap_or(0),
),
);
Ok(FunctionSignature {
arguments,
python_signature: PythonSignature {
positional_parameters,
positional_only_parameters,
required_positional_parameters,
varargs,
keyword_only_parameters,
kwargs,
},
attribute: None,
})
}
/// Without `#[pyo3(signature)]` or `#[args]` - just take the Rust function arguments as positional.
pub fn from_arguments(mut arguments: Vec<FnArg<'a>>, deprecations: &mut Deprecations) -> Self {
pub fn from_arguments(arguments: Vec<FnArg<'a>>) -> syn::Result<Self> {
let mut python_signature = PythonSignature::default();
for arg in &arguments {
// Python<'_> arguments don't show in Python signature
@ -553,13 +434,12 @@ impl<'a> FunctionSignature<'a> {
}
if arg.optional.is_none() {
// This argument is required
if python_signature.required_positional_parameters
!= python_signature.positional_parameters.len()
{
// A previous argument was not required
deprecations.push(Deprecation::RequiredArgumentAfterOption, arg.name.span());
}
// This argument is required, all previous arguments must also have been required
ensure_spanned!(
python_signature.required_positional_parameters == python_signature.positional_parameters.len(),
arg.ty.span() => "required arguments after an `Option<_>` argument are ambiguous\n\
= help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters"
);
python_signature.required_positional_parameters =
python_signature.positional_parameters.len() + 1;
@ -570,20 +450,11 @@ impl<'a> FunctionSignature<'a> {
.push(arg.name.unraw().to_string());
}
// Fixup any `Option<_>` arguments that were made implicitly made required by the deprecated
// branch above
for arg in arguments
.iter_mut()
.take(python_signature.required_positional_parameters)
{
arg.optional = None;
}
Self {
Ok(Self {
arguments,
python_signature,
attribute: None,
}
})
}
fn default_value_for_parameter(&self, parameter: &str) -> String {

View File

@ -2,8 +2,8 @@ use std::borrow::Cow;
use crate::attributes::NameAttribute;
use crate::method::{CallingConvention, ExtractErrorMode};
use crate::utils;
use crate::utils::{ensure_not_async_fn, PythonDoc};
use crate::{deprecations::Deprecations, utils};
use crate::{
method::{FnArg, FnSpec, FnType, SelfType},
pyfunction::PyFunctionOptions,
@ -441,13 +441,11 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<Me
};
let wrapper_ident = format_ident!("__pymethod_{}__", name);
let deprecations = &spec.deprecations;
let python_name = spec.null_terminated_python_name();
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
let function = #cls::#name; // Shadow the method name to avoid #3017
#deprecations
_pyo3::impl_::pymethods::OkWrap::wrap(#fncall, py)
.map_err(::core::convert::Into::into)
}
@ -496,7 +494,6 @@ pub fn impl_py_setter_def(
property_type: PropertyType<'_>,
) -> Result<MethodAndMethodDef> {
let python_name = property_type.null_terminated_python_name()?;
let deprecations = property_type.deprecations();
let doc = property_type.doc();
let setter_impl = match property_type {
PropertyType::Descriptor {
@ -570,14 +567,13 @@ pub fn impl_py_setter_def(
let method_def = quote! {
#cfg_attrs
_pyo3::class::PyMethodDefType::Setter({
#deprecations
_pyo3::class::PyMethodDefType::Setter(
_pyo3::class::PySetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PySetter(#cls::#wrapper_ident),
#doc
)
})
)
};
Ok(MethodAndMethodDef {
@ -609,7 +605,6 @@ pub fn impl_py_getter_def(
property_type: PropertyType<'_>,
) -> Result<MethodAndMethodDef> {
let python_name = property_type.null_terminated_python_name()?;
let deprecations = property_type.deprecations();
let doc = property_type.doc();
let getter_impl = match property_type {
PropertyType::Descriptor {
@ -691,14 +686,13 @@ pub fn impl_py_getter_def(
let method_def = quote! {
#cfg_attrs
_pyo3::class::PyMethodDefType::Getter({
#deprecations
_pyo3::class::PyMethodDefType::Getter(
_pyo3::class::PyGetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PyGetter(#cls::#wrapper_ident),
#doc
)
})
)
};
Ok(MethodAndMethodDef {
@ -747,13 +741,6 @@ impl PropertyType<'_> {
}
}
fn deprecations(&self) -> Option<&Deprecations> {
match self {
PropertyType::Descriptor { .. } => None,
PropertyType::Function { spec, .. } => Some(&spec.deprecations),
}
}
fn doc(&self) -> Cow<'_, PythonDoc> {
match self {
PropertyType::Descriptor { field, .. } => {

View File

@ -1,68 +0,0 @@
#![allow(deprecated)]
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
#[pyfunction]
fn none() {}
#[pyfunction(b = "\"bar\"", "*", c = "None")]
fn simple<'a>(a: i32, b: &'a str, c: Option<&'a PyDict>) -> (i32, &'a str, Option<&'a PyDict>) {
(a, b, c)
}
#[pyfunction(b = "\"bar\"", args = "*", c = "None")]
fn simple_args<'a>(
a: i32,
b: &'a str,
c: Option<&'a PyDict>,
args: &'a PyTuple,
) -> (i32, &'a str, &'a PyTuple, Option<&'a PyDict>) {
(a, b, args, c)
}
#[pyfunction(b = "\"bar\"", c = "None", kwargs = "**")]
fn simple_kwargs<'a>(
a: i32,
b: &'a str,
c: Option<&'a PyDict>,
kwargs: Option<&'a PyDict>,
) -> (i32, &'a str, Option<&'a PyDict>, Option<&'a PyDict>) {
(a, b, c, kwargs)
}
#[pyfunction(a, b = "\"bar\"", args = "*", c = "None", kwargs = "**")]
fn simple_args_kwargs<'a>(
a: i32,
b: &'a str,
args: &'a PyTuple,
c: Option<&'a PyDict>,
kwargs: Option<&'a PyDict>,
) -> (
i32,
&'a str,
&'a PyTuple,
Option<&'a PyDict>,
Option<&'a PyDict>,
) {
(a, b, args, c, kwargs)
}
#[pyfunction(args = "*", kwargs = "**")]
fn args_kwargs<'a>(
args: &'a PyTuple,
kwargs: Option<&'a PyDict>,
) -> (&'a PyTuple, Option<&'a PyDict>) {
(args, kwargs)
}
#[pymodule]
pub fn deprecated_pyfunctions(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(none, m)?)?;
m.add_function(wrap_pyfunction!(simple, m)?)?;
m.add_function(wrap_pyfunction!(simple_args, m)?)?;
m.add_function(wrap_pyfunction!(simple_kwargs, m)?)?;
m.add_function(wrap_pyfunction!(simple_args_kwargs, m)?)?;
m.add_function(wrap_pyfunction!(args_kwargs, m)?)?;
Ok(())
}

View File

@ -5,7 +5,6 @@ use pyo3::wrap_pymodule;
pub mod buf_and_str;
pub mod comparisons;
pub mod datetime;
pub mod deprecated_pyfunctions;
pub mod dict_iter;
pub mod misc;
pub mod objstore;
@ -23,9 +22,6 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(comparisons::comparisons))?;
#[cfg(not(Py_LIMITED_API))]
m.add_wrapped(wrap_pymodule!(datetime::datetime))?;
m.add_wrapped(wrap_pymodule!(
deprecated_pyfunctions::deprecated_pyfunctions
))?;
m.add_wrapped(wrap_pymodule!(dict_iter::dict_iter))?;
m.add_wrapped(wrap_pymodule!(misc::misc))?;
m.add_wrapped(wrap_pymodule!(objstore::objstore))?;
@ -44,10 +40,6 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
sys_modules.set_item("pyo3_pytests.buf_and_str", m.getattr("buf_and_str")?)?;
sys_modules.set_item("pyo3_pytests.comparisons", m.getattr("comparisons")?)?;
sys_modules.set_item("pyo3_pytests.datetime", m.getattr("datetime")?)?;
sys_modules.set_item(
"pyo3_pytests.deprecated_pyfunctions",
m.getattr("deprecated_pyfunctions")?,
)?;
sys_modules.set_item("pyo3_pytests.dict_iter", m.getattr("dict_iter")?)?;
sys_modules.set_item("pyo3_pytests.misc", m.getattr("misc")?)?;
sys_modules.set_item("pyo3_pytests.objstore", m.getattr("objstore")?)?;

View File

@ -1,61 +0,0 @@
from pyo3_pytests import deprecated_pyfunctions as pyfunctions
def none_py():
return None
def test_none_rs():
rust = pyfunctions.none()
py = none_py()
assert rust == py
def simple_py(a, b="bar", *, c=None):
return a, b, c
def test_simple_rs():
rust = pyfunctions.simple(1, "foo", c={1: 2})
py = simple_py(1, "foo", c={1: 2})
assert rust == py
def simple_args_py(a, b="bar", *args, c=None):
return a, b, args, c
def test_simple_args_rs():
rust = pyfunctions.simple_args(1, "foo", 4, 5, 6, c={1: 2})
py = simple_args_py(1, "foo", 4, 5, 6, c={1: 2})
assert rust == py
def simple_kwargs_py(a, b="bar", c=None, **kwargs):
return a, b, c, kwargs
def test_simple_kwargs_rs():
rust = pyfunctions.simple_kwargs(1, "foo", c={1: 2}, bar=4, foo=10)
py = simple_kwargs_py(1, "foo", c={1: 2}, bar=4, foo=10)
assert rust == py
def simple_args_kwargs_py(a, b="bar", *args, c=None, **kwargs):
return (a, b, args, c, kwargs)
def test_simple_args_kwargs_rs():
rust = pyfunctions.simple_args_kwargs(1, "foo", "baz", bar=4, foo=10)
py = simple_args_kwargs_py(1, "foo", "baz", bar=4, foo=10)
assert rust == py
def args_kwargs_py(*args, **kwargs):
return (args, kwargs)
def test_args_kwargs_rs():
rust = pyfunctions.args_kwargs(1, "foo", {1: 2}, bar=4, foo=10)
py = args_kwargs_py(1, "foo", {1: 2}, bar=4, foo=10)
assert rust == py

View File

@ -1,23 +1,5 @@
//! Symbols used to denote deprecated usages of PyO3's proc macros.
#[deprecated(
since = "0.18.0",
note = "passing arbitrary arguments to `#[pyfunction()]` to specify the signature is being replaced by `#[pyo3(signature)]`"
)]
pub const PYFUNCTION_ARGUMENTS: () = ();
#[deprecated(
since = "0.18.0",
note = "the `#[args]` attribute for `#[methods]` is being replaced by `#[pyo3(signature)]`"
)]
pub const PYMETHODS_ARGS_ATTRIBUTE: () = ();
#[deprecated(
since = "0.18.0",
note = "required arguments after an `Option<_>` argument are ambiguous and being phased out\n= help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters"
)]
pub const REQUIRED_ARGUMENT_AFTER_OPTION: () = ();
#[deprecated(
since = "0.19.0",
note = "put `text_signature` on `#[new]` instead of `#[pyclass]`"

View File

@ -1106,15 +1106,6 @@ impl PyObject {
{
<T as PyTryFrom<'_>>::try_from_unchecked(self.as_ref(py))
}
/// Casts the PyObject to a concrete Python object type.
#[deprecated(since = "0.18.0", note = "use downcast() instead")]
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError<'_>>
where
D: PyTryFrom<'p>,
{
self.downcast(py)
}
}
#[cfg(test)]

View File

@ -799,15 +799,6 @@ impl PyAny {
unsafe { ffi::Py_TYPE(self.as_ptr()) }
}
/// Converts this `PyAny` to a concrete Python type.
#[deprecated(since = "0.18.0", note = "use the equivalent .downcast()")]
pub fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError<'_>>
where
D: PyTryFrom<'a>,
{
self.downcast()
}
/// Downcast this `PyAny` to a concrete Python type or pyclass.
///
/// Note that you can often avoid downcasting yourself by just specifying

View File

@ -5,7 +5,6 @@
fn test_compile_errors() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/invalid_macro_args.rs");
t.compile_fail("tests/ui/invalid_need_module_arg_position.rs");
t.compile_fail("tests/ui/invalid_property_args.rs");
t.compile_fail("tests/ui/invalid_proto_pymethods.rs");

View File

@ -162,464 +162,6 @@ fn static_method_with_args() {
});
}
#[allow(deprecated)]
mod deprecated {
use super::*;
#[pyclass]
struct MethArgs {}
#[pymethods]
impl MethArgs {
#[args(test)]
fn get_optional(&self, test: Option<i32>) -> i32 {
test.unwrap_or(10)
}
fn get_optional2(&self, test: Option<i32>) -> Option<i32> {
test
}
#[args(test = "None")]
fn get_optional3(&self, test: Option<i32>) -> Option<i32> {
test
}
fn get_optional_positional(
&self,
_t1: Option<i32>,
t2: Option<i32>,
_t3: Option<i32>,
) -> Option<i32> {
t2
}
#[args(test = "10")]
fn get_default(&self, test: i32) -> i32 {
test
}
#[args("*", test = 10)]
fn get_kwarg(&self, test: i32) -> i32 {
test
}
#[args(args = "*", kwargs = "**")]
fn get_kwargs(&self, py: Python<'_>, args: &PyTuple, kwargs: Option<&PyDict>) -> PyObject {
[args.into(), kwargs.to_object(py)].to_object(py)
}
#[args(args = "*", kwargs = "**")]
fn get_pos_arg_kw(
&self,
py: Python<'_>,
a: i32,
args: &PyTuple,
kwargs: Option<&PyDict>,
) -> PyObject {
[a.to_object(py), args.into(), kwargs.to_object(py)].to_object(py)
}
#[args(a, b, "/")]
fn get_pos_only(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args(a, "/", b)]
fn get_pos_only_and_pos(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args(a, "/", b, c = 5)]
fn get_pos_only_and_pos_and_kw(&self, a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
#[args(a, "/", "*", b)]
fn get_pos_only_and_kw_only(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args(a, "/", "*", b = 3)]
fn get_pos_only_and_kw_only_with_default(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args(a, "/", b, "*", c, d = 5)]
fn get_all_arg_types_together(&self, a: i32, b: i32, c: i32, d: i32) -> i32 {
a + b + c + d
}
#[args(a, "/", args = "*")]
fn get_pos_only_with_varargs(&self, a: i32, args: Vec<i32>) -> i32 {
a + args.iter().sum::<i32>()
}
#[args(a, "/", kwargs = "**")]
fn get_pos_only_with_kwargs(
&self,
py: Python<'_>,
a: i32,
kwargs: Option<&PyDict>,
) -> PyObject {
[a.to_object(py), kwargs.to_object(py)].to_object(py)
}
#[args("*", a = 2, b = 3)]
fn get_kwargs_only_with_defaults(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args("*", a, b)]
fn get_kwargs_only(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args("*", a = 1, b)]
fn get_kwargs_only_with_some_default(&self, a: i32, b: i32) -> i32 {
a + b
}
#[args(args = "*", a)]
fn get_args_and_required_keyword(
&self,
py: Python<'_>,
args: &PyTuple,
a: i32,
) -> PyObject {
(args, a).to_object(py)
}
#[args(a, b = 2, "*", c = 3)]
fn get_pos_arg_kw_sep1(&self, a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
#[args(a, "*", b = 2, c = 3)]
fn get_pos_arg_kw_sep2(&self, a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
#[args(kwargs = "**")]
fn get_pos_kw(&self, py: Python<'_>, a: i32, kwargs: Option<&PyDict>) -> PyObject {
[a.to_object(py), kwargs.to_object(py)].to_object(py)
}
// "args" can be anything that can be extracted from PyTuple
#[args(args = "*")]
fn args_as_vec(&self, args: Vec<i32>) -> i32 {
args.iter().sum()
}
}
#[test]
fn meth_args() {
Python::with_gil(|py| {
let inst = Py::new(py, MethArgs {}).unwrap();
py_run!(py, inst, "assert inst.get_optional() == 10");
py_run!(py, inst, "assert inst.get_optional(100) == 100");
py_run!(py, inst, "assert inst.get_optional2() == None");
py_run!(py, inst, "assert inst.get_optional2(100) == 100");
py_run!(py, inst, "assert inst.get_optional3() == None");
py_run!(py, inst, "assert inst.get_optional3(100) == 100");
py_run!(
py,
inst,
"assert inst.get_optional_positional(1, 2, 3) == 2"
);
py_run!(py, inst, "assert inst.get_optional_positional(1) == None");
py_run!(py, inst, "assert inst.get_default() == 10");
py_run!(py, inst, "assert inst.get_default(100) == 100");
py_run!(py, inst, "assert inst.get_kwarg() == 10");
py_expect_exception!(py, inst, "inst.get_kwarg(100)", PyTypeError);
py_run!(py, inst, "assert inst.get_kwarg(test=100) == 100");
py_run!(py, inst, "assert inst.get_kwargs() == [(), None]");
py_run!(py, inst, "assert inst.get_kwargs(1,2,3) == [(1,2,3), None]");
py_run!(
py,
inst,
"assert inst.get_kwargs(t=1,n=2) == [(), {'t': 1, 'n': 2}]"
);
py_run!(
py,
inst,
"assert inst.get_kwargs(1,2,3,t=1,n=2) == [(1,2,3), {'t': 1, 'n': 2}]"
);
py_run!(py, inst, "assert inst.get_pos_arg_kw(1) == [1, (), None]");
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw(1, 2, 3) == [1, (2, 3), None]"
);
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw(1, b=2) == [1, (), {'b': 2}]"
);
py_run!(py, inst, "assert inst.get_pos_arg_kw(a=1) == [1, (), None]");
py_expect_exception!(py, inst, "inst.get_pos_arg_kw()", PyTypeError);
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(1, a=1)", PyTypeError);
py_expect_exception!(py, inst, "inst.get_pos_arg_kw(b=2)", PyTypeError);
py_run!(py, inst, "assert inst.get_pos_only(10, 11) == 21");
py_expect_exception!(py, inst, "inst.get_pos_only(10, b = 11)", PyTypeError);
py_expect_exception!(py, inst, "inst.get_pos_only(a = 10, b = 11)", PyTypeError);
py_run!(py, inst, "assert inst.get_pos_only_and_pos(10, 11) == 21");
py_run!(
py,
inst,
"assert inst.get_pos_only_and_pos(10, b = 11) == 21"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_pos(a = 10, b = 11)",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_pos_and_kw(10, 11) == 26"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_pos_and_kw(10, b = 11) == 26"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_pos_and_kw(10, 11, c = 0) == 21"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_pos_and_kw(10, b = 11, c = 0) == 21"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_pos_and_kw(a = 10, b = 11)",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_kw_only(10, b = 11) == 21"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_kw_only(10, 11)",
PyTypeError
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_kw_only(a = 10, b = 11)",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_kw_only_with_default(10) == 13"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_and_kw_only_with_default(10, b = 11) == 21"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_kw_only_with_default(10, 11)",
PyTypeError
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_and_kw_only_with_default(a = 10, b = 11)",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_all_arg_types_together(10, 10, c = 10) == 35"
);
py_run!(
py,
inst,
"assert inst.get_all_arg_types_together(10, 10, c = 10, d = 10) == 40"
);
py_run!(
py,
inst,
"assert inst.get_all_arg_types_together(10, b = 10, c = 10, d = 10) == 40"
);
py_expect_exception!(
py,
inst,
"inst.get_all_arg_types_together(10, 10, 10)",
PyTypeError
);
py_expect_exception!(
py,
inst,
"inst.get_all_arg_types_together(a = 10, b = 10, c = 10)",
PyTypeError
);
py_run!(py, inst, "assert inst.get_pos_only_with_varargs(10) == 10");
py_run!(
py,
inst,
"assert inst.get_pos_only_with_varargs(10, 10) == 20"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_with_varargs(10, 10, 10, 10, 10) == 50"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_with_varargs(a = 10)",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_pos_only_with_kwargs(10) == [10, None]"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_with_kwargs(10, b = 10) == [10, {'b': 10}]"
);
py_run!(
py,
inst,
"assert inst.get_pos_only_with_kwargs(10, b = 10, c = 10, d = 10, e = 10) == [10, {'b': 10, 'c': 10, 'd': 10, 'e': 10}]"
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_with_kwargs(a = 10)",
PyTypeError
);
py_expect_exception!(
py,
inst,
"inst.get_pos_only_with_kwargs(a = 10, b = 10)",
PyTypeError
);
py_run!(py, inst, "assert inst.get_kwargs_only_with_defaults() == 5");
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_defaults(a = 8) == 11"
);
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_defaults(b = 8) == 10"
);
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_defaults(a = 1, b = 1) == 2"
);
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_defaults(b = 1, a = 1) == 2"
);
py_run!(py, inst, "assert inst.get_kwargs_only(a = 1, b = 1) == 2");
py_run!(py, inst, "assert inst.get_kwargs_only(b = 1, a = 1) == 2");
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_some_default(a = 2, b = 1) == 3"
);
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_some_default(b = 1) == 2"
);
py_run!(
py,
inst,
"assert inst.get_kwargs_only_with_some_default(b = 1, a = 2) == 3"
);
py_expect_exception!(
py,
inst,
"inst.get_kwargs_only_with_some_default()",
PyTypeError
);
py_run!(
py,
inst,
"assert inst.get_args_and_required_keyword(1, 2, a=3) == ((1, 2), 3)"
);
py_run!(
py,
inst,
"assert inst.get_args_and_required_keyword(a=1) == ((), 1)"
);
py_expect_exception!(
py,
inst,
"inst.get_args_and_required_keyword()",
PyTypeError
);
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1) == 6");
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep1(1, 2) == 6");
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw_sep1(1, 2, c=13) == 16"
);
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw_sep1(a=1, b=2, c=13) == 16"
);
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw_sep1(b=2, c=13, a=1) == 16"
);
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw_sep1(c=13, b=2, a=1) == 16"
);
py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep1(1, 2, 3)", PyTypeError);
py_run!(py, inst, "assert inst.get_pos_arg_kw_sep2(1) == 6");
py_run!(
py,
inst,
"assert inst.get_pos_arg_kw_sep2(1, b=12, c=13) == 26"
);
py_expect_exception!(py, inst, "inst.get_pos_arg_kw_sep2(1, 2)", PyTypeError);
py_run!(py, inst, "assert inst.get_pos_kw(1, b=2) == [1, {'b': 2}]");
py_expect_exception!(py, inst, "inst.get_pos_kw(1,2)", PyTypeError);
py_run!(py, inst, "assert inst.args_as_vec(1,2,3) == 6");
});
}
}
#[pyclass]
struct MethSignature {}

View File

@ -478,43 +478,6 @@ fn use_pyfunction() {
})
}
#[test]
#[allow(deprecated)]
fn required_argument_after_option() {
#[pyfunction]
pub fn foo(x: Option<i32>, y: i32) -> i32 {
y + x.unwrap_or_default()
}
Python::with_gil(|py| {
let f = wrap_pyfunction!(foo, py).unwrap();
// it is an error to call this function with no arguments
py_expect_exception!(
py,
f,
"f()",
PyTypeError,
"foo() missing 2 required positional arguments: 'x' and 'y'"
);
// it is an error to call this function with one argument
py_expect_exception!(
py,
f,
"f(None)",
PyTypeError,
"foo() missing 1 required positional argument: 'y'"
);
// ok to call with two arguments
py_assert!(py, f, "f(None, 5) == 5");
// ok to call with keyword arguments
py_assert!(py, f, "f(x=None, y=5) == 5");
})
}
#[pyclass]
struct Key(String);

View File

@ -1,49 +0,0 @@
#![cfg(feature = "macros")]
#![allow(deprecated)]
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
mod common;
#[pyclass]
struct MyClass {}
#[pymethods]
impl MyClass {
#[staticmethod]
#[args(args = "*")]
fn test_args(args: &PyTuple) -> &PyTuple {
args
}
#[staticmethod]
#[args(kwargs = "**")]
fn test_kwargs(kwargs: Option<&PyDict>) -> Option<&PyDict> {
kwargs
}
}
#[test]
fn variable_args() {
Python::with_gil(|py| {
let my_obj = py.get_type::<MyClass>();
py_assert!(py, my_obj, "my_obj.test_args() == ()");
py_assert!(py, my_obj, "my_obj.test_args(1) == (1,)");
py_assert!(py, my_obj, "my_obj.test_args(1, 2) == (1, 2)");
});
}
#[test]
fn variable_kwargs() {
Python::with_gil(|py| {
let my_obj = py.get_type::<MyClass>();
py_assert!(py, my_obj, "my_obj.test_kwargs() == None");
py_assert!(py, my_obj, "my_obj.test_kwargs(test=1) == {'test': 1}");
py_assert!(
py,
my_obj,
"my_obj.test_kwargs(test1=1, test2=2) == {'test1':1, 'test2':2}"
);
});
}

View File

@ -2,25 +2,8 @@
use pyo3::prelude::*;
#[pyfunction(_opt = "None", x = "5")]
fn function_with_args(_opt: Option<i32>, _x: i32) {}
#[pyfunction]
fn function_with_required_after_option(_opt: Option<i32>, _x: i32) {}
#[pyclass]
#[pyo3(text_signature = "()")]
struct MyClass;
#[pymethods]
impl MyClass {
#[args(_opt = "None", x = "5")]
fn function_with_args(&self, _opt: Option<i32>, _x: i32) {}
#[args(_has_default = 1)]
fn default_arg_before_required_deprecated(&self, _has_default: isize, _required: isize) {}
}
fn main() {
function_with_required_after_option(None, 0);
function_with_args(None, 0);
}
fn main() {}

View File

@ -1,30 +1,11 @@
error: use of deprecated constant `pyo3::impl_::deprecations::PYFUNCTION_ARGUMENTS`: passing arbitrary arguments to `#[pyfunction()]` to specify the signature is being replaced by `#[pyo3(signature)]`
--> tests/ui/deprecations.rs:5:14
error: use of deprecated constant `pyo3::impl_::deprecations::PYCLASS_TEXT_SIGNATURE`: put `text_signature` on `#[new]` instead of `#[pyclass]`
--> tests/ui/deprecations.rs:6:8
|
5 | #[pyfunction(_opt = "None", x = "5")]
| ^^^^
6 | #[pyo3(text_signature = "()")]
| ^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui/deprecations.rs:1:9
|
1 | #![deny(deprecated)]
| ^^^^^^^^^^
error: use of deprecated constant `pyo3::impl_::deprecations::REQUIRED_ARGUMENT_AFTER_OPTION`: required arguments after an `Option<_>` argument are ambiguous and being phased out
= help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters
--> tests/ui/deprecations.rs:9:59
|
9 | fn function_with_required_after_option(_opt: Option<i32>, _x: i32) {}
| ^^
error: use of deprecated constant `pyo3::impl_::deprecations::PYMETHODS_ARGS_ATTRIBUTE`: the `#[args]` attribute for `#[methods]` is being replaced by `#[pyo3(signature)]`
--> tests/ui/deprecations.rs:16:7
|
16 | #[args(_opt = "None", x = "5")]
| ^^^^
error: use of deprecated constant `pyo3::impl_::deprecations::PYMETHODS_ARGS_ATTRIBUTE`: the `#[args]` attribute for `#[methods]` is being replaced by `#[pyo3(signature)]`
--> tests/ui/deprecations.rs:19:7
|
19 | #[args(_has_default = 1)]
| ^^^^

View File

@ -1,33 +0,0 @@
use pyo3::prelude::*;
#[pyfunction(a = 5, b)]
fn pos_after_kw(py: Python<'_>, a: i32, b: i32) -> PyObject {
[a.to_object(py), vararg.into()].to_object(py)
}
#[pyfunction(kwargs = "**", a = 5)]
fn kw_after_kwargs(py: Python<'_>, kwargs: &PyDict, a: i32) -> PyObject {
[a.to_object(py), vararg.into()].to_object(py)
}
#[pyfunction(a, "*", b, "/", c)]
fn pos_only_after_kw_only(py: Python<'_>, a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
#[pyfunction(a, args="*", "/", b)]
fn pos_only_after_args(py: Python<'_>, a: i32, args: Vec<i32>, b: i32) -> i32 {
a + b + c
}
#[pyfunction(a, kwargs="**", "/", b)]
fn pos_only_after_kwargs(py: Python<'_>, a: i32, args: Vec<i32>, b: i32) -> i32 {
a + b
}
#[pyfunction(kwargs = "**", "*", a)]
fn kw_only_after_kwargs(py: Python<'_>, kwargs: &PyDict, a: i32) -> PyObject {
[a.to_object(py), vararg.into()].to_object(py)
}
fn main() {}

View File

@ -1,35 +0,0 @@
error: positional argument or varargs(*) not allowed after keyword arguments
--> tests/ui/invalid_macro_args.rs:3:21
|
3 | #[pyfunction(a = 5, b)]
| ^
error: keyword argument or kwargs(**) is not allowed after kwargs(**)
--> tests/ui/invalid_macro_args.rs:8:29
|
8 | #[pyfunction(kwargs = "**", a = 5)]
| ^
error: / is not allowed after /, varargs(*), or kwargs(**)
--> tests/ui/invalid_macro_args.rs:13:25
|
13 | #[pyfunction(a, "*", b, "/", c)]
| ^^^
error: / is not allowed after /, varargs(*), or kwargs(**)
--> tests/ui/invalid_macro_args.rs:18:27
|
18 | #[pyfunction(a, args="*", "/", b)]
| ^^^
error: / is not allowed after /, varargs(*), or kwargs(**)
--> tests/ui/invalid_macro_args.rs:23:30
|
23 | #[pyfunction(a, kwargs="**", "/", b)]
| ^^^
error: * is not allowed after varargs(*) or kwargs(**)
--> tests/ui/invalid_macro_args.rs:28:29
|
28 | #[pyfunction(kwargs = "**", "*", a)]
| ^^^

View File

@ -16,11 +16,11 @@ error: expected argument from function definition `y` but got argument `x`
13 | #[pyo3(signature = (x))]
| ^
error: cannot define both function signature and legacy arguments
--> tests/ui/invalid_pyfunction_signatures.rs:19:8
error: expected one of: `name`, `pass_module`, `signature`, `text_signature`, `crate`
--> tests/ui/invalid_pyfunction_signatures.rs:18:14
|
19 | #[pyo3(signature = (x))]
| ^^^^^^^^^
18 | #[pyfunction(x)]
| ^
error: `*args` not allowed after `*`
--> tests/ui/invalid_pyfunction_signatures.rs:25:24
@ -52,8 +52,8 @@ error: arguments of type `Python` must not be part of the signature
47 | #[pyfunction(signature = (py))]
| ^^
error: cannot define both function signature and legacy arguments
--> tests/ui/invalid_pyfunction_signatures.rs:58:12
error: cannot find attribute `args` in this scope
--> tests/ui/invalid_pyfunction_signatures.rs:57:7
|
58 | #[pyo3(signature = (x))]
| ^^^^^^^^^
57 | #[args(x)]
| ^^^^

View File

@ -15,4 +15,7 @@ fn wildcard_argument(_: i32) {}
#[pyfunction]
fn destructured_argument((a, b): (i32, i32)) {}
#[pyfunction]
fn function_with_required_after_option(_opt: Option<i32>, _x: i32) {}
fn main() {}

View File

@ -29,3 +29,10 @@ error: destructuring in arguments is not supported
|
16 | 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:19:63
|
19 | fn function_with_required_after_option(_opt: Option<i32>, _x: i32) {}
| ^^^