Skip to content

Commit e9e0da5

Browse files
authored
Merge pull request #880 from davidhewitt/py_get_set
Allow use of `#[pyo3(get, set)]` with `Py<T>`
2 parents dcab478 + 3008528 commit e9e0da5

File tree

4 files changed

+110
-4
lines changed

4 files changed

+110
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
* `PyObject` and `Py<T>` reference counts are now decremented sooner after `drop()`. [#851](https://github.com/PyO3/pyo3/pull/851)
1313
* When the GIL is held, the refcount is now decreased immediately on drop. (Previously would wait until just before releasing the GIL.)
1414
* When the GIL is not held, the refcount is now decreased when the GIL is next acquired. (Previously would wait until next time the GIL was released.)
15+
* `FromPyObject` for `Py<T>` now works for a wider range of `T`, in particular for `T: PyClass`. [#880](https://github.com/PyO3/pyo3/pull/880)
1516

1617
### Added
1718
* `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849)
@@ -21,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2122
* `__radd__` and other `__r*__` methods now correctly work with operators. [#839](https://github.com/PyO3/pyo3/pull/839)
2223
* Garbage Collector causing random panics when traversing objects that were mutably borrowed. [#855](https://github.com/PyO3/pyo3/pull/855)
2324
* `&'static Py~` being allowed as arguments. [#869](https://github.com/PyO3/pyo3/pull/869)
25+
* `#[pyo3(get)]` for `Py<T>`. [#880](https://github.com/PyO3/pyo3/pull/880)
26+
2427

2528
## [0.9.2]
2629

src/derive_utils.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::instance::PyNativeType;
1010
use crate::pyclass::PyClass;
1111
use crate::pyclass_init::PyClassInitializer;
1212
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
13-
use crate::{ffi, GILPool, IntoPy, PyCell, PyObject, Python};
13+
use crate::{ffi, GILPool, IntoPy, Py, PyCell, PyObject, Python};
1414
use std::cell::UnsafeCell;
1515

1616
/// Description of a python parameter; used for `parse_args()`.
@@ -215,6 +215,12 @@ impl GetPropertyValue for PyObject {
215215
}
216216
}
217217

218+
impl<T> GetPropertyValue for Py<T> {
219+
fn get_property_value(&self, py: Python) -> PyObject {
220+
self.clone_ref(py).into()
221+
}
222+
}
223+
218224
/// Utilities for basetype
219225
#[doc(hidden)]
220226
pub trait PyBaseTypeUtils {

src/instance.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,14 @@ where
295295

296296
impl<'a, T> FromPyObject<'a> for Py<T>
297297
where
298-
T: AsPyPointer,
299-
&'a T: 'a + FromPyObject<'a>,
298+
T: PyTypeInfo,
299+
&'a T::AsRefTarget: FromPyObject<'a>,
300+
T::AsRefTarget: 'a + AsPyPointer,
300301
{
301302
/// Extracts `Self` from the source `PyObject`.
302303
fn extract(ob: &'a PyAny) -> PyResult<Self> {
303304
unsafe {
304-
ob.extract::<&T>()
305+
ob.extract::<&T::AsRefTarget>()
305306
.map(|val| Py::from_borrowed_ptr(val.as_ptr()))
306307
}
307308
}

tests/test_class_conversion.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
use pyo3::prelude::*;
2+
use pyo3::{ObjectProtocol, ToPyObject};
3+
4+
#[macro_use]
5+
mod common;
26

37
#[pyclass]
48
#[derive(Clone, Debug, PartialEq)]
@@ -25,3 +29,95 @@ fn test_cloneable_pyclass() {
2529
let mrc: PyRefMut<Cloneable> = py_c.extract(py).unwrap();
2630
assert_eq!(&c, &*mrc);
2731
}
32+
33+
#[pyclass]
34+
struct BaseClass {}
35+
36+
#[pymethods]
37+
impl BaseClass {
38+
fn foo(&self) -> &'static str {
39+
"BaseClass"
40+
}
41+
}
42+
43+
#[pyclass(extends=BaseClass)]
44+
struct SubClass {}
45+
46+
#[pymethods]
47+
impl SubClass {
48+
fn foo(&self) -> &'static str {
49+
"SubClass"
50+
}
51+
}
52+
53+
#[pyclass]
54+
struct PolymorphicContainer {
55+
#[pyo3(get, set)]
56+
inner: Py<BaseClass>,
57+
}
58+
59+
#[test]
60+
fn test_polymorphic_container_stores_base_class() {
61+
let gil = Python::acquire_gil();
62+
let py = gil.python();
63+
64+
let p = PyCell::new(
65+
py,
66+
PolymorphicContainer {
67+
inner: Py::new(py, BaseClass {}).unwrap(),
68+
},
69+
)
70+
.unwrap()
71+
.to_object(py);
72+
73+
py_assert!(py, p, "p.inner.foo() == 'BaseClass'");
74+
}
75+
76+
#[test]
77+
fn test_polymorphic_container_stores_sub_class() {
78+
let gil = Python::acquire_gil();
79+
let py = gil.python();
80+
81+
let p = PyCell::new(
82+
py,
83+
PolymorphicContainer {
84+
inner: Py::new(py, BaseClass {}).unwrap(),
85+
},
86+
)
87+
.unwrap()
88+
.to_object(py);
89+
90+
p.as_ref(py)
91+
.setattr(
92+
"inner",
93+
PyCell::new(
94+
py,
95+
PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {}),
96+
)
97+
.unwrap(),
98+
)
99+
.unwrap();
100+
101+
py_assert!(py, p, "p.inner.foo() == 'SubClass'");
102+
}
103+
104+
#[test]
105+
fn test_polymorphic_container_does_not_accept_other_types() {
106+
let gil = Python::acquire_gil();
107+
let py = gil.python();
108+
109+
let p = PyCell::new(
110+
py,
111+
PolymorphicContainer {
112+
inner: Py::new(py, BaseClass {}).unwrap(),
113+
},
114+
)
115+
.unwrap()
116+
.to_object(py);
117+
118+
let setattr = |value: PyObject| p.as_ref(py).setattr("inner", value);
119+
120+
assert!(setattr(1i32.into_py(py)).is_err());
121+
assert!(setattr(py.None()).is_err());
122+
assert!(setattr((1i32, 2i32).into_py(py)).is_err());
123+
}

0 commit comments

Comments
 (0)