Skip to content

Commit 76bf521

Browse files
authored
Merge pull request #3505 from davidhewitt/deprecate_dunder_new
deprecate undocumented `#[__new__]` form of `#[new]`
2 parents c0b5004 + 6c90325 commit 76bf521

File tree

8 files changed

+50
-13
lines changed

8 files changed

+50
-13
lines changed

newsfragments/3505.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deprecate undocumented `#[__new__]` form of `#[new]` attribute.

pyo3-macros-backend/src/deprecations.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use quote::{quote_spanned, ToTokens};
33

44
pub enum Deprecation {
55
PyClassTextSignature,
6+
PyMethodsNewDeprecatedForm,
67
}
78

89
impl Deprecation {
910
fn ident(&self, span: Span) -> syn::Ident {
1011
let string = match self {
1112
Deprecation::PyClassTextSignature => "PYCLASS_TEXT_SIGNATURE",
13+
Deprecation::PyMethodsNewDeprecatedForm => "PYMETHODS_NEW_DEPRECATED_FORM",
1214
};
1315
syn::Ident::new(string, span)
1416
}

pyo3-macros-backend/src/method.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt::Display;
22

33
use crate::attributes::{TextSignatureAttribute, TextSignatureAttributeValue};
4+
use crate::deprecations::{Deprecation, Deprecations};
45
use crate::params::impl_arg_params;
56
use crate::pyfunction::{FunctionSignature, PyFunctionArgPyO3Attributes};
67
use crate::pyfunction::{PyFunctionOptions, SignatureAttribute};
@@ -228,6 +229,7 @@ pub struct FnSpec<'a> {
228229
pub convention: CallingConvention,
229230
pub text_signature: Option<TextSignatureAttribute>,
230231
pub unsafety: Option<syn::Token![unsafe]>,
232+
pub deprecations: Deprecations,
231233
}
232234

233235
pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
@@ -275,8 +277,9 @@ impl<'a> FnSpec<'a> {
275277
} = options;
276278

277279
let mut python_name = name.map(|name| name.value.0);
280+
let mut deprecations = Deprecations::new();
278281

279-
let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name)?;
282+
let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name, &mut deprecations)?;
280283
ensure_signatures_on_valid_method(&fn_type, signature.as_ref(), text_signature.as_ref())?;
281284

282285
let name = &sig.ident;
@@ -315,6 +318,7 @@ impl<'a> FnSpec<'a> {
315318
output: ty,
316319
text_signature,
317320
unsafety: sig.unsafety,
321+
deprecations,
318322
})
319323
}
320324

@@ -326,8 +330,9 @@ impl<'a> FnSpec<'a> {
326330
sig: &syn::Signature,
327331
meth_attrs: &mut Vec<syn::Attribute>,
328332
python_name: &mut Option<syn::Ident>,
333+
deprecations: &mut Deprecations,
329334
) -> Result<FnType> {
330-
let mut method_attributes = parse_method_attributes(meth_attrs)?;
335+
let mut method_attributes = parse_method_attributes(meth_attrs, deprecations)?;
331336

332337
let name = &sig.ident;
333338
let parse_receiver = |msg: &'static str| {
@@ -648,7 +653,10 @@ impl MethodTypeAttribute {
648653
/// If the attribute does not match one of the attribute names, returns `Ok(None)`.
649654
///
650655
/// Otherwise will either return a parse error or the attribute.
651-
fn parse_if_matching_attribute(attr: &syn::Attribute) -> Result<Option<Self>> {
656+
fn parse_if_matching_attribute(
657+
attr: &syn::Attribute,
658+
deprecations: &mut Deprecations,
659+
) -> Result<Option<Self>> {
652660
fn ensure_no_arguments(meta: &syn::Meta, ident: &str) -> syn::Result<()> {
653661
match meta {
654662
syn::Meta::Path(_) => Ok(()),
@@ -693,9 +701,10 @@ impl MethodTypeAttribute {
693701
ensure_no_arguments(meta, "new")?;
694702
Ok(Some(MethodTypeAttribute::New(path.span())))
695703
} else if path.is_ident("__new__") {
696-
// TODO deprecate this form?
704+
let span = path.span();
705+
deprecations.push(Deprecation::PyMethodsNewDeprecatedForm, span);
697706
ensure_no_arguments(meta, "__new__")?;
698-
Ok(Some(MethodTypeAttribute::New(path.span())))
707+
Ok(Some(MethodTypeAttribute::New(span)))
699708
} else if path.is_ident("classmethod") {
700709
ensure_no_arguments(meta, "classmethod")?;
701710
Ok(Some(MethodTypeAttribute::ClassMethod(path.span())))
@@ -730,12 +739,15 @@ impl Display for MethodTypeAttribute {
730739
}
731740
}
732741

733-
fn parse_method_attributes(attrs: &mut Vec<syn::Attribute>) -> Result<Vec<MethodTypeAttribute>> {
742+
fn parse_method_attributes(
743+
attrs: &mut Vec<syn::Attribute>,
744+
deprecations: &mut Deprecations,
745+
) -> Result<Vec<MethodTypeAttribute>> {
734746
let mut new_attrs = Vec::new();
735747
let mut found_attrs = Vec::new();
736748

737749
for attr in attrs.drain(..) {
738-
match MethodTypeAttribute::parse_if_matching_attribute(&attr)? {
750+
match MethodTypeAttribute::parse_if_matching_attribute(&attr, deprecations)? {
739751
Some(attr) => found_attrs.push(attr),
740752
None => new_attrs.push(attr),
741753
}

pyo3-macros-backend/src/pyfunction.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{
33
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
44
FromPyWithAttribute, NameAttribute, TextSignatureAttribute,
55
},
6+
deprecations::Deprecations,
67
method::{self, CallingConvention, FnArg},
78
pymethod::check_generic,
89
utils::{ensure_not_async_fn, get_pyo3_crate},
@@ -230,6 +231,7 @@ pub fn impl_wrap_pyfunction(
230231
output: ty,
231232
text_signature,
232233
unsafety: func.sig.unsafety,
234+
deprecations: Deprecations::new(),
233235
};
234236

235237
let krate = get_pyo3_crate(&krate);

pyo3-macros-backend/src/pymethod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAn
335335
|| quote!(::std::option::Option::None),
336336
|text_signature| quote!(::std::option::Option::Some(#text_signature)),
337337
);
338+
let deprecations = &spec.deprecations;
338339
let slot_def = quote! {
339340
_pyo3::ffi::PyType_Slot {
340341
slot: _pyo3::ffi::Py_tp_new,
@@ -345,6 +346,8 @@ fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAn
345346
kwargs: *mut _pyo3::ffi::PyObject,
346347
) -> *mut _pyo3::ffi::PyObject
347348
{
349+
#deprecations
350+
348351
use _pyo3::impl_::pyclass::*;
349352
impl PyClassNewTextSignature<#cls> for PyClassImplCollector<#cls> {
350353
#[inline]

src/impl_/deprecations.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
note = "put `text_signature` on `#[new]` instead of `#[pyclass]`"
66
)]
77
pub const PYCLASS_TEXT_SIGNATURE: () = ();
8+
9+
#[deprecated(since = "0.20.0", note = "use `#[new]` instead of `#[__new__]`")]
10+
pub const PYMETHODS_NEW_DEPRECATED_FORM: () = ();

tests/ui/deprecations.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,12 @@ use pyo3::prelude::*;
66
#[pyo3(text_signature = "()")]
77
struct MyClass;
88

9+
#[pymethods]
10+
impl MyClass {
11+
#[__new__]
12+
fn new() -> Self {
13+
Self
14+
}
15+
}
16+
917
fn main() {}

tests/ui/deprecations.stderr

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
error: use of deprecated constant `pyo3::impl_::deprecations::PYMETHODS_NEW_DEPRECATED_FORM`: use `#[new]` instead of `#[__new__]`
2+
--> tests/ui/deprecations.rs:11:7
3+
|
4+
11 | #[__new__]
5+
| ^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> tests/ui/deprecations.rs:1:9
9+
|
10+
1 | #![deny(deprecated)]
11+
| ^^^^^^^^^^
12+
113
error: use of deprecated constant `pyo3::impl_::deprecations::PYCLASS_TEXT_SIGNATURE`: put `text_signature` on `#[new]` instead of `#[pyclass]`
214
--> tests/ui/deprecations.rs:6:8
315
|
416
6 | #[pyo3(text_signature = "()")]
517
| ^^^^^^^^^^^^^^
6-
|
7-
note: the lint level is defined here
8-
--> tests/ui/deprecations.rs:1:9
9-
|
10-
1 | #![deny(deprecated)]
11-
| ^^^^^^^^^^

0 commit comments

Comments
 (0)