Skip to content

Commit

Permalink
remove all functionality deprecated in PyO3 0.18
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jun 13, 2023
1 parent a053489 commit beb4dde
Show file tree
Hide file tree
Showing 24 changed files with 47 additions and 1,304 deletions.
83 changes: 0 additions & 83 deletions guide/src/function/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3232.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove all functionality deprecated in PyO3 0.18.
6 changes: 0 additions & 6 deletions pyo3-macros-backend/src/deprecations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
38 changes: 4 additions & 34 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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]>,
Expand Down Expand Up @@ -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)?;
Expand All @@ -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 =
Expand All @@ -335,7 +325,6 @@ impl<'a> FnSpec<'a> {
python_name,
signature,
output: ty,
deprecations,
text_signature,
unsafety: sig.unsafety,
})
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)?;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand All @@ -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";
Expand Down
Loading

0 comments on commit beb4dde

Please sign in to comment.