Skip to content

Commit 762ef5f

Browse files
committed
emit deprecation on pyclasses that are affected by the FromPyObject blanket impl
1 parent 85e6507 commit 762ef5f

File tree

20 files changed

+90
-37
lines changed

20 files changed

+90
-37
lines changed

guide/src/class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ Classes can also be passed by value if they can be cloned, i.e. they automatical
887887
```rust,no_run
888888
# #![allow(dead_code)]
889889
# use pyo3::prelude::*;
890-
#[pyclass]
890+
#[pyclass(from_py_object)]
891891
#[derive(Clone)]
892892
struct MyClass {
893893
my_field: Box<i32>,

guide/src/faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ You may have a nested struct similar to this:
8888

8989
```rust,no_run
9090
# use pyo3::prelude::*;
91-
#[pyclass]
91+
#[pyclass(from_py_object)]
9292
#[derive(Clone)]
9393
struct Inner {/* fields omitted */}
9494

newsfragments/5550.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
deprecate implicit by value extraction of pyclasses

pyo3-macros-backend/src/pyclass.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,20 @@ impl<'a> PyClassImplsBuilder<'a> {
25172517
quote! {}
25182518
};
25192519

2520+
let deprecation = if self.attr.options.skip_from_py_object.is_none()
2521+
&& self.attr.options.from_py_object.is_none()
2522+
{
2523+
quote! {
2524+
const _: () = {
2525+
#[allow(unused_import)]
2526+
use #pyo3_path::impl_::pyclass::Probe as _;
2527+
#pyo3_path::impl_::deprecated::DeprecatedFromPyObjectBlanket::<{ #pyo3_path::impl_::pyclass::IsClone::<#cls>::VALUE }>::MSG
2528+
};
2529+
}
2530+
} else {
2531+
TokenStream::new()
2532+
};
2533+
25202534
let extract_pyclass_with_clone = if let Some(from_py_object) =
25212535
self.attr.options.from_py_object
25222536
{
@@ -2546,6 +2560,8 @@ impl<'a> PyClassImplsBuilder<'a> {
25462560
};
25472561

25482562
Ok(quote! {
2563+
#deprecation
2564+
25492565
#extract_pyclass_with_clone
25502566

25512567
#assertions

pytests/src/pyclasses.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pyo3::exceptions::{PyStopIteration, PyValueError};
44
use pyo3::prelude::*;
55
use pyo3::types::PyType;
66

7-
#[pyclass]
7+
#[pyclass(from_py_object)]
88
#[derive(Clone, Default)]
99
struct EmptyClass {}
1010

@@ -70,7 +70,7 @@ impl PyClassThreadIter {
7070
}
7171

7272
/// Demonstrates a base class which can operate on the relevant subclass in its constructor.
73-
#[pyclass(subclass)]
73+
#[pyclass(subclass, skip_from_py_object)]
7474
#[derive(Clone, Debug)]
7575
struct AssertingBaseClass;
7676

@@ -104,7 +104,7 @@ impl ClassWithDict {
104104
}
105105
}
106106

107-
#[pyclass]
107+
#[pyclass(skip_from_py_object)]
108108
#[derive(Clone)]
109109
struct ClassWithDecorators {
110110
attr: usize,

src/impl_.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod callback;
1010
pub mod concat;
1111
#[cfg(feature = "experimental-async")]
1212
pub mod coroutine;
13+
pub mod deprecated;
1314
pub mod exceptions;
1415
pub mod extract_argument;
1516
pub mod freelist;

src/impl_/deprecated.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub struct DeprecatedFromPyObjectBlanket<const IS_CLONE: bool> {}
2+
3+
impl DeprecatedFromPyObjectBlanket<true> {
4+
#[deprecated(
5+
since = "0.28.0",
6+
note = "Implicit by value extraction of pyclasses is deprecated. Use `from_py_object` to keep the current behaviour or `skip_from_py_object` to opt-out."
7+
)]
8+
pub const MSG: () = ();
9+
}
10+
11+
impl DeprecatedFromPyObjectBlanket<false> {
12+
pub const MSG: () = ();
13+
}

src/impl_/pyclass/probes.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ impl<T: super::doc::PyClassNewTextSignature> HasNewTextSignature<T> {
7373
pub const VALUE: bool = true;
7474
}
7575

76+
probe!(IsClone);
77+
78+
impl<T: Clone> IsClone<T> {
79+
pub const VALUE: bool = true;
80+
}
81+
7682
#[cfg(test)]
7783
macro_rules! value_of {
7884
($probe:ident, $ty:ty) => {{

src/marker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ mod tests {
10071007
fn test_py_run_inserts_globals_2() {
10081008
use std::ffi::CString;
10091009

1010-
#[crate::pyclass(crate = "crate")]
1010+
#[crate::pyclass(crate = "crate", skip_from_py_object)]
10111011
#[derive(Clone)]
10121012
struct CodeRunner {
10131013
code: CString,

src/pycell.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ mod tests {
751751

752752
use super::*;
753753

754-
#[crate::pyclass]
754+
#[crate::pyclass(skip_from_py_object)]
755755
#[pyo3(crate = "crate")]
756756
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
757757
struct SomeClass(i32);

0 commit comments

Comments
 (0)