Add change log and migration guide entries.

This commit is contained in:
Adam Reichold 2023-12-19 18:54:56 +01:00
parent 83697f0c62
commit a605308cee
2 changed files with 119 additions and 1 deletions

View File

@ -74,7 +74,124 @@ Python::with_gil(|py| {
});
```
### `PyType::name` is now `PyType::qualname`
### `Iter(A)NextOutput` are deprecated
The `__next__` and `__anext__` magic methods can now return any type convertible into Python objects directly just like all other `#[pymethods]`. The `IterNextOutput` used by `__next__` and `IterANextOutput` used by `__anext__` are subsequently deprecated. Most importantly, this change allows returning an awaitable from `__anext__` without non-sensically wrapping it into `Yield` or `Some`. Only the return types `Option<T>` and `PyResult<Option<T>>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration.
Starting with an implementation of a Python iterator using `IterNextOutput`, e.g.
```rust
#![allow(deprecated)]
use pyo3::prelude::*;
use pyo3::iter::IterNextOutput;
#[pyclass]
struct PyClassIter {
count: usize,
}
#[pymethods]
impl PyClassIter {
fn __next__(&mut self) -> IterNextOutput<usize, &'static str> {
if self.count < 5 {
self.count += 1;
IterNextOutput::Yield(self.count)
} else {
IterNextOutput::Return("done")
}
}
}
```
If returning `"done"` via `StopIteration` is not really required, this should be written as
```rust
use pyo3::prelude::*;
#[pyclass]
struct PyClassIter {
count: usize,
}
#[pymethods]
impl PyClassIter {
fn __next__(&mut self) -> Option<usize> {
if self.count < 5 {
self.count += 1;
Some(self.count)
} else {
None
}
}
}
```
This form also has additional benefits: It has already worked in previous PyO3 versions, it matches the signature of Rust's [`Iterator` trait](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html) and it allows using a fast path in CPython which completely avoids the cost of raising a `StopIteration` exception. Note that using [`Option::transpose`](https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.transpose) and the `PyResult<Option<T>>` variant, this form can also be used to wrap fallible iterators.
Alternatively, the implementation can also be done as it would in Python itself, i.e. by "raising" a `StopIteration` exception
```rust
use pyo3::prelude::*;
use pyo3::exceptions::PyStopIteration;
#[pyclass]
struct PyClassIter {
count: usize,
}
#[pymethods]
impl PyClassIter {
fn __next__(&mut self) -> PyResult<usize> {
if self.count < 5 {
self.count += 1;
Ok(self.count)
} else {
Err(PyStopIteration::new_err("done"))
}
}
}
```
Finally, an asynchronous iterator can directly return an awaitable without confusing wrapping
```rust
use pyo3::prelude::*;
#[pyclass]
struct PyClassAwaitable {
number: usize,
}
#[pymethods]
impl PyClassAwaitable {
fn __next__(&self) -> usize {
self.number
}
fn __await__(slf: Py<Self>) -> Py<Self> {
slf
}
}
#[pyclass]
struct PyClassAsyncIter {
number: usize,
}
#[pymethods]
impl PyClassAsyncIter {
fn __anext__(&mut self) -> PyClassAwaitable {
self.number += 1;
PyClassAwaitable { number: self.number }
}
fn __aiter__(slf: Py<Self>) -> Py<Self> {
slf
}
}
```
### `PyType::name` has been renamed to `PyType::qualname`
`PyType::name` has been renamed to `PyType::qualname` to indicate that it does indeed return the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name), matching the `__qualname__` attribute. The newly added `PyType::name` yields the full name including the module name now which corresponds to `__module__.__name__` on the level of attributes.

View File

@ -0,0 +1 @@
The `Iter(A)NextOutput` types are now deprecated and `__(a)next__` can directly return anything which can be converted into Python objects, i.e. awaitables do not need to be wrapped into `IterANextOutput` or `Option` any more. `Option` can still be used as well and returning `None` will trigger the fast path for `__next__`, stopping iteration without having to raise a `StopIteration` exception.