Skip to content

Commit

Permalink
Merge pull request #880 from davidhewitt/py_get_set
Browse files Browse the repository at this point in the history
Allow use of `#[pyo3(get, set)]` with `Py<T>`
  • Loading branch information
kngwyu authored Apr 23, 2020
2 parents dcab478 + 3008528 commit e9e0da5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* `PyObject` and `Py<T>` reference counts are now decremented sooner after `drop()`. [#851](https://github.com/PyO3/pyo3/pull/851)
* When the GIL is held, the refcount is now decreased immediately on drop. (Previously would wait until just before releasing the GIL.)
* 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.)
* `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)

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


## [0.9.2]

Expand Down
8 changes: 7 additions & 1 deletion src/derive_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::instance::PyNativeType;
use crate::pyclass::PyClass;
use crate::pyclass_init::PyClassInitializer;
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
use crate::{ffi, GILPool, IntoPy, PyCell, PyObject, Python};
use crate::{ffi, GILPool, IntoPy, Py, PyCell, PyObject, Python};
use std::cell::UnsafeCell;

/// Description of a python parameter; used for `parse_args()`.
Expand Down Expand Up @@ -215,6 +215,12 @@ impl GetPropertyValue for PyObject {
}
}

impl<T> GetPropertyValue for Py<T> {
fn get_property_value(&self, py: Python) -> PyObject {
self.clone_ref(py).into()
}
}

/// Utilities for basetype
#[doc(hidden)]
pub trait PyBaseTypeUtils {
Expand Down
7 changes: 4 additions & 3 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,14 @@ where

impl<'a, T> FromPyObject<'a> for Py<T>
where
T: AsPyPointer,
&'a T: 'a + FromPyObject<'a>,
T: PyTypeInfo,
&'a T::AsRefTarget: FromPyObject<'a>,
T::AsRefTarget: 'a + AsPyPointer,
{
/// Extracts `Self` from the source `PyObject`.
fn extract(ob: &'a PyAny) -> PyResult<Self> {
unsafe {
ob.extract::<&T>()
ob.extract::<&T::AsRefTarget>()
.map(|val| Py::from_borrowed_ptr(val.as_ptr()))
}
}
Expand Down
96 changes: 96 additions & 0 deletions tests/test_class_conversion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use pyo3::prelude::*;
use pyo3::{ObjectProtocol, ToPyObject};

#[macro_use]
mod common;

#[pyclass]
#[derive(Clone, Debug, PartialEq)]
Expand All @@ -25,3 +29,95 @@ fn test_cloneable_pyclass() {
let mrc: PyRefMut<Cloneable> = py_c.extract(py).unwrap();
assert_eq!(&c, &*mrc);
}

#[pyclass]
struct BaseClass {}

#[pymethods]
impl BaseClass {
fn foo(&self) -> &'static str {
"BaseClass"
}
}

#[pyclass(extends=BaseClass)]
struct SubClass {}

#[pymethods]
impl SubClass {
fn foo(&self) -> &'static str {
"SubClass"
}
}

#[pyclass]
struct PolymorphicContainer {
#[pyo3(get, set)]
inner: Py<BaseClass>,
}

#[test]
fn test_polymorphic_container_stores_base_class() {
let gil = Python::acquire_gil();
let py = gil.python();

let p = PyCell::new(
py,
PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(),
},
)
.unwrap()
.to_object(py);

py_assert!(py, p, "p.inner.foo() == 'BaseClass'");
}

#[test]
fn test_polymorphic_container_stores_sub_class() {
let gil = Python::acquire_gil();
let py = gil.python();

let p = PyCell::new(
py,
PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(),
},
)
.unwrap()
.to_object(py);

p.as_ref(py)
.setattr(
"inner",
PyCell::new(
py,
PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {}),
)
.unwrap(),
)
.unwrap();

py_assert!(py, p, "p.inner.foo() == 'SubClass'");
}

#[test]
fn test_polymorphic_container_does_not_accept_other_types() {
let gil = Python::acquire_gil();
let py = gil.python();

let p = PyCell::new(
py,
PolymorphicContainer {
inner: Py::new(py, BaseClass {}).unwrap(),
},
)
.unwrap()
.to_object(py);

let setattr = |value: PyObject| p.as_ref(py).setattr("inner", value);

assert!(setattr(1i32.into_py(py)).is_err());
assert!(setattr(py.None()).is_err());
assert!(setattr((1i32, 2i32).into_py(py)).is_err());
}

0 comments on commit e9e0da5

Please sign in to comment.