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

Add PyClass borrow methods to Py #976

Merged
merged 1 commit into from
Jun 18, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Add FFI definition `PyObject_AsFileDescriptor` [#938](https://github.com/PyO3/pyo3/pull/938)
- Add `PyByteArray::data`, `PyByteArray::as_bytes`, and `PyByteArray::as_bytes_mut`. [#967](https://github.com/PyO3/pyo3/pull/967)
- Add `Py::borrow`, `Py::borrow_mut`, `Py::try_borrow`, and `Py::try_borrow_mut` for accessing `#[pyclass]` values. [#976](https://github.com/PyO3/pyo3/pull/976)

### Changed
- Simplify internals of `#[pyo3(get)]` attribute. (Remove the hidden API `GetPropertyValue`.) [#934](https://github.com/PyO3/pyo3/pull/934)
Expand Down
67 changes: 59 additions & 8 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
use crate::err::{PyErr, PyResult};
use crate::gil;
use crate::object::PyObject;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::type_object::PyBorrowFlagLayout;
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyCell, PyClass,
PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
};
use std::marker::PhantomData;
use std::mem;
Expand Down Expand Up @@ -49,14 +50,13 @@ unsafe impl<T> Send for Py<T> {}

unsafe impl<T> Sync for Py<T> {}

impl<T> Py<T> {
/// Create a new instance `Py<T>`.
///
/// You can crate [`PyCell::new`](../pycell/struct.PyCell.html#method.new) and `Py::from`,
/// but this method can be more efficient.
impl<T> Py<T>
where
T: PyClass,
{
/// Create a new instance `Py<T>` of a `#[pyclass]` on the Python heap.
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>>
where
T: PyClass,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let initializer = value.into();
Expand All @@ -65,6 +65,57 @@ impl<T> Py<T> {
Ok(ob)
}

/// Immutably borrows the value `T`. This borrow lasts untill the returned `PyRef` exists.
davidhewitt marked this conversation as resolved.
Show resolved Hide resolved
///
/// Equivalent to `self.as_ref(py).borrow()` -
/// see [`PyCell::borrow`](../pycell/struct.PyCell.html#method.borrow)
///
/// # Panics
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow`](#method.try_borrow).
pub fn borrow<'py>(&'py self, py: Python<'py>) -> PyRef<'py, T> {
self.as_ref(py).borrow()
}

/// Mutably borrows the value `T`. This borrow lasts untill the returned `PyRefMut` exists.
///
/// Equivalent to `self.as_ref(py).borrow_mut()` -
/// see [`PyCell::borrow_mut`](../pycell/struct.PyCell.html#method.borrow_mut)
///
/// # Panics
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> {
self.as_ref(py).borrow_mut()
}

/// Immutably borrows the value `T`, returning an error if the value is currently
/// mutably borrowed. This borrow lasts untill the returned `PyRef` exists.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
///
/// Equivalent to `self.as_ref(py).try_borrow()` -
/// see [`PyCell::try_borrow`](../pycell/struct.PyCell.html#method.try_borrow)
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.as_ref(py).try_borrow()
}

/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
/// This borrow lasts untill the returned `PyRefMut` exists.
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
///
/// Equivalent to `self.as_ref(py).try_borrow_mut() -
/// see [`PyCell::try_borrow_mut`](../pycell/struct.PyCell.html#method.try_borrow_mut)
pub fn try_borrow_mut<'py>(
&'py self,
py: Python<'py>,
) -> Result<PyRefMut<'py, T>, PyBorrowMutError> {
self.as_ref(py).try_borrow_mut()
}
}

impl<T> Py<T> {
/// Creates a `Py<T>` instance for the given FFI pointer.
///
/// This moves ownership over the pointer into the `Py<T>`.
Expand Down
5 changes: 4 additions & 1 deletion src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,11 @@ pub struct PyCell<T: PyClass> {
unsafe impl<T: PyClass> PyNativeType for PyCell<T> {}

impl<T: PyClass> PyCell<T> {
/// Make new `PyCell` on the Python heap and returns the reference of it.
/// Make a new `PyCell` on the Python heap and return the reference to it.
///
/// In cases where the value in the cell does not need to be accessed immediately after
/// creation, consider [`Py::new`](../instance/struct.Py.html#method.new) as a more efficient
/// alternative.
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self>
where
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
Expand Down
6 changes: 5 additions & 1 deletion src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) {
/// The `#[pyclass]` attribute automatically implements this trait for your Rust struct,
/// so you don't have to use this trait directly.
pub trait PyClass:
PyTypeInfo<Layout = PyCell<Self>> + Sized + PyClassAlloc + PyMethods + Send
PyTypeInfo<Layout = PyCell<Self>, AsRefTarget = PyCell<Self>>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively I can add AsRefTarget = PyCell<Self> bound just to Py<T> implementation, but I think it always applies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

+ Sized
+ PyClassAlloc
+ PyMethods
+ Send
{
/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;
Expand Down