Skip to content

Commit 646beb7

Browse files
committed
fix deprecation warning for trailing optional on #[setter] functions
1 parent f3603a0 commit 646beb7

File tree

6 files changed

+86
-44
lines changed

6 files changed

+86
-44
lines changed

newsfragments/4304.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix invalid deprecation warning for trailing optional on `#[setter]` function.

pyo3-macros-backend/src/deprecations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl<'ctx> ToTokens for Deprecations<'ctx> {
5151

5252
pub(crate) fn deprecate_trailing_option_default(spec: &FnSpec<'_>) -> TokenStream {
5353
if spec.signature.attribute.is_none()
54+
&& spec.tp.signature_attribute_allowed()
5455
&& spec.signature.arguments.iter().any(|arg| {
5556
if let FnArg::Regular(arg) = arg {
5657
arg.option_wrapped_type.is_some()

pyo3-macros-backend/src/method.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,18 @@ impl FnType {
228228
}
229229
}
230230

231+
pub fn signature_attribute_allowed(&self) -> bool {
232+
match self {
233+
FnType::Fn(_)
234+
| FnType::FnNew
235+
| FnType::FnStatic
236+
| FnType::FnClass(_)
237+
| FnType::FnNewClass(_)
238+
| FnType::FnModule(_) => true,
239+
FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => false,
240+
}
241+
}
242+
231243
pub fn self_arg(
232244
&self,
233245
cls: Option<&syn::Type>,
@@ -1096,15 +1108,18 @@ fn ensure_signatures_on_valid_method(
10961108
if let Some(signature) = signature {
10971109
match fn_type {
10981110
FnType::Getter(_) => {
1111+
debug_assert!(!fn_type.signature_attribute_allowed());
10991112
bail_spanned!(signature.kw.span() => "`signature` not allowed with `getter`")
11001113
}
11011114
FnType::Setter(_) => {
1115+
debug_assert!(!fn_type.signature_attribute_allowed());
11021116
bail_spanned!(signature.kw.span() => "`signature` not allowed with `setter`")
11031117
}
11041118
FnType::ClassAttribute => {
1119+
debug_assert!(!fn_type.signature_attribute_allowed());
11051120
bail_spanned!(signature.kw.span() => "`signature` not allowed with `classattr`")
11061121
}
1107-
_ => {}
1122+
_ => debug_assert!(fn_type.signature_attribute_allowed()),
11081123
}
11091124
}
11101125
if let Some(text_signature) = text_signature {

tests/test_getter_setter.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,39 @@ fn frozen_py_field_get() {
280280
py_run!(py, inst, "assert inst.value == 'value'");
281281
});
282282
}
283+
284+
#[test]
285+
fn test_optional_setter() {
286+
#[pyclass]
287+
struct SimpleClass {
288+
field: Option<u32>,
289+
}
290+
291+
#[pymethods]
292+
impl SimpleClass {
293+
#[getter]
294+
fn get_field(&self) -> Option<u32> {
295+
self.field
296+
}
297+
298+
#[setter]
299+
fn set_field(&mut self, field: Option<u32>) {
300+
self.field = field;
301+
}
302+
}
303+
304+
Python::with_gil(|py| {
305+
let instance = Py::new(py, SimpleClass { field: None }).unwrap();
306+
py_run!(py, instance, "assert instance.field is None");
307+
py_run!(
308+
py,
309+
instance,
310+
"instance.field = 42; assert instance.field == 42"
311+
);
312+
py_run!(
313+
py,
314+
instance,
315+
"instance.field = None; assert instance.field is None"
316+
);
317+
})
318+
}

tests/ui/deprecations.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ impl MyClass {
3939
#[setter]
4040
fn set_bar_bound(&self, _value: &Bound<'_, PyAny>) {}
4141

42-
#[setter]
43-
fn set_option(&self, _value: Option<i32>) {}
44-
4542
fn __eq__(&self, #[pyo3(from_py_with = "extract_gil_ref")] _other: i32) -> bool {
4643
true
4744
}

tests/ui/deprecations.stderr

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,34 @@ note: the lint level is defined here
1010
1 | #![deny(deprecated)]
1111
| ^^^^^^^^^^
1212

13-
error: use of deprecated constant `MyClass::__pymethod_set_set_option__::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
14-
= note: these implicit defaults are being phased out
15-
= help: add `#[pyo3(signature = (_value=None))]` to this function to silence this warning and keep the current behavior
16-
--> tests/ui/deprecations.rs:43:8
17-
|
18-
43 | fn set_option(&self, _value: Option<i32>) {}
19-
| ^^^^^^^^^^
20-
2113
error: use of deprecated constant `__pyfunction_pyfunction_option_2::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
2214
= note: these implicit defaults are being phased out
2315
= help: add `#[pyo3(signature = (_i, _any=None))]` to this function to silence this warning and keep the current behavior
24-
--> tests/ui/deprecations.rs:132:4
16+
--> tests/ui/deprecations.rs:129:4
2517
|
26-
132 | fn pyfunction_option_2(_i: u32, _any: Option<i32>) {}
18+
129 | fn pyfunction_option_2(_i: u32, _any: Option<i32>) {}
2719
| ^^^^^^^^^^^^^^^^^^^
2820

2921
error: use of deprecated constant `__pyfunction_pyfunction_option_3::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
3022
= note: these implicit defaults are being phased out
3123
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
32-
--> tests/ui/deprecations.rs:135:4
24+
--> tests/ui/deprecations.rs:132:4
3325
|
34-
135 | fn pyfunction_option_3(_i: u32, _any: Option<i32>, _foo: Option<String>) {}
26+
132 | fn pyfunction_option_3(_i: u32, _any: Option<i32>, _foo: Option<String>) {}
3527
| ^^^^^^^^^^^^^^^^^^^
3628

3729
error: use of deprecated constant `__pyfunction_pyfunction_option_4::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
3830
= note: these implicit defaults are being phased out
3931
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
40-
--> tests/ui/deprecations.rs:138:4
32+
--> tests/ui/deprecations.rs:135:4
4133
|
42-
138 | fn pyfunction_option_4(
34+
135 | fn pyfunction_option_4(
4335
| ^^^^^^^^^^^^^^^^^^^
4436

4537
error: use of deprecated constant `SimpleEnumWithoutEq::__pyo3__generated____richcmp__::DEPRECATION`: Implicit equality for simple enums is deprecated. Use `#[pyclass(eq, eq_int)` to keep the current behavior.
46-
--> tests/ui/deprecations.rs:200:1
38+
--> tests/ui/deprecations.rs:197:1
4739
|
48-
200 | #[pyclass]
40+
197 | #[pyclass]
4941
| ^^^^^^^^^^
5042
|
5143
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -57,9 +49,9 @@ error: use of deprecated struct `pyo3::PyCell`: `PyCell` was merged into `Bound`
5749
| ^^^^^^
5850

5951
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
60-
--> tests/ui/deprecations.rs:45:44
52+
--> tests/ui/deprecations.rs:42:44
6153
|
62-
45 | fn __eq__(&self, #[pyo3(from_py_with = "extract_gil_ref")] _other: i32) -> bool {
54+
42 | fn __eq__(&self, #[pyo3(from_py_with = "extract_gil_ref")] _other: i32) -> bool {
6355
| ^^^^^^^^^^^^^^^^^
6456

6557
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
@@ -93,69 +85,69 @@ error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`
9385
| ^
9486

9587
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
96-
--> tests/ui/deprecations.rs:64:44
88+
--> tests/ui/deprecations.rs:61:44
9789
|
98-
64 | fn pyfunction_with_module_gil_ref(_module: &PyModule) -> PyResult<&str> {
90+
61 | fn pyfunction_with_module_gil_ref(_module: &PyModule) -> PyResult<&str> {
9991
| ^
10092

10193
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
102-
--> tests/ui/deprecations.rs:74:19
94+
--> tests/ui/deprecations.rs:71:19
10395
|
104-
74 | fn module_gil_ref(_m: &PyModule) -> PyResult<()> {
96+
71 | fn module_gil_ref(_m: &PyModule) -> PyResult<()> {
10597
| ^^
10698

10799
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
108-
--> tests/ui/deprecations.rs:79:57
100+
--> tests/ui/deprecations.rs:76:57
109101
|
110-
79 | fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, _m: &PyModule) -> PyResult<()> {
102+
76 | fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, _m: &PyModule) -> PyResult<()> {
111103
| ^^
112104

113105
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
114-
--> tests/ui/deprecations.rs:115:27
106+
--> tests/ui/deprecations.rs:112:27
115107
|
116-
115 | #[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32,
108+
112 | #[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32,
117109
| ^^^^^^^^^^^^^^^^^
118110

119111
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
120-
--> tests/ui/deprecations.rs:121:29
112+
--> tests/ui/deprecations.rs:118:29
121113
|
122-
121 | fn pyfunction_gil_ref(_any: &PyAny) {}
114+
118 | fn pyfunction_gil_ref(_any: &PyAny) {}
123115
| ^
124116

125117
error: use of deprecated method `pyo3::deprecations::OptionGilRefs::<std::option::Option<T>>::function_arg`: use `Option<&Bound<'_, T>>` instead for this function argument
126-
--> tests/ui/deprecations.rs:125:36
118+
--> tests/ui/deprecations.rs:122:36
127119
|
128-
125 | fn pyfunction_option_gil_ref(_any: Option<&PyAny>) {}
120+
122 | fn pyfunction_option_gil_ref(_any: Option<&PyAny>) {}
129121
| ^^^^^^
130122

131123
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
132-
--> tests/ui/deprecations.rs:150:27
124+
--> tests/ui/deprecations.rs:147:27
133125
|
134-
150 | #[pyo3(from_py_with = "PyAny::len", item("my_object"))]
126+
147 | #[pyo3(from_py_with = "PyAny::len", item("my_object"))]
135127
| ^^^^^^^^^^^^
136128

137129
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
138-
--> tests/ui/deprecations.rs:160:27
130+
--> tests/ui/deprecations.rs:157:27
139131
|
140-
160 | #[pyo3(from_py_with = "PyAny::len")] usize,
132+
157 | #[pyo3(from_py_with = "PyAny::len")] usize,
141133
| ^^^^^^^^^^^^
142134

143135
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
144-
--> tests/ui/deprecations.rs:166:31
136+
--> tests/ui/deprecations.rs:163:31
145137
|
146-
166 | Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32),
138+
163 | Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32),
147139
| ^^^^^^^^^^^^^^^^^
148140

149141
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
150-
--> tests/ui/deprecations.rs:173:27
142+
--> tests/ui/deprecations.rs:170:27
151143
|
152-
173 | #[pyo3(from_py_with = "extract_gil_ref")]
144+
170 | #[pyo3(from_py_with = "extract_gil_ref")]
153145
| ^^^^^^^^^^^^^^^^^
154146

155147
error: use of deprecated method `pyo3::deprecations::GilRefs::<pyo3::Python<'_>>::is_python`: use `wrap_pyfunction_bound!` instead
156-
--> tests/ui/deprecations.rs:186:13
148+
--> tests/ui/deprecations.rs:183:13
157149
|
158-
186 | let _ = wrap_pyfunction!(double, py);
150+
183 | let _ = wrap_pyfunction!(double, py);
159151
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
160152
|
161153
= note: this error originates in the macro `wrap_pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)