pyfn: deprecate name argument

This commit is contained in:
David Hewitt 2021-05-16 10:41:59 +01:00
parent 51ba541a18
commit a109640850
21 changed files with 267 additions and 171 deletions

View File

@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `PyAsyncProtocol::__aenter__` and `PyAsyncProtocol::__aexit__`
- Deprecate `#[name = "..."]` attributes in favor of `#[pyo3(name = "...")]`. [#1567](https://github.com/PyO3/pyo3/pull/1567)
- Improve compilation times for projects using PyO3 [#1604](https://github.com/PyO3/pyo3/pull/1604)
- Deprecate string-literal second argument to `#[pyfn(m, "name")]`.
### Removed
- Remove deprecated exception names `BaseException` etc. [#1426](https://github.com/PyO3/pyo3/pull/1426)

View File

@ -242,8 +242,6 @@ These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API.
as every cycle must contain at least one mutable reference.
Example:
```rust
extern crate pyo3;
use pyo3::prelude::*;
use pyo3::PyTraverseError;
use pyo3::gc::{PyGCProtocol, PyVisit};

View File

@ -1,29 +1,8 @@
# Python Functions
PyO3 supports two ways to define a free function in Python. Both require registering
the function to a [module](./module.md).
PyO3 supports two ways to define a free function in Python. Both require registering the function to a [module](./module.md).
One way is defining the function in the module definition, annotated with `#[pyfn]`.
```rust
use pyo3::prelude::*;
#[pymodule]
fn rust2py(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "sum_as_string")]
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
Ok(format!("{}", a + b))
}
Ok(())
}
# fn main() {}
```
The other is annotating a function with `#[pyfunction]` and then adding it
to the module using the `wrap_pyfunction!` macro.
One way is annotating a function with `#[pyfunction]` and then adding it to the module using the `wrap_pyfunction!` macro.
```rust
use pyo3::prelude::*;
@ -36,7 +15,25 @@ fn double(x: usize) -> usize {
#[pymodule]
fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?).unwrap();
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
}
# fn main() {}
```
Alternatively there is a shorthand; the function can be placed inside the module definition and annotated with `#[pyfn]`, as below:
```rust
use pyo3::prelude::*;
#[pymodule]
fn rust2py(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m)]
fn sum_as_string(_py: Python, a:i64, b:i64) -> PyResult<String> {
Ok(format!("{}", a + b))
}
Ok(())
}
@ -44,16 +41,61 @@ fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
# fn main() {}
```
## Argument parsing
Both the `#[pyfunction]` and `#[pyfn]` attributes support specifying details of
argument parsing. The details are given in the section "Method arguments" in
the [Classes](class.md) chapter. Here is an example for a function that accepts
arbitrary keyword arguments (`**kwargs` in Python syntax) and returns the number
that was passed:
`#[pyfn(m)]` is just syntax sugar for `#[pyfunction]`, and takes all the same options documented in the rest of this chapter. The code above is expanded to the following:
```rust
use pyo3::prelude::*;
#[pymodule]
fn rust2py(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfunction]
fn sum_as_string(_py: Python, a:i64, b:i64) -> PyResult<String> {
Ok(format!("{}", a + b))
}
m.add_function(pyo3::wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}
# fn main() {}
```
## Function options
The `#[pyo3]` attribute can be used to modify properties of the generated Python function. It can take any combination of the following options:
- `#[pyo3(name = "...")]`
Overrides the name generated in Python:
```rust
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pyfunction]
#[pyo3(name = "no_args")]
fn no_args_py() -> usize { 42 }
#[pymodule]
fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(no_args_py, m)?)?;
Ok(())
}
# Python::with_gil(|py| {
# let m = pyo3::wrap_pymodule!(module_with_functions)(py);
# assert!(m.getattr(py, "no_args").is_ok());
# assert!(m.getattr(py, "no_args_py").is_err());
# });
```
## Argument parsing
The `#[pyfunction]` attribute supports specifying details of argument parsing. The details are given in the section ["Method arguments" of the Classes chapter](class.md#method-arguments). Here is an example for a function that accepts arbitrary keyword arguments (`**kwargs` in Python syntax) and returns the number that was passed:
```rust
# extern crate pyo3;
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use pyo3::types::PyDict;
@ -213,8 +255,7 @@ in Python code.
### Accessing the module of a function
It is possible to access the module of a `#[pyfunction]` and `#[pyfn]` in the
function body by passing the `pass_module` argument to the attribute:
It is possible to access the module of a `#[pyfunction]` in the function body by passing the `pass_module` argument to the attribute:
```rust
use pyo3::wrap_pyfunction;
@ -236,25 +277,6 @@ fn module_with_fn(py: Python, m: &PyModule) -> PyResult<()> {
If `pass_module` is set, the first argument **must** be the `&PyModule`. It is then possible to use the module
in the function body.
The same works for `#[pyfn]`:
```rust
use pyo3::wrap_pyfunction;
use pyo3::prelude::*;
#[pymodule]
fn module_with_fn(py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "module_name", pass_module)]
fn module_name(module: &PyModule) -> PyResult<&str> {
module.name()
}
Ok(())
}
# fn main() {}
```
## Accessing the FFI functions
In order to make Rust functions callable from Python, PyO3 generates a

View File

@ -15,7 +15,8 @@ fn rust2py(py: Python, m: &PyModule) -> PyResult<()> {
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
// Python objects to Rust values, and the Rust return value back into a Python object.
// The `_py` argument represents that we're holding the GIL.
#[pyfn(m, "sum_as_string")]
#[pyfn(m)]
#[pyo3(name = "sum_as_string")]
fn sum_as_string_py(_py: Python, a: i64, b: i64) -> PyResult<String> {
let out = sum_as_string(a, b);
Ok(out)
@ -32,7 +33,7 @@ fn sum_as_string(a: i64, b: i64) -> String {
# fn main() {}
```
The `#[pymodule]` procedural macro attribute takes care of exporting the initialization function of your
The `#[pymodule]` procedural macro attribute takes care of exporting the initialization function of your
module to Python. It can take as an argument the name of your module, which must be the name of the `.so`
or `.pyd` file; the default is the Rust function's name.
@ -41,7 +42,7 @@ If the name of the module (the default being the function name) does not match t
`ImportError: dynamic module does not define module export function (PyInit_name_of_your_module)`
To import the module, either copy the shared library as described in [the README](https://github.com/PyO3/pyo3)
or use a tool, e.g. `maturin develop` with [maturin](https://github.com/PyO3/maturin) or
or use a tool, e.g. `maturin develop` with [maturin](https://github.com/PyO3/maturin) or
`python setup.py develop` with [setuptools-rust](https://github.com/PyO3/setuptools-rust).
## Documentation
@ -95,4 +96,4 @@ fn supermodule(py: Python, module: &PyModule) -> PyResult<()> {
This way, you can create a module hierarchy within a single extension module.
It is not necessary to add `#[pymodule]` on nested modules, this is only required on the top-level module.
It is not necessary to add `#[pymodule]` on nested modules, this is only required on the top-level module.

View File

@ -6,6 +6,8 @@ use syn::{
Attribute, ExprPath, Ident, LitStr, Result, Token,
};
use crate::deprecations::{Deprecation, Deprecations};
pub mod kw {
syn::custom_keyword!(annotation);
syn::custom_keyword!(attribute);
@ -81,17 +83,18 @@ pub fn take_attributes(
Ok(())
}
pub fn get_deprecated_name_attribute(attr: &syn::Attribute) -> syn::Result<Option<NameAttribute>> {
pub fn get_deprecated_name_attribute(
attr: &syn::Attribute,
deprecations: &mut Deprecations,
) -> syn::Result<Option<NameAttribute>> {
match attr.parse_meta() {
Ok(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
})) if path.is_ident("name") => {
let mut ident: syn::Ident = s.parse()?;
// This span is the whole attribute span, which is nicer for reporting errors.
ident.set_span(attr.span());
Ok(Some(NameAttribute(ident)))
deprecations.push(Deprecation::NameAttribute, attr.span());
Ok(Some(NameAttribute(s.parse()?)))
}
_ => Ok(None),
}

View File

@ -0,0 +1,43 @@
use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
pub enum Deprecation {
NameAttribute,
PyfnNameArgument,
}
impl Deprecation {
fn ident(&self, span: Span) -> syn::Ident {
let string = match self {
Deprecation::NameAttribute => "NAME_ATTRIBUTE",
Deprecation::PyfnNameArgument => "PYFN_NAME_ARGUMENT",
};
syn::Ident::new(string, span)
}
}
#[derive(Default)]
pub struct Deprecations(Vec<(Deprecation, Span)>);
impl Deprecations {
pub fn new() -> Self {
Deprecations(Vec::new())
}
pub fn push(&mut self, deprecation: Deprecation, span: Span) {
self.0.push((deprecation, span))
}
}
impl ToTokens for Deprecations {
fn to_tokens(&self, tokens: &mut TokenStream) {
for (deprecation, span) in &self.0 {
let ident = deprecation.ident(*span);
quote_spanned!(
*span =>
let _ = pyo3::impl_::deprecations::#ident;
)
.to_tokens(tokens)
}
}
}

View File

@ -1,8 +1,10 @@
use crate::attributes::{
self, get_deprecated_name_attribute, get_pyo3_attributes, is_attribute_ident, take_attributes,
NameAttribute,
use crate::{
attributes::{
self, get_deprecated_name_attribute, get_pyo3_attributes, is_attribute_ident,
take_attributes, NameAttribute,
},
deprecations::Deprecations,
};
use crate::utils;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
@ -19,12 +21,10 @@ pub struct ConstSpec {
impl ConstSpec {
/// Null-terminated Python name
pub fn python_name_with_deprecation(&self) -> TokenStream {
pub fn null_terminated_python_name(&self) -> TokenStream {
if let Some(name) = &self.attributes.name {
let deprecation =
utils::name_deprecation_token(name.0.span(), self.attributes.name_is_deprecated);
let name = format!("{}\0", name.0);
quote!({#deprecation #name})
quote!({#name})
} else {
let name = format!("{}\0", self.rust_ident.unraw().to_string());
quote!(#name)
@ -35,7 +35,7 @@ impl ConstSpec {
pub struct ConstAttributes {
pub is_class_attr: bool,
pub name: Option<NameAttribute>,
pub name_is_deprecated: bool,
pub deprecations: Deprecations,
}
pub enum PyO3ConstAttribute {
@ -58,7 +58,7 @@ impl ConstAttributes {
let mut attributes = ConstAttributes {
is_class_attr: false,
name: None,
name_is_deprecated: false,
deprecations: Deprecations::new(),
};
take_attributes(attrs, |attr| {
@ -76,9 +76,10 @@ impl ConstAttributes {
}
}
Ok(true)
} else if let Some(name) = get_deprecated_name_attribute(attr)? {
} else if let Some(name) =
get_deprecated_name_attribute(attr, &mut attributes.deprecations)?
{
attributes.set_name(name)?;
attributes.name_is_deprecated = true;
Ok(true)
} else {
Ok(false)

View File

@ -10,6 +10,7 @@ mod utils;
mod attributes;
mod defs;
mod deprecations;
mod from_pyobject;
mod konst;
mod method;

View File

@ -1,9 +1,9 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::pyfunction::Argument;
use crate::pyfunction::PyFunctionOptions;
use crate::pyfunction::{PyFunctionArgPyO3Attributes, PyFunctionSignature};
use crate::utils;
use crate::{deprecations::Deprecations, pyfunction::Argument};
use proc_macro2::TokenStream;
use quote::ToTokens;
use quote::{quote, quote_spanned};
@ -122,7 +122,6 @@ impl SelfType {
}
}
#[derive(Clone, Debug)]
pub struct FnSpec<'a> {
pub tp: FnType,
// Rust function name
@ -134,7 +133,7 @@ pub struct FnSpec<'a> {
pub args: Vec<FnArg<'a>>,
pub output: syn::Type,
pub doc: syn::LitStr,
pub name_is_deprecated: bool,
pub deprecations: Deprecations,
}
pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
@ -217,15 +216,13 @@ impl<'a> FnSpec<'a> {
args: arguments,
output: ty,
doc,
name_is_deprecated: options.name_is_deprecated,
deprecations: options.deprecations,
})
}
pub fn python_name_with_deprecation(&self) -> TokenStream {
let deprecation =
utils::name_deprecation_token(self.python_name.span(), self.name_is_deprecated);
pub fn null_terminated_python_name(&self) -> TokenStream {
let name = format!("{}\0", self.python_name);
quote!({#deprecation #name})
quote!({#name})
}
fn parse_text_signature(

View File

@ -1,8 +1,11 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//! Code generation for the function that initializes a python module and adds classes and function.
use crate::attributes::{is_attribute_ident, take_attributes, NameAttribute};
use crate::pyfunction::{impl_wrap_pyfunction, PyFunctionOptions};
use crate::{
attributes::{is_attribute_ident, take_attributes, NameAttribute},
deprecations::Deprecation,
};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{parse::Parse, spanned::Spanned, token::Comma, Ident, Path};
@ -59,18 +62,35 @@ pub struct PyFnArgs {
impl Parse for PyFnArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let modname = input.parse()?;
let _: Comma = input.parse()?;
let fnname_literal: syn::LitStr = input.parse()?;
let fnname = fnname_literal.parse()?;
let modname = input.parse().map_err(
|e| err_spanned!(e.span() => "expected module as first argument to #[pyfn()]"),
)?;
if input.is_empty() {
let mut options = PyFunctionOptions::default();
options.set_name(NameAttribute(fnname))?;
return Ok(Self { modname, options });
return Ok(Self {
modname,
options: Default::default(),
});
}
let _: Comma = input.parse()?;
let mut deprecated_name_argument = None;
if let Ok(lit_str) = input.parse::<syn::LitStr>() {
deprecated_name_argument = Some(lit_str);
if !input.is_empty() {
let _: Comma = input.parse()?;
}
}
let mut options: PyFunctionOptions = input.parse()?;
options.set_name(NameAttribute(fnname))?;
if let Some(lit_str) = deprecated_name_argument {
options.set_name(NameAttribute(lit_str.parse()?))?;
options
.deprecations
.push(Deprecation::PyfnNameArgument, lit_str.span());
}
Ok(Self { modname, options })
}
}

View File

@ -508,10 +508,10 @@ fn impl_descriptors(
);
match desc {
FnType::Getter(self_ty) => {
impl_py_getter_def(cls, property_type, self_ty, &doc)
impl_py_getter_def(cls, property_type, self_ty, &doc, &Default::default())
}
FnType::Setter(self_ty) => {
impl_py_setter_def(cls, property_type, self_ty, &doc)
impl_py_setter_def(cls, property_type, self_ty, &doc, &Default::default())
}
_ => unreachable!(),
}

View File

@ -5,6 +5,7 @@ use crate::{
self, get_deprecated_name_attribute, get_pyo3_attributes, take_attributes,
FromPyWithAttribute, NameAttribute,
},
deprecations::Deprecations,
method::{self, FnArg, FnSpec},
pymethod::{check_generic, get_arg_names, impl_arg_params},
utils,
@ -209,8 +210,8 @@ impl PyFunctionSignature {
pub struct PyFunctionOptions {
pub pass_module: bool,
pub name: Option<NameAttribute>,
pub name_is_deprecated: bool,
pub signature: Option<PyFunctionSignature>,
pub deprecations: Deprecations,
}
impl Parse for PyFunctionOptions {
@ -218,8 +219,8 @@ impl Parse for PyFunctionOptions {
let mut options = PyFunctionOptions {
pass_module: false,
name: None,
name_is_deprecated: false,
signature: None,
deprecations: Deprecations::new(),
};
while !input.is_empty() {
@ -278,9 +279,9 @@ impl PyFunctionOptions {
if let Some(pyo3_attributes) = get_pyo3_attributes(attr)? {
self.add_attributes(pyo3_attributes)?;
Ok(true)
} else if let Some(name) = get_deprecated_name_attribute(attr)? {
} else if let Some(name) = get_deprecated_name_attribute(attr, &mut self.deprecations)?
{
self.set_name(name)?;
self.name_is_deprecated = true;
Ok(true)
} else {
Ok(false)
@ -390,11 +391,11 @@ pub fn impl_wrap_pyfunction(
args: arguments,
output: ty,
doc,
name_is_deprecated: options.name_is_deprecated,
deprecations: options.deprecations,
};
let doc = &spec.doc;
let python_name = spec.python_name_with_deprecation();
let python_name = spec.null_terminated_python_name();
let name = &func.sig.ident;
let wrapper_ident = format_ident!("__pyo3_raw_{}", name);
@ -453,12 +454,14 @@ fn function_c_wrapper(
)
};
let py = syn::Ident::new("_py", Span::call_site());
let deprecations = &spec.deprecations;
if spec.args.is_empty() {
Ok(quote! {
unsafe extern "C" fn #wrapper_ident(
_slf: *mut pyo3::ffi::PyObject,
_unused: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
#slf_module
#cb
@ -473,6 +476,7 @@ fn function_c_wrapper(
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
#slf_module
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);

View File

@ -1,6 +1,6 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::utils;
use crate::{attributes::FromPyWithAttribute, konst::ConstSpec};
use crate::{deprecations::Deprecations, utils};
use crate::{
method::{FnArg, FnSpec, FnType, SelfType},
pyfunction::PyFunctionOptions,
@ -48,12 +48,14 @@ pub fn gen_py_method(
PropertyType::Function(&spec),
self_ty,
&spec.doc,
&spec.deprecations,
)?),
FnType::Setter(self_ty) => GeneratedPyMethod::Method(impl_py_setter_def(
cls,
PropertyType::Function(&spec),
self_ty,
&spec.doc,
&spec.deprecations,
)?),
})
}
@ -72,8 +74,10 @@ pub(crate) fn check_generic(sig: &syn::Signature) -> syn::Result<()> {
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> TokenStream {
let member = &spec.rust_ident;
let deprecations = &spec.attributes.deprecations;
let wrapper = quote! {{
fn __wrap(py: pyo3::Python<'_>) -> pyo3::PyObject {
#deprecations
pyo3::IntoPy::into_py(#cls::#member, py)
}
__wrap
@ -91,12 +95,14 @@ pub fn impl_wrap_cfunction_with_keywords(
let slf = self_ty.receiver(cls);
let py = syn::Ident::new("_py", Span::call_site());
let body = impl_arg_params(&spec, Some(cls), body, &py)?;
let deprecations = &spec.deprecations;
Ok(quote! {{
unsafe extern "C" fn __wrap(
_slf: *mut pyo3::ffi::PyObject,
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
#slf
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
@ -113,6 +119,7 @@ pub fn impl_wrap_cfunction_with_keywords(
pub fn impl_wrap_noargs(cls: &syn::Type, spec: &FnSpec<'_>, self_ty: &SelfType) -> TokenStream {
let body = impl_call(cls, &spec);
let slf = self_ty.receiver(cls);
let deprecations = &spec.deprecations;
assert!(spec.args.is_empty());
quote! {{
unsafe extern "C" fn __wrap(
@ -120,6 +127,7 @@ pub fn impl_wrap_noargs(cls: &syn::Type, spec: &FnSpec<'_>, self_ty: &SelfType)
_args: *mut pyo3::ffi::PyObject,
) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|_py| {
#slf
#body
@ -136,7 +144,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream>
let cb = quote! { #cls::#name(#(#names),*) };
let py = syn::Ident::new("_py", Span::call_site());
let body = impl_arg_params(spec, Some(cls), cb, &py)?;
let deprecations = &spec.deprecations;
Ok(quote! {{
#[allow(unused_mut)]
unsafe extern "C" fn __wrap(
@ -144,8 +152,8 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream>
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
use pyo3::callback::IntoPyCallbackOutput;
pyo3::callback::handle_panic(|#py| {
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
@ -166,7 +174,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream
let cb = quote! { pyo3::callback::convert(_py, #cls::#name(&_cls, #(#names),*)) };
let py = syn::Ident::new("_py", Span::call_site());
let body = impl_arg_params(spec, Some(cls), cb, &py)?;
let deprecations = &spec.deprecations;
Ok(quote! {{
#[allow(unused_mut)]
unsafe extern "C" fn __wrap(
@ -174,6 +182,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
let _cls = pyo3::types::PyType::from_type_ptr(#py, _cls as *mut pyo3::ffi::PyTypeObject);
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
@ -193,7 +202,7 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStrea
let cb = quote! { pyo3::callback::convert(_py, #cls::#name(#(#names),*)) };
let py = syn::Ident::new("_py", Span::call_site());
let body = impl_arg_params(spec, Some(cls), cb, &py)?;
let deprecations = &spec.deprecations;
Ok(quote! {{
#[allow(unused_mut)]
unsafe extern "C" fn __wrap(
@ -201,6 +210,7 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStrea
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
@ -218,9 +228,10 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStrea
pub fn impl_wrap_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
let name = &spec.name;
let cb = quote! { #cls::#name() };
let deprecations = &spec.deprecations;
quote! {{
fn __wrap(py: pyo3::Python<'_>) -> pyo3::PyObject {
#deprecations
pyo3::IntoPy::into_py(#cb, py)
}
__wrap
@ -578,7 +589,7 @@ pub fn impl_py_method_def(
flags: Option<TokenStream>,
) -> Result<TokenStream> {
let add_flags = flags.map(|flags| quote!(.flags(#flags)));
let python_name = spec.python_name_with_deprecation();
let python_name = spec.null_terminated_python_name();
let doc = &spec.doc;
if spec.args.is_empty() {
let wrapper = impl_wrap_noargs(cls, spec, self_ty);
@ -621,7 +632,7 @@ pub fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec) -> Result<TokenStr
pub fn impl_py_method_def_class(cls: &syn::Type, spec: &FnSpec) -> Result<TokenStream> {
let wrapper = impl_wrap_class(cls, &spec)?;
let python_name = spec.python_name_with_deprecation();
let python_name = spec.null_terminated_python_name();
let doc = &spec.doc;
Ok(quote! {
pyo3::class::PyMethodDefType::Class({
@ -636,7 +647,7 @@ pub fn impl_py_method_def_class(cls: &syn::Type, spec: &FnSpec) -> Result<TokenS
pub fn impl_py_method_def_static(cls: &syn::Type, spec: &FnSpec) -> Result<TokenStream> {
let wrapper = impl_wrap_static(cls, &spec)?;
let python_name = spec.python_name_with_deprecation();
let python_name = spec.null_terminated_python_name();
let doc = &spec.doc;
Ok(quote! {
pyo3::class::PyMethodDefType::Static({
@ -651,7 +662,7 @@ pub fn impl_py_method_def_static(cls: &syn::Type, spec: &FnSpec) -> Result<Token
pub fn impl_py_method_class_attribute(cls: &syn::Type, spec: &FnSpec) -> TokenStream {
let wrapper = impl_wrap_class_attribute(cls, &spec);
let python_name = spec.python_name_with_deprecation();
let python_name = spec.null_terminated_python_name();
quote! {
pyo3::class::PyMethodDefType::ClassAttribute({
pyo3::class::PyClassAttributeDef::new(
@ -663,7 +674,7 @@ pub fn impl_py_method_class_attribute(cls: &syn::Type, spec: &FnSpec) -> TokenSt
}
pub fn impl_py_const_class_attribute(spec: &ConstSpec, wrapper: &TokenStream) -> TokenStream {
let python_name = &spec.python_name_with_deprecation();
let python_name = &spec.null_terminated_python_name();
quote! {
{
pyo3::class::PyMethodDefType::ClassAttribute({
@ -696,17 +707,19 @@ pub(crate) fn impl_py_setter_def(
property_type: PropertyType,
self_ty: &SelfType,
doc: &syn::LitStr,
deprecations: &Deprecations,
) -> Result<TokenStream> {
let python_name = match property_type {
PropertyType::Descriptor(ident) => {
let formatted_name = format!("{}\0", ident.unraw());
quote!(#formatted_name)
}
PropertyType::Function(spec) => spec.python_name_with_deprecation(),
PropertyType::Function(spec) => spec.null_terminated_python_name(),
};
let wrapper = impl_wrap_setter(cls, property_type, self_ty)?;
Ok(quote! {
pyo3::class::PyMethodDefType::Setter({
#deprecations
pyo3::class::PySetterDef::new(
#python_name,
pyo3::class::methods::PySetter(#wrapper),
@ -721,17 +734,19 @@ pub(crate) fn impl_py_getter_def(
property_type: PropertyType,
self_ty: &SelfType,
doc: &syn::LitStr,
deprecations: &Deprecations,
) -> Result<TokenStream> {
let python_name = match property_type {
PropertyType::Descriptor(ident) => {
let formatted_name = format!("{}\0", ident.unraw());
quote!(#formatted_name)
}
PropertyType::Function(spec) => spec.python_name_with_deprecation(),
PropertyType::Function(spec) => spec.null_terminated_python_name(),
};
let wrapper = impl_wrap_getter(cls, property_type, self_ty)?;
Ok(quote! {
pyo3::class::PyMethodDefType::Getter({
#deprecations
pyo3::class::PyGetterDef::new(
#python_name,
pyo3::class::methods::PyGetter(#wrapper),

View File

@ -1,6 +1,5 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
use proc_macro2::Span;
use syn::spanned::Spanned;
/// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span.
@ -165,14 +164,3 @@ pub fn get_doc(
Ok(syn::LitStr::new(&doc, span))
}
pub fn name_deprecation_token(span: Span, name_is_deprecated: bool) -> Option<TokenStream> {
if name_is_deprecated {
Some(quote_spanned!(
span =>
let _ = pyo3::impl_::deprecations::NAME_ATTRIBUTE;
))
} else {
None
}
}

View File

@ -2,12 +2,4 @@
//! any of these APIs in downstream code is implicitly acknowledging that these APIs may change at
//! any time without documentation in the CHANGELOG and without breaking semver guarantees.
/// Symbols to represent deprecated uses of PyO3's macros.
pub mod deprecations {
#[doc(hidden)]
#[deprecated(
since = "0.14.0",
note = "use `#[pyo3(name = \"...\")]` instead of `#[name = \"...\"]`"
)]
pub const NAME_ATTRIBUTE: () = ();
}
pub mod deprecations;

13
src/impl_/deprecations.rs Normal file
View File

@ -0,0 +1,13 @@
//! Symbols used to denote deprecated usages of PyO3's proc macros.
#[deprecated(
since = "0.14.0",
note = "use `#[pyo3(name = \"...\")]` instead of `#[name = \"...\"]`"
)]
pub const NAME_ATTRIBUTE: () = ();
#[deprecated(
since = "0.14.0",
note = "use `#[pyfn(m)] #[pyo3(name = \"...\")]` instead of `#[pyfn(m, \"...\")]`"
)]
pub const PYFN_NAME_ARGUMENT: () = ();

View File

@ -1,7 +1,7 @@
use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::types::{IntoPyDict, PyDict, PyTuple};
use pyo3::{py_run, wrap_pyfunction};
mod common;
#[pyclass]
@ -36,24 +36,24 @@ fn double(x: usize) -> usize {
/// This module is implemented in Rust.
#[pymodule]
fn module_with_functions(_py: Python, m: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
#![allow(deprecated)]
#[pyfn(m, "sum_as_string")]
fn sum_as_string_py(_py: Python, a: i64, b: i64) -> String {
fn function_with_deprecated_name(_py: Python, a: i64, b: i64) -> String {
sum_as_string(a, b)
}
#[pyfn(m, "no_parameters")]
fn no_parameters() -> usize {
#[pyfn(m)]
#[pyo3(name = "no_parameters")]
fn function_with_name() -> usize {
42
}
#[pyfn(m, "with_module", pass_module)]
#[pyfn(m, pass_module)]
fn with_module(module: &PyModule) -> PyResult<&str> {
module.name()
}
#[pyfn(m, "double_value")]
#[pyfn(m)]
fn double_value(v: &ValueClass) -> usize {
v.value * 2
}
@ -174,8 +174,6 @@ fn r#move() -> usize {
#[pymodule]
fn raw_ident_module(_py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(r#move, module)?)
}
@ -199,8 +197,6 @@ fn custom_named_fn() -> usize {
#[pymodule]
fn foobar_module(_py: Python, m: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
m.add_function(wrap_pyfunction!(custom_named_fn, m)?)?;
m.dict().set_item("yay", "me")?;
Ok(())
@ -241,16 +237,12 @@ fn subfunction() -> String {
}
fn submodule(module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(subfunction, module)?)?;
Ok(())
}
#[pymodule]
fn submodule_with_init_fn(_py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(subfunction, module)?)?;
Ok(())
}
@ -262,8 +254,6 @@ fn superfunction() -> String {
#[pymodule]
fn supermodule(py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(superfunction, module)?)?;
let module_to_add = PyModule::new(py, "submodule")?;
submodule(module_to_add)?;
@ -308,13 +298,12 @@ fn ext_vararg_fn(py: Python, a: i32, vararg: &PyTuple) -> PyObject {
#[pymodule]
fn vararg_module(_py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "int_vararg_fn", a = 5, vararg = "*")]
#[pyfn(m, a = 5, vararg = "*")]
fn int_vararg_fn(py: Python, a: i32, vararg: &PyTuple) -> PyObject {
ext_vararg_fn(py, a, vararg)
}
m.add_function(pyo3::wrap_pyfunction!(ext_vararg_fn, m)?)
.unwrap();
m.add_function(wrap_pyfunction!(ext_vararg_fn, m)?).unwrap();
Ok(())
}
@ -390,17 +379,11 @@ fn pyfunction_with_module_and_args_kwargs<'a>(
#[pymodule]
fn module_with_functions_with_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_py, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_arg, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(
pyfunction_with_module_and_default_arg,
m
)?)?;
m.add_function(pyo3::wrap_pyfunction!(
pyfunction_with_module_and_args_kwargs,
m
)?)
m.add_function(wrap_pyfunction!(pyfunction_with_module, m)?)?;
m.add_function(wrap_pyfunction!(pyfunction_with_module_and_py, m)?)?;
m.add_function(wrap_pyfunction!(pyfunction_with_module_and_arg, m)?)?;
m.add_function(wrap_pyfunction!(pyfunction_with_module_and_default_arg, m)?)?;
m.add_function(wrap_pyfunction!(pyfunction_with_module_and_args_kwargs, m)?)
}
#[test]

View File

@ -119,7 +119,7 @@ fn test_function() {
fn test_pyfn() {
#[pymodule]
fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "my_function", a, b = "None", "*", c = 42)]
#[pyfn(m, a, b = "None", "*", c = 42)]
#[text_signature = "(a, b=None, *, c=42)"]
fn my_function(a: i32, b: Option<i32>, c: i32) {
let _ = (a, b, c);

View File

@ -25,6 +25,14 @@ impl TestClass {
#[name = "foo"]
fn deprecated_name_pyfunction() { }
#[pymodule]
fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "some_name")]
fn deprecated_name_pyfn() { }
Ok(())
}
fn main() {
}

View File

@ -27,3 +27,9 @@ error: use of deprecated constant `pyo3::impl_::deprecations::NAME_ATTRIBUTE`: u
|
25 | #[name = "foo"]
| ^
error: use of deprecated constant `pyo3::impl_::deprecations::PYFN_NAME_ARGUMENT`: use `#[pyfn(m)] #[pyo3(name = "...")]` instead of `#[pyfn(m, "...")]`
--> $DIR/deprecations.rs:30:15
|
30 | #[pyfn(m, "some_name")]
| ^^^^^^^^^^^

View File

@ -2,11 +2,11 @@ use pyo3::prelude::*;
#[pymodule]
fn module(_py: Python, m: &PyModule) -> PyResult<()> {
#[pyfn(m, "with_module", pass_module)]
#[pyfn(m, pass_module)]
fn fail(string: &str, module: &PyModule) -> PyResult<&str> {
module.name()
}
Ok(())
}
fn main(){}
fn main(){}