Relax the error type in the Result<Option<T>, E>> specializations for __(a)next__.

This commit is contained in:
Adam Reichold 2023-12-20 13:12:16 +01:00
parent a605308cee
commit 5528895f3e
2 changed files with 15 additions and 11 deletions

View File

@ -76,7 +76,7 @@ Python::with_gil(|py| {
### `Iter(A)NextOutput` are deprecated ### `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. 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 `Result<Option<T>, E>` 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. Starting with an implementation of a Python iterator using `IterNextOutput`, e.g.
@ -126,7 +126,7 @@ impl PyClassIter {
} }
``` ```
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. 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 `Result<Option<T>, E>` 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 Alternatively, the implementation can also be done as it would in Python itself, i.e. by "raising" a `StopIteration` exception

View File

@ -3,7 +3,9 @@ use crate::exceptions::PyStopAsyncIteration;
use crate::gil::LockGIL; use crate::gil::LockGIL;
use crate::impl_::panic::PanicTrap; use crate::impl_::panic::PanicTrap;
use crate::internal_tricks::extract_c_string; use crate::internal_tricks::extract_c_string;
use crate::{ffi, PyAny, PyCell, PyClass, PyObject, PyResult, PyTraverseError, PyVisit, Python}; use crate::{
ffi, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult, PyTraverseError, PyVisit, Python,
};
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt; use std::fmt;
@ -358,18 +360,19 @@ pub struct IterResultOptionTag;
impl IterResultOptionTag { impl IterResultOptionTag {
#[inline] #[inline]
pub fn convert<Value>( pub fn convert<Value, Error>(
self, self,
py: Python<'_>, py: Python<'_>,
value: PyResult<Option<Value>>, value: Result<Option<Value>, Error>,
) -> PyResult<*mut ffi::PyObject> ) -> PyResult<*mut ffi::PyObject>
where where
Value: IntoPyCallbackOutput<*mut ffi::PyObject>, Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
Error: Into<PyErr>,
{ {
match value { match value {
Ok(Some(value)) => value.convert(py), Ok(Some(value)) => value.convert(py),
Ok(None) => Ok(null_mut()), Ok(None) => Ok(null_mut()),
Err(err) => Err(err), Err(err) => Err(err.into()),
} }
} }
} }
@ -381,7 +384,7 @@ pub trait IterResultOptionKind {
} }
} }
impl<Value> IterResultOptionKind for PyResult<Option<Value>> {} impl<Value, Error> IterResultOptionKind for Result<Option<Value>, Error> {}
// Autoref-based specialization for handling `__anext__` returning `Option` // Autoref-based specialization for handling `__anext__` returning `Option`
@ -438,18 +441,19 @@ pub struct AsyncIterResultOptionTag;
impl AsyncIterResultOptionTag { impl AsyncIterResultOptionTag {
#[inline] #[inline]
pub fn convert<Value>( pub fn convert<Value, Error>(
self, self,
py: Python<'_>, py: Python<'_>,
value: PyResult<Option<Value>>, value: Result<Option<Value>, Error>,
) -> PyResult<*mut ffi::PyObject> ) -> PyResult<*mut ffi::PyObject>
where where
Value: IntoPyCallbackOutput<*mut ffi::PyObject>, Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
Error: Into<PyErr>,
{ {
match value { match value {
Ok(Some(value)) => value.convert(py), Ok(Some(value)) => value.convert(py),
Ok(None) => Err(PyStopAsyncIteration::new_err(())), Ok(None) => Err(PyStopAsyncIteration::new_err(())),
Err(err) => Err(err), Err(err) => Err(err.into()),
} }
} }
} }
@ -461,4 +465,4 @@ pub trait AsyncIterResultOptionKind {
} }
} }
impl<Value> AsyncIterResultOptionKind for PyResult<Option<Value>> {} impl<Value, Error> AsyncIterResultOptionKind for Result<Option<Value>, Error> {}