diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e9833c7e6..e90482636e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/instance.rs b/src/instance.rs index cf94a317f5a..ad2fd59b0b4 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -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; @@ -49,14 +50,13 @@ unsafe impl Send for Py {} unsafe impl Sync for Py {} -impl Py { - /// Create a new instance `Py`. - /// - /// You can crate [`PyCell::new`](../pycell/struct.PyCell.html#method.new) and `Py::from`, - /// but this method can be more efficient. +impl Py +where + T: PyClass, +{ + /// Create a new instance `Py` of a `#[pyclass]` on the Python heap. pub fn new(py: Python, value: impl Into>) -> PyResult> where - T: PyClass, T::BaseLayout: PyBorrowFlagLayout, { let initializer = value.into(); @@ -65,6 +65,57 @@ impl Py { 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, 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, PyBorrowMutError> { + self.as_ref(py).try_borrow_mut() + } +} + +impl Py { /// Creates a `Py` instance for the given FFI pointer. /// /// This moves ownership over the pointer into the `Py`. diff --git a/src/pycell.rs b/src/pycell.rs index e6249824693..15ef1035a72 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -166,8 +166,11 @@ pub struct PyCell { unsafe impl PyNativeType for PyCell {} impl PyCell { - /// 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>) -> PyResult<&Self> where T::BaseLayout: PyBorrowFlagLayout, diff --git a/src/pyclass.rs b/src/pyclass.rs index 9d7bc8def10..95351cf45e4 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -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> + Sized + PyClassAlloc + PyMethods + Send + PyTypeInfo, AsRefTarget = PyCell> + + Sized + + PyClassAlloc + + PyMethods + + Send { /// Specify this class has `#[pyclass(dict)]` or not. type Dict: PyClassDict;