Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of #[pyo3(get, set)] with Py<T> #880

Merged
merged 1 commit into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}