Skip to content

Commit

Permalink
feat: add #[pyo3(get, set)] for Cell, Arc and Box
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineRR committed Mar 5, 2023
1 parent 713c02a commit 4d1f156
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
2 changes: 2 additions & 0 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ To use these annotations, your field type must implement some conversion traits:
- For `get` the field type must implement both `IntoPy<PyObject>` and `Clone`.
- For `set` the field type must implement `FromPyObject`.

Implementations of those traits are already provided for `Cell`, `Arc` and `Box` types, if the inner type also implements the trait. This means you can use `#[pyo3(get, set)]` on fields wrapped in one of those three struct.

### Object properties using `#[getter]` and `#[setter]`

For cases which don't satisfy the `#[pyo3(get, set)]` trait requirements, or need side effects, descriptor methods can be defined in a `#[pymethods]` `impl` block.
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3014.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an `IntoPy<PyObject>`, `ToPyObject` and `FromPyObject` impl for `Cell`, `Arc`, and `Box`.
56 changes: 56 additions & 0 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::types::PyTuple;
use crate::{
ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
use std::cell::Cell;
use std::ptr::NonNull;
use std::sync::Arc;

/// Returns a borrowed pointer to a Python object.
///
Expand Down Expand Up @@ -370,6 +372,60 @@ where
}
}

impl<T: ToPyObject> ToPyObject for Box<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
self.as_ref().to_object(py)
}
}

impl<T: Clone + IntoPy<PyObject>> IntoPy<PyObject> for Box<T> {
fn into_py(self, py: Python<'_>) -> PyObject {
self.as_ref().clone().into_py(py)
}
}

// impl<'a, T: FromPyObject<'a>> FromPyObject<'a> for Box<T> {
// fn extract(ob: &'a PyAny) -> PyResult<Self> {
// T::extract(ob).map(Box::new)
// }
// }

impl<T: Copy + ToPyObject> ToPyObject for Cell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
self.get().to_object(py)
}
}

impl<T: Copy + IntoPy<PyObject>> IntoPy<PyObject> for Cell<T> {
fn into_py(self, py: Python<'_>) -> PyObject {
self.get().into_py(py)
}
}

impl<'a, T: FromPyObject<'a>> FromPyObject<'a> for Cell<T> {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
T::extract(ob).map(Cell::new)
}
}

impl<T: ToPyObject> ToPyObject for Arc<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
self.as_ref().to_object(py)
}
}

impl<T: Clone + IntoPy<PyObject>> IntoPy<PyObject> for Arc<T> {
fn into_py(self, py: Python<'_>) -> PyObject {
self.as_ref().clone().into_py(py)
}
}

impl<'a, T: FromPyObject<'a>> FromPyObject<'a> for Arc<T> {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
T::extract(ob).map(Arc::new)
}
}

impl<'a, T> FromPyObject<'a> for &'a PyCell<T>
where
T: PyClass,
Expand Down
39 changes: 39 additions & 0 deletions tests/test_getter_setter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#![cfg(feature = "macros")]

use std::cell::Cell;
use std::sync::Arc;

use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::types::{IntoPyDict, PyList};
Expand Down Expand Up @@ -189,3 +192,39 @@ fn get_all_and_set() {
py_run!(py, inst, "inst.num = 20; assert inst.num == 20");
});
}

#[pyclass]
struct WrapperGetterSetter {
#[pyo3(get, set)]
cell_inner: Cell<i32>,
#[pyo3(get, set)]
arc_inner: Arc<i32>,
#[pyo3(get)]
box_inner: Box<i32>,
}

#[test]
fn wrapper_getter() {
Python::with_gil(|py| {
let inst = Py::new(
py,
WrapperGetterSetter {
cell_inner: Cell::new(10),
arc_inner: Arc::new(20),
box_inner: Box::new(30),
},
)
.unwrap();

py_run!(py, inst, "assert inst.cell_inner == 10");
py_run!(
py,
inst,
"inst.cell_inner = 20; assert inst.cell_inner == 20"
);
py_run!(py, inst, "assert inst.arc_inner == 20");
py_run!(py, inst, "inst.arc_inner = 30; assert inst.arc_inner == 30");
py_run!(py, inst, "assert inst.box_inner == 30");
// py_run!(py, inst, "inst.box_inner = 40; assert inst.box_inner == 40");
});
}

0 comments on commit 4d1f156

Please sign in to comment.