Skip to content

Commit 565e36d

Browse files
authored
Merge pull request #1098 from davidhewitt/py-as-into-ref
Py::as_ref and Py::into_ref (remove AsPyRef)
2 parents b17d4ff + 6b3c6fd commit 565e36d

20 files changed

+127
-79
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111
- Implement `Debug` for `PyIterator`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
1212
- Implement type information for conversion failures. [#1050](https://github.com/PyO3/pyo3/pull/1050)
1313
- 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)
14+
- Add `Py::as_ref` and `Py::into_ref`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
1415

1516
### Changed
1617
- 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)
@@ -31,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
3132
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
3233
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
3334
- Remove `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
35+
- Remove `AsPyRef`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
3436

3537
### Fixed
3638
- Conversion from types with an `__index__` method to Rust BigInts. [#1027](https://github.com/PyO3/pyo3/pull/1027)

guide/src/class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn return_myclass() -> Py<MyClass> {
122122
}
123123
let gil = Python::acquire_gil();
124124
let obj = return_myclass();
125-
let cell = obj.as_ref(gil.python()); // AsPyRef::as_ref returns &PyCell
125+
let cell = obj.as_ref(gil.python()); // Py<MyClass>::as_ref returns &PyCell<MyClass>
126126
let obj_ref = cell.borrow(); // Get PyRef<T>
127127
assert_eq!(obj_ref.num, 1);
128128
```

guide/src/conversions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ There are also a few special types related to the GIL and Rust-defined `#[pyclas
4646
| What | Description |
4747
| ------------- | ------------------------------------- |
4848
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
49-
| `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. |
49+
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. |
5050
| `PyObject` | An alias for `Py<PyAny>` |
5151
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
5252
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |

guide/src/migration.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ Now, the canonical implementation is always `IntoPy`, so downstream crates may n
1414
accordingly.
1515

1616
Before:
17-
1817
```rust,ignore
1918
# use pyo3::prelude::*;
2019
struct MyPyObjectWrapper(PyObject);
@@ -27,7 +26,6 @@ impl FromPy<MyPyObjectWrapper> for PyObject {
2726
```
2827

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

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

62+
### `AsPyRef` has been removed
63+
As `PyObject` has been changed to be just a type alias, the only remaining implementor of `AsPyRef`
64+
was `Py<T>`. This removed the need for a trait, so the `AsPyRef::as_ref` method has been moved to
65+
`Py::as_ref`.
66+
67+
This should require no code changes except removing `use pyo3::AsPyRef` for code which did not use
68+
`pyo3::prelude::*`.
69+
70+
Before:
71+
```rust,ignore
72+
use pyo3::{AsPyRef, Py, types::PyList};
73+
# pyo3::Python::with_gil(|py| {
74+
let list_py: Py<PyList> = PyList::empty(py).into();
75+
let list_ref: &PyList = list_py.as_ref(py);
76+
# })
77+
```
78+
79+
After:
80+
```rust
81+
use pyo3::{Py, types::PyList};
82+
# pyo3::Python::with_gil(|py| {
83+
let list_py: Py<PyList> = PyList::empty(py).into();
84+
let list_ref: &PyList = list_py.as_ref(py);
85+
# })
86+
```
87+
88+
6589
## from 0.10.* to 0.11
6690

6791
### Stable Rust

guide/src/types.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,18 @@ Can be cloned using Python reference counts with `.clone()`.
140140
# let py = gil.python();
141141
let list: Py<PyList> = PyList::empty(py).into();
142142

143-
// Access the native type using AsPyRef::as_ref(py)
144-
// (For #[pyclass] types, as_ref() will return &PyCell<T>)
143+
// Access to the native type using Py::as_ref(py) or Py::into_ref(py)
144+
// (For #[pyclass] types T, these will return &PyCell<T>)
145+
146+
// Py::as_ref() borrows the object
145147
let _: &PyList = list.as_ref(py);
146148

149+
# let list_clone = list.clone(); // Just so that the .into() example for PyObject compiles.
150+
// Py::into_ref() moves the reference into pyo3's "object storage"; useful for making APIs
151+
// which return gil-bound references.
152+
let _: &PyList = list.into_ref(py);
153+
154+
# let list = list_clone;
147155
// Convert to PyObject with .into()
148156
let _: PyObject = list.into();
149157
```

src/instance.rs

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,21 @@ pub unsafe trait PyNativeType: Sized {
3333
}
3434
}
3535

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

@@ -63,7 +68,74 @@ where
6368
let ob = unsafe { Py::from_owned_ptr(py, obj as _) };
6469
Ok(ob)
6570
}
71+
}
72+
73+
impl<T> Py<T>
74+
where
75+
T: PyTypeInfo,
76+
{
77+
/// Borrows a GIL-bound reference to the contained `T`. By binding to the GIL lifetime, this
78+
/// allows the GIL-bound reference to not require `Python` for any of its methods.
79+
///
80+
/// For native types, this reference is `&T`. For pyclasses, this is `&PyCell<T>`.
81+
///
82+
/// # Examples
83+
/// Get access to `&PyList` from `Py<PyList>`:
84+
///
85+
/// ```
86+
/// # use pyo3::prelude::*;
87+
/// # use pyo3::types::PyList;
88+
/// # Python::with_gil(|py| {
89+
/// let list: Py<PyList> = PyList::empty(py).into();
90+
/// let list: &PyList = list.as_ref(py);
91+
/// assert_eq!(list.len(), 0);
92+
/// # });
93+
/// ```
94+
///
95+
/// Get access to `&PyCell<MyClass>` from `Py<MyClass>`:
96+
///
97+
/// ```
98+
/// # use pyo3::prelude::*;
99+
/// #[pyclass]
100+
/// struct MyClass { }
101+
/// # Python::with_gil(|py| {
102+
/// let my_class: Py<MyClass> = Py::new(py, MyClass { }).unwrap();
103+
/// let my_class_cell: &PyCell<MyClass> = my_class.as_ref(py);
104+
/// assert!(my_class_cell.try_borrow().is_ok());
105+
/// # });
106+
/// ```
107+
pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py T::AsRefTarget {
108+
let any = self.as_ptr() as *const PyAny;
109+
unsafe { PyNativeType::unchecked_downcast(&*any) }
110+
}
111+
112+
/// Similar to [`as_ref`](#method.as_ref), and also consumes this `Py` and registers the
113+
/// Python object reference in PyO3's object storage. The reference count for the Python
114+
/// object will not be decreased until the GIL lifetime ends.
115+
///
116+
/// # Example
117+
///
118+
/// Useful when returning GIL-bound references from functions. In the snippet below, note that
119+
/// the `'py` lifetime of the input GIL lifetime is also given to the returned reference:
120+
/// ```
121+
/// # use pyo3::prelude::*;
122+
/// fn new_py_any<'py>(py: Python<'py>, value: impl IntoPy<PyObject>) -> &'py PyAny {
123+
/// let obj: PyObject = value.into_py(py);
124+
///
125+
/// // .as_ref(py) would not be suitable here, because a reference to `obj` may not be
126+
/// // returned from the function.
127+
/// obj.into_ref(py)
128+
/// }
129+
/// ```
130+
pub fn into_ref(self, py: Python) -> &T::AsRefTarget {
131+
unsafe { py.from_owned_ptr(self.into_ptr()) }
132+
}
133+
}
66134

135+
impl<T> Py<T>
136+
where
137+
T: PyClass,
138+
{
67139
/// Immutably borrows the value `T`. This borrow lasts untill the returned `PyRef` exists.
68140
///
69141
/// Equivalent to `self.as_ref(py).borrow()` -
@@ -355,58 +427,6 @@ impl<T> Py<T> {
355427
}
356428
}
357429

358-
/// Retrieves `&'py` types from `Py<T>` or `PyObject`.
359-
///
360-
/// # Examples
361-
/// `PyObject::as_ref` returns `&PyAny`.
362-
/// ```
363-
/// # use pyo3::prelude::*;
364-
/// let obj: PyObject = {
365-
/// let gil = Python::acquire_gil();
366-
/// let py = gil.python();
367-
/// py.eval("[]", None, None).unwrap().to_object(py)
368-
/// };
369-
/// let gil = Python::acquire_gil();
370-
/// let py = gil.python();
371-
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0);
372-
/// ```
373-
///
374-
/// `Py<T>::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell<T>`
375-
/// for `#[pyclass]`.
376-
/// ```
377-
/// # use pyo3::prelude::*;
378-
/// #[pyclass]
379-
/// struct Counter {
380-
/// count: usize,
381-
/// }
382-
/// let counter = {
383-
/// let gil = Python::acquire_gil();
384-
/// let py = gil.python();
385-
/// Py::new(py, Counter { count: 0}).unwrap()
386-
/// };
387-
/// let gil = Python::acquire_gil();
388-
/// let py = gil.python();
389-
/// let counter_cell: &PyCell<Counter> = counter.as_ref(py);
390-
/// let counter_ref = counter_cell.borrow();
391-
/// assert_eq!(counter_ref.count, 0);
392-
/// ```
393-
pub trait AsPyRef: Sized {
394-
type Target;
395-
/// Return reference to object.
396-
fn as_ref<'p>(&'p self, py: Python<'p>) -> &'p Self::Target;
397-
}
398-
399-
impl<T> AsPyRef for Py<T>
400-
where
401-
T: PyTypeInfo,
402-
{
403-
type Target = T::AsRefTarget;
404-
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p Self::Target {
405-
let any = self.as_ptr() as *const PyAny;
406-
unsafe { PyNativeType::unchecked_downcast(&*any) }
407-
}
408-
}
409-
410430
impl<T> ToPyObject for Py<T> {
411431
/// Converts `Py` instance -> PyObject.
412432
fn to_object(&self, py: Python) -> PyObject {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ pub use crate::conversion::{
140140
};
141141
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
142142
pub use crate::gil::{GILGuard, GILPool};
143-
pub use crate::instance::{AsPyRef, Py, PyNativeType, PyObject};
143+
pub use crate::instance::{Py, PyNativeType, PyObject};
144144
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
145145
pub use crate::pyclass::PyClass;
146146
pub use crate::pyclass_init::PyClassInitializer;

src/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
1313
pub use crate::err::{PyErr, PyResult};
1414
pub use crate::gil::GILGuard;
15-
pub use crate::instance::{AsPyRef, Py, PyObject};
15+
pub use crate::instance::{Py, PyObject};
1616
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
1717
pub use crate::pyclass_init::PyClassInitializer;
1818
pub use crate::python::Python;

src/type_object.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub unsafe trait PyTypeInfo: Sized {
9898
/// Initializer for layout
9999
type Initializer: PyObjectInit<Self>;
100100

101-
/// Utility type to make AsPyRef work
101+
/// Utility type to make Py::as_ref work
102102
type AsRefTarget: crate::PyNativeType;
103103

104104
/// PyTypeObject instance for this type.

src/types/dict.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,6 @@ where
357357
#[cfg(test)]
358358
mod test {
359359
use crate::conversion::IntoPy;
360-
use crate::instance::AsPyRef;
361360
use crate::types::dict::IntoPyDict;
362361
#[cfg(not(PyPy))]
363362
use crate::types::PyList;

src/types/iterator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ mod tests {
8585
use super::PyIterator;
8686
use crate::exceptions::PyTypeError;
8787
use crate::gil::GILPool;
88-
use crate::instance::AsPyRef;
8988
use crate::types::{PyDict, PyList};
9089
use crate::Python;
9190
use crate::ToPyObject;

src/types/list.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ where
225225

226226
#[cfg(test)]
227227
mod test {
228-
use crate::instance::AsPyRef;
229228
use crate::types::PyList;
230229
use crate::Python;
231230
use crate::{IntoPy, PyObject, PyTryFrom, ToPyObject};

src/types/num.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,6 @@ mod bigint_conversion {
326326
}
327327
impl<'source> FromPyObject<'source> for $rust_ty {
328328
fn extract(ob: &'source PyAny) -> PyResult<$rust_ty> {
329-
use crate::instance::AsPyRef;
330329
let py = ob.py();
331330
unsafe {
332331
let num = ffi::PyNumber_Index(ob.as_ptr());

src/types/sequence.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,6 @@ impl<'v> PyTryFrom<'v> for PySequence {
396396

397397
#[cfg(test)]
398398
mod test {
399-
use crate::instance::AsPyRef;
400399
use crate::types::PySequence;
401400
use crate::AsPyPointer;
402401
use crate::Python;

src/types/set.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
303303
#[cfg(test)]
304304
mod test {
305305
use super::{PyFrozenSet, PySet};
306-
use crate::{AsPyRef, IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
306+
use crate::{IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
307307
use std::collections::{BTreeSet, HashSet};
308308
use std::iter::FromIterator;
309309

src/types/string.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ impl<'source> FromPyObject<'source> for String {
146146
#[cfg(test)]
147147
mod test {
148148
use super::PyString;
149-
use crate::instance::AsPyRef;
150149
use crate::Python;
151150
use crate::{FromPyObject, PyObject, PyTryFrom, ToPyObject};
152151

src/types/tuple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ tuple_conversion!(
242242
#[cfg(test)]
243243
mod test {
244244
use crate::types::{PyAny, PyTuple};
245-
use crate::{AsPyRef, PyTryFrom, Python, ToPyObject};
245+
use crate::{PyTryFrom, Python, ToPyObject};
246246
use std::collections::HashSet;
247247

248248
#[test]

tests/test_pyself.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Test slf: PyRef/PyMutRef<Self>(especially, slf.into::<Py>) works
22
use pyo3::prelude::*;
33
use pyo3::types::{PyBytes, PyString};
4-
use pyo3::{AsPyRef, PyCell, PyIterProtocol};
4+
use pyo3::{PyCell, PyIterProtocol};
55
use std::collections::HashMap;
66

77
mod common;

tests/test_various.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use pyo3::prelude::*;
22
use pyo3::types::IntoPyDict;
33
use pyo3::types::{PyDict, PyTuple};
4-
use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyCell};
4+
use pyo3::{py_run, wrap_pyfunction, PyCell};
55

66
mod common;
77

tests/ui/wrong_aspyref_lifetimes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use pyo3::{types::PyDict, AsPyRef, Py, PyNativeType, Python};
1+
use pyo3::{types::PyDict, Py, PyNativeType, Python};
22

33
fn main() {
44
let gil = Python::acquire_gil();

0 commit comments

Comments
 (0)