Skip to content

Commit

Permalink
Merge pull request #1098 from davidhewitt/py-as-into-ref
Browse files Browse the repository at this point in the history
Py::as_ref and Py::into_ref (remove AsPyRef)
  • Loading branch information
davidhewitt authored Aug 11, 2020
2 parents b17d4ff + 6b3c6fd commit 565e36d
Show file tree
Hide file tree
Showing 20 changed files with 127 additions and 79 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Implement `Debug` for `PyIterator`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
- Implement type information for conversion failures. [#1050](https://github.com/PyO3/pyo3/pull/1050)
- Add `PyBytes::new_with` and `PyByteArray::new_with` for initialising Python-allocated bytes and bytearrays using a closure. [#1074](https://github.com/PyO3/pyo3/pull/1074)
- Add `Py::as_ref` and `Py::into_ref`. [#1098](https://github.com/PyO3/pyo3/pull/1098)

### Changed
- Exception types have been renamed from e.g. `RuntimeError` to `PyRuntimeError`, and are now only accessible by `&T` or `Py<T>` similar to other Python-native types. The old names continue to exist but are deprecated. [#1024](https://github.com/PyO3/pyo3/pull/1024)
Expand All @@ -31,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
- Remove `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- Remove `AsPyRef`. [#1098](https://github.com/PyO3/pyo3/pull/1098)

### Fixed
- Conversion from types with an `__index__` method to Rust BigInts. [#1027](https://github.com/PyO3/pyo3/pull/1027)
Expand Down
2 changes: 1 addition & 1 deletion guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn return_myclass() -> Py<MyClass> {
}
let gil = Python::acquire_gil();
let obj = return_myclass();
let cell = obj.as_ref(gil.python()); // AsPyRef::as_ref returns &PyCell
let cell = obj.as_ref(gil.python()); // Py<MyClass>::as_ref returns &PyCell<MyClass>
let obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
```
Expand Down
2 changes: 1 addition & 1 deletion guide/src/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ There are also a few special types related to the GIL and Rust-defined `#[pyclas
| What | Description |
| ------------- | ------------------------------------- |
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. To call Python APIs using this object, it must be used with `AsPyRef::as_ref` to get an `&T` reference bound to the GIL. |
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. |
| `PyObject` | An alias for `Py<PyAny>` |
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
Expand Down
30 changes: 27 additions & 3 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Now, the canonical implementation is always `IntoPy`, so downstream crates may n
accordingly.

Before:

```rust,ignore
# use pyo3::prelude::*;
struct MyPyObjectWrapper(PyObject);
Expand All @@ -27,7 +26,6 @@ impl FromPy<MyPyObjectWrapper> for PyObject {
```

After

```rust
# use pyo3::prelude::*;
struct MyPyObjectWrapper(PyObject);
Expand All @@ -42,7 +40,6 @@ impl IntoPy<PyObject> for MyPyObjectWrapper {
Similarly, code which was using the `FromPy` trait can be trivially rewritten to use `IntoPy`.

Before:

```rust,ignore
# use pyo3::prelude::*;
# Python::with_gil(|py| {
Expand All @@ -62,6 +59,33 @@ let obj: PyObject = 1.234.into_py(py);
This should change very little from a usage perspective. If you implemented traits for both
`PyObject` and `Py<T>`, you may find you can just remove the `PyObject` implementation.

### `AsPyRef` has been removed
As `PyObject` has been changed to be just a type alias, the only remaining implementor of `AsPyRef`
was `Py<T>`. This removed the need for a trait, so the `AsPyRef::as_ref` method has been moved to
`Py::as_ref`.

This should require no code changes except removing `use pyo3::AsPyRef` for code which did not use
`pyo3::prelude::*`.

Before:
```rust,ignore
use pyo3::{AsPyRef, Py, types::PyList};
# pyo3::Python::with_gil(|py| {
let list_py: Py<PyList> = PyList::empty(py).into();
let list_ref: &PyList = list_py.as_ref(py);
# })
```

After:
```rust
use pyo3::{Py, types::PyList};
# pyo3::Python::with_gil(|py| {
let list_py: Py<PyList> = PyList::empty(py).into();
let list_ref: &PyList = list_py.as_ref(py);
# })
```


## from 0.10.* to 0.11

### Stable Rust
Expand Down
12 changes: 10 additions & 2 deletions guide/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,18 @@ Can be cloned using Python reference counts with `.clone()`.
# let py = gil.python();
let list: Py<PyList> = PyList::empty(py).into();

// Access the native type using AsPyRef::as_ref(py)
// (For #[pyclass] types, as_ref() will return &PyCell<T>)
// Access to the native type using Py::as_ref(py) or Py::into_ref(py)
// (For #[pyclass] types T, these will return &PyCell<T>)

// Py::as_ref() borrows the object
let _: &PyList = list.as_ref(py);

# let list_clone = list.clone(); // Just so that the .into() example for PyObject compiles.
// Py::into_ref() moves the reference into pyo3's "object storage"; useful for making APIs
// which return gil-bound references.
let _: &PyList = list.into_ref(py);

# let list = list_clone;
// Convert to PyObject with .into()
let _: PyObject = list.into();
```
Expand Down
136 changes: 78 additions & 58 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,21 @@ pub unsafe trait PyNativeType: Sized {
}
}

/// A Python object of known type.
/// A Python object of known type T.
///
/// Accessing this object is thread-safe, since any access to its API requires a
/// `Python<'py>` GIL token.
/// Accessing this object is thread-safe, since any access to its API requires a `Python<'py>` GIL
/// token. There are a few different ways to use the Python object contained:
/// - [`Py::as_ref`](#method.as_ref) to borrow a GIL-bound reference to the contained object.
/// - [`Py::borrow`](#method.borrow), [`Py::try_borrow`](#method.try_borrow),
/// [`Py::borrow_mut`](#method.borrow_mut), or [`Py::try_borrow_mut`](#method.try_borrow_mut),
/// to directly access a `#[pyclass]` value (which has RefCell-like behavior, see
/// [the `PyCell` guide entry](https://pyo3.rs/master/class.html#pycell-and-interior-mutability)
/// ).
/// - Use methods directly on `Py`, such as [`Py::call`](#method.call) and
/// [`Py::call_method`](#method.call_method).
///
/// See [the guide](https://pyo3.rs/master/types.html) for an explanation
/// of the different Python object types.
///
/// Technically, it is a safe wrapper around `NonNull<ffi::PyObject>` with
/// specified type information.
#[repr(transparent)]
pub struct Py<T>(NonNull<ffi::PyObject>, PhantomData<T>);

Expand All @@ -63,7 +68,74 @@ where
let ob = unsafe { Py::from_owned_ptr(py, obj as _) };
Ok(ob)
}
}

impl<T> Py<T>
where
T: PyTypeInfo,
{
/// Borrows a GIL-bound reference to the contained `T`. By binding to the GIL lifetime, this
/// allows the GIL-bound reference to not require `Python` for any of its methods.
///
/// For native types, this reference is `&T`. For pyclasses, this is `&PyCell<T>`.
///
/// # Examples
/// Get access to `&PyList` from `Py<PyList>`:
///
/// ```
/// # use pyo3::prelude::*;
/// # use pyo3::types::PyList;
/// # Python::with_gil(|py| {
/// let list: Py<PyList> = PyList::empty(py).into();
/// let list: &PyList = list.as_ref(py);
/// assert_eq!(list.len(), 0);
/// # });
/// ```
///
/// Get access to `&PyCell<MyClass>` from `Py<MyClass>`:
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct MyClass { }
/// # Python::with_gil(|py| {
/// let my_class: Py<MyClass> = Py::new(py, MyClass { }).unwrap();
/// let my_class_cell: &PyCell<MyClass> = my_class.as_ref(py);
/// assert!(my_class_cell.try_borrow().is_ok());
/// # });
/// ```
pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py T::AsRefTarget {
let any = self.as_ptr() as *const PyAny;
unsafe { PyNativeType::unchecked_downcast(&*any) }
}

/// Similar to [`as_ref`](#method.as_ref), and also consumes this `Py` and registers the
/// Python object reference in PyO3's object storage. The reference count for the Python
/// object will not be decreased until the GIL lifetime ends.
///
/// # Example
///
/// Useful when returning GIL-bound references from functions. In the snippet below, note that
/// the `'py` lifetime of the input GIL lifetime is also given to the returned reference:
/// ```
/// # use pyo3::prelude::*;
/// fn new_py_any<'py>(py: Python<'py>, value: impl IntoPy<PyObject>) -> &'py PyAny {
/// let obj: PyObject = value.into_py(py);
///
/// // .as_ref(py) would not be suitable here, because a reference to `obj` may not be
/// // returned from the function.
/// obj.into_ref(py)
/// }
/// ```
pub fn into_ref(self, py: Python) -> &T::AsRefTarget {
unsafe { py.from_owned_ptr(self.into_ptr()) }
}
}

impl<T> Py<T>
where
T: PyClass,
{
/// Immutably borrows the value `T`. This borrow lasts untill the returned `PyRef` exists.
///
/// Equivalent to `self.as_ref(py).borrow()` -
Expand Down Expand Up @@ -355,58 +427,6 @@ impl<T> Py<T> {
}
}

/// Retrieves `&'py` types from `Py<T>` or `PyObject`.
///
/// # Examples
/// `PyObject::as_ref` returns `&PyAny`.
/// ```
/// # use pyo3::prelude::*;
/// let obj: PyObject = {
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// py.eval("[]", None, None).unwrap().to_object(py)
/// };
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0);
/// ```
///
/// `Py<T>::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell<T>`
/// for `#[pyclass]`.
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Counter {
/// count: usize,
/// }
/// let counter = {
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// Py::new(py, Counter { count: 0}).unwrap()
/// };
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let counter_cell: &PyCell<Counter> = counter.as_ref(py);
/// let counter_ref = counter_cell.borrow();
/// assert_eq!(counter_ref.count, 0);
/// ```
pub trait AsPyRef: Sized {
type Target;
/// Return reference to object.
fn as_ref<'p>(&'p self, py: Python<'p>) -> &'p Self::Target;
}

impl<T> AsPyRef for Py<T>
where
T: PyTypeInfo,
{
type Target = T::AsRefTarget;
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p Self::Target {
let any = self.as_ptr() as *const PyAny;
unsafe { PyNativeType::unchecked_downcast(&*any) }
}
}

impl<T> ToPyObject for Py<T> {
/// Converts `Py` instance -> PyObject.
fn to_object(&self, py: Python) -> PyObject {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub use crate::conversion::{
};
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
pub use crate::gil::{GILGuard, GILPool};
pub use crate::instance::{AsPyRef, Py, PyNativeType, PyObject};
pub use crate::instance::{Py, PyNativeType, PyObject};
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass::PyClass;
pub use crate::pyclass_init::PyClassInitializer;
Expand Down
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
pub use crate::err::{PyErr, PyResult};
pub use crate::gil::GILGuard;
pub use crate::instance::{AsPyRef, Py, PyObject};
pub use crate::instance::{Py, PyObject};
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::python::Python;
Expand Down
2 changes: 1 addition & 1 deletion src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub unsafe trait PyTypeInfo: Sized {
/// Initializer for layout
type Initializer: PyObjectInit<Self>;

/// Utility type to make AsPyRef work
/// Utility type to make Py::as_ref work
type AsRefTarget: crate::PyNativeType;

/// PyTypeObject instance for this type.
Expand Down
1 change: 0 additions & 1 deletion src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,6 @@ where
#[cfg(test)]
mod test {
use crate::conversion::IntoPy;
use crate::instance::AsPyRef;
use crate::types::dict::IntoPyDict;
#[cfg(not(PyPy))]
use crate::types::PyList;
Expand Down
1 change: 0 additions & 1 deletion src/types/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ mod tests {
use super::PyIterator;
use crate::exceptions::PyTypeError;
use crate::gil::GILPool;
use crate::instance::AsPyRef;
use crate::types::{PyDict, PyList};
use crate::Python;
use crate::ToPyObject;
Expand Down
1 change: 0 additions & 1 deletion src/types/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ where

#[cfg(test)]
mod test {
use crate::instance::AsPyRef;
use crate::types::PyList;
use crate::Python;
use crate::{IntoPy, PyObject, PyTryFrom, ToPyObject};
Expand Down
1 change: 0 additions & 1 deletion src/types/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ mod bigint_conversion {
}
impl<'source> FromPyObject<'source> for $rust_ty {
fn extract(ob: &'source PyAny) -> PyResult<$rust_ty> {
use crate::instance::AsPyRef;
let py = ob.py();
unsafe {
let num = ffi::PyNumber_Index(ob.as_ptr());
Expand Down
1 change: 0 additions & 1 deletion src/types/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ impl<'v> PyTryFrom<'v> for PySequence {

#[cfg(test)]
mod test {
use crate::instance::AsPyRef;
use crate::types::PySequence;
use crate::AsPyPointer;
use crate::Python;
Expand Down
2 changes: 1 addition & 1 deletion src/types/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
#[cfg(test)]
mod test {
use super::{PyFrozenSet, PySet};
use crate::{AsPyRef, IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
use crate::{IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
use std::collections::{BTreeSet, HashSet};
use std::iter::FromIterator;

Expand Down
1 change: 0 additions & 1 deletion src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ impl<'source> FromPyObject<'source> for String {
#[cfg(test)]
mod test {
use super::PyString;
use crate::instance::AsPyRef;
use crate::Python;
use crate::{FromPyObject, PyObject, PyTryFrom, ToPyObject};

Expand Down
2 changes: 1 addition & 1 deletion src/types/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ tuple_conversion!(
#[cfg(test)]
mod test {
use crate::types::{PyAny, PyTuple};
use crate::{AsPyRef, PyTryFrom, Python, ToPyObject};
use crate::{PyTryFrom, Python, ToPyObject};
use std::collections::HashSet;

#[test]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pyself.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Test slf: PyRef/PyMutRef<Self>(especially, slf.into::<Py>) works
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyString};
use pyo3::{AsPyRef, PyCell, PyIterProtocol};
use pyo3::{PyCell, PyIterProtocol};
use std::collections::HashMap;

mod common;
Expand Down
2 changes: 1 addition & 1 deletion tests/test_various.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::types::{PyDict, PyTuple};
use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyCell};
use pyo3::{py_run, wrap_pyfunction, PyCell};

mod common;

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/wrong_aspyref_lifetimes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pyo3::{types::PyDict, AsPyRef, Py, PyNativeType, Python};
use pyo3::{types::PyDict, Py, PyNativeType, Python};

fn main() {
let gil = Python::acquire_gil();
Expand Down

0 comments on commit 565e36d

Please sign in to comment.