Skip to content

Commit

Permalink
Merge pull request #1063 from davidhewitt/remove-pyobject
Browse files Browse the repository at this point in the history
Make `PyObject` a type alias of `Py<PyAny>` (& remove `FromPy`)
  • Loading branch information
davidhewitt authored Aug 9, 2020
2 parents 2911fb4 + be239d4 commit bcb9077
Show file tree
Hide file tree
Showing 29 changed files with 472 additions and 627 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Rename `PYTHON_SYS_EXECUTABLE` to `PYO3_PYTHON`. The old name will continue to work but will be undocumented, and will be removed in a future release. [#1039](https://github.com/PyO3/pyo3/pull/1039)
- `PyType::as_type_ptr` is no longer `unsafe`. [#1047](https://github.com/PyO3/pyo3/pull/1047)
- Change `PyIterator::from_object` to return `PyResult<PyIterator>` instead of `Result<PyIterator, PyDowncastError>`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
- `IntoPy` is no longer implied by `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- `PyObject` is now just a type alias for `Py<PyAny>`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- Implement `Send + Sync` for `PyErr`. `PyErr::new`, `PyErr::from_type`, `PyException::py_err` and `PyException::into` have had these bounds added to their arguments. [#1067](https://github.com/PyO3/pyo3/pull/1067)
- Change `#[pyproto]` to return NotImplemented for operators for which Python can try a reversed operation. [1072](https://github.com/PyO3/pyo3/pull/1072)

### Removed
- Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- 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)

### Fixed
- Conversion from types with an `__index__` method to Rust BigInts. [#1027](https://github.com/PyO3/pyo3/pull/1027)
Expand Down
46 changes: 28 additions & 18 deletions guide/src/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ The table below contains the Python type and the corresponding function argument
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:

| What | Description |
| ------------- | ------------------------------- |
| ------------- | ------------------------------------- |
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
| `PyObject` | 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 a `&PyAny` reference. |
| `Py<T>` | Same as above, for a specific Python type or `#[pyclass]` T. |
| `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. |
| `PyObject` | An alias for `Py<PyAny>` |
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |

For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](class.md).

Expand Down Expand Up @@ -119,6 +119,28 @@ mutable references, you have to extract the PyO3 reference wrappers [`PyRef`]
and [`PyRefMut`]. They work like the reference wrappers of
`std::cell::RefCell` and ensure (at runtime) that Rust borrows are allowed.

### `IntoPy<T>`

This trait defines the to-python conversion for a Rust type. It is usually implemented as
`IntoPy<PyObject>`, which is the trait needed for returning a value from `#[pyfunction]` and
`#[pymethods]`.

All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`.

Occasionally you may choose to implement this for custom types which are mapped to Python types
_without_ having a unique python type.

```
use pyo3::prelude::*;
struct MyPyObjectWrapper(PyObject);
impl IntoPy<PyObject> for MyPyObjectWrapper {
fn into_py(self, py: Python) -> PyObject {
self.0
}
}
```

### The `ToPyObject` trait

Expand Down Expand Up @@ -216,22 +238,10 @@ fn main() {
}
```

### `FromPy<T>` and `IntoPy<T>`

Many conversions in PyO3 can't use `std::convert::From` because they need a GIL token.
The [`FromPy`] trait offers an `from_py` method that works just like `from`, except for taking a `Python<'_>` argument.
I.e. `FromPy<T>` could be converting a Rust object into a Python object even though it is called [`FromPy`] - it doesn't say anything about which side of the conversion is a Python object.

Just like `From<T>`, if you implement `FromPy<T>` you gain a blanket implementation of [`IntoPy`] for free.

Eventually, traits such as [`ToPyObject`] will be replaced by this trait and a [`FromPy`] trait will be added that will implement
[`IntoPy`], just like with `From` and `Into`.

[`IntoPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPy.html
[`FromPy`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPy.html
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPyObject.html
[`ToPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.ToPyObject.html
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
[`PyTuple`]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyTuple.html
[`PyAny`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
[`IntoPyDict`]: https://docs.rs/pyo3/latest/pyo3/types/trait.IntoPyDict.html
Expand Down
9 changes: 3 additions & 6 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,12 @@ mod io {
pyo3::import_exception!(io, UnsupportedOperation);
}

fn tell(file: PyObject) -> PyResult<u64> {
fn tell(file: &PyAny) -> PyResult<u64> {
use pyo3::exceptions::*;

let gil = Python::acquire_gil();
let py = gil.python();

match file.call_method0(py, "tell") {
match file.call_method0("tell") {
Err(_) => Err(io::UnsupportedOperation::py_err("not supported: tell")),
Ok(x) => x.extract::<u64>(py),
Ok(x) => x.extract::<u64>(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion guide/src/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,5 @@ If you have a static function, you can expose it with `#[pyfunction]` and use [`
[`PyAny::call`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call
[`PyAny::call0`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call0
[`PyAny::call1`]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html#tymethod.call1
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html
[`PyObject`]: https://docs.rs/pyo3/latest/pyo3/type.PyObject.html
[`wrap_pyfunction!`]: https://docs.rs/pyo3/latest/pyo3/macro.wrap_pyfunction.html
59 changes: 59 additions & 0 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,65 @@
This guide can help you upgrade code through breaking changes from one PyO3 version to the next.
For a detailed list of all changes, see [CHANGELOG.md](https://github.com/PyO3/pyo3/blob/master/CHANGELOG.md)

## from 0.11.* to 0.12

### `FromPy` has been removed
To simplify the PyO3 public conversion trait hierarchy, the `FromPy` has been removed. In PyO3
`0.11` there were two ways to define the to-Python conversion for a type: `FromPy<T> for PyObject`,
and `IntoPy<PyObject> for T`.

Now, the canonical implementation is always `IntoPy`, so downstream crates may need to adjust
accordingly.

Before:

```rust,ignore
# use pyo3::prelude::*;
struct MyPyObjectWrapper(PyObject);
impl FromPy<MyPyObjectWrapper> for PyObject {
fn from_py(other: MyPyObjectWrapper, _py: Python) -> Self {
other.0
}
}
```

After

```rust
# use pyo3::prelude::*;
struct MyPyObjectWrapper(PyObject);

impl IntoPy<PyObject> for MyPyObjectWrapper {
fn into_py(self, _py: Python) -> PyObject {
self.0
}
}
```

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| {
let obj = PyObject::from_py(1.234, py);
# })
```

After:
```rust
# use pyo3::prelude::*;
# Python::with_gil(|py| {
let obj: PyObject = 1.234.into_py(py);
# })
```

### `PyObject` is now a type alias of `Py<PyAny>`
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.

## from 0.10.* to 0.11

### Stable Rust
Expand Down
62 changes: 14 additions & 48 deletions guide/src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ In PyO3, holding the GIL is modeled by acquiring a token of the type
* It provides some global API for the Python interpreter, such as
[`eval`][eval].
* It can be passed to functions that require a proof of holding the GIL,
such as [`PyObject::clone_ref`][clone_ref].
such as [`Py::clone_ref`][clone_ref].
* Its lifetime can be used to create Rust references that implicitly guarantee
holding the GIL, such as [`&'py PyAny`][PyAny].

Expand Down Expand Up @@ -71,10 +71,7 @@ let obj: &PyAny = PyList::empty(py);
// Convert to &ConcreteType using PyAny::downcast
let _: &PyList = obj.downcast().unwrap();

// Convert to PyObject using .into() or .to_object(py)
let _: PyObject = obj.into();

// Convert to Py<PyAny> using .into() or Py::from
// Convert to Py<PyAny> (aka PyObject) using .into()
let _: Py<PyAny> = obj.into();

// Convert to Py<ConcreteType> using PyAny::extract
Expand Down Expand Up @@ -115,51 +112,24 @@ let _: &PyAny = list;
// For more explicit &PyAny conversion, use .as_ref()
let _: &PyAny = list.as_ref();

// To convert to PyObject use .into() or .to_object(py)
let _: PyObject = list.into();

// To convert to Py<T> use .into() or Py::from()
let _: Py<PyList> = list.into();
```

### `PyObject`

**Represents:** a GIL independent reference to a Python object of unspecified
type.

**Used:** Whenever you want to carry around references to "some" Python object,
without caring about a GIL lifetime. For example, storing Python object
references in a Rust struct that outlives the Python-Rust FFI boundary,
or returning objects from functions implemented in Rust back to Python.

Can be cloned using Python reference counts with `.clone_ref()`.

**Conversions:**

```rust
# use pyo3::prelude::*;
# use pyo3::types::PyList;
# let gil = Python::acquire_gil();
# let py = gil.python();
let obj: PyObject = PyList::empty(py).into();

// Convert to &PyAny using AsPyRef::as_ref
let _: &PyAny = obj.as_ref(py);

// Convert to &ConcreteType using PyObject::cast_as
let _: &PyList = obj.cast_as(py).unwrap();

// Convert to Py<ConcreteType> using PyObject::extract
let _: Py<PyList> = obj.extract(py).unwrap();
// To convert to PyObject use .into() or .to_object(py)
let _: PyObject = list.into();
```

### `Py<SomeType>`
### `Py<T>`

**Represents:** a GIL independent reference to a Python object of known type.
This can be a Python native type (like `PyTuple`), or a `pyclass` type
implemented in Rust.
**Represents:** a GIL independent reference to a Python object. This can be a Python native type
(like `PyTuple`), or a `pyclass` type implemented in Rust. The most commonly-used variant,
`Py<PyAny>`, is also known as `PyObject`.

**Used:** Like `PyObject`, but with a known inner type.
**Used:** Whenever you want to carry around references to a Python object without caring about a
GIL lifetime. For example, storing Python object references in a Rust struct that outlives the
Python-Rust FFI boundary, or returning objects from functions implemented in Rust back to Python.

Can be cloned using Python reference counts with `.clone()`.

**Conversions:**

Expand All @@ -178,10 +148,6 @@ let _: &PyList = list.as_ref(py);
let _: PyObject = list.into();
```

**Note:** `PyObject` is semantically equivalent to `Py<PyAny>` and might be
merged with it in the future.


### `PyCell<SomeType>`

**Represents:** a reference to a Rust object (instance of `PyClass`) which is
Expand Down Expand Up @@ -248,7 +214,7 @@ This trait marks structs that mirror native Python types, such as `PyList`.


[eval]: https://docs.rs/pyo3/latest/pyo3/struct.Python.html#method.eval
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.PyObject.html#method.clone_ref
[clone_ref]: https://docs.rs/pyo3/latest/pyo3/struct.Py.html#method.clone_ref
[pyo3::types]: https://docs.rs/pyo3/latest/pyo3/types/index.html
[PyAny]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html
[PyList_append]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyList.html#method.append
Expand Down
2 changes: 1 addition & 1 deletion pyo3-derive-backend/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pub fn add_fn_to_module(
};

let function = unsafe {
pyo3::PyObject::from_owned_ptr_or_panic(
pyo3::PyObject::from_owned_ptr(
py,
pyo3::ffi::PyCFunction_New(
Box::into_raw(Box::new(_def.as_method_def())),
Expand Down
Loading

0 comments on commit bcb9077

Please sign in to comment.