Skip to content

Commit

Permalink
Merge pull request #976 from davidhewitt/prefer-py
Browse files Browse the repository at this point in the history
Add PyClass borrow methods to Py
  • Loading branch information
kngwyu committed Jun 18, 2020
2 parents 72a0dee + 925986c commit 6ea8345
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
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
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.
///
/// 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
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
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>>
+ Sized
+ PyClassAlloc
+ PyMethods
+ Send
{
/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;
Expand Down

0 comments on commit 6ea8345

Please sign in to comment.