Skip to content

Py::as_ref and Py::into_ref (remove AsPyRef) #1098

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

Merged
merged 3 commits into from
Aug 11, 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
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