Also replace IterANextOutput by autoref-based specialization to allow returning arbitrary values

This commit is contained in:
Adam Reichold 2023-12-19 10:27:53 +01:00
parent ca7d90dcf3
commit 83697f0c62
4 changed files with 101 additions and 29 deletions

View File

@ -799,8 +799,9 @@ const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc")
);
const __AWAIT__: SlotDef = SlotDef::new("Py_am_await", "unaryfunc");
const __AITER__: SlotDef = SlotDef::new("Py_am_aiter", "unaryfunc");
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_conversion(
TokenGenerator(|| quote! { _pyo3::class::pyasync::IterANextOutput::<_, _> }),
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_specialized_conversion(
TokenGenerator(|| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind }),
TokenGenerator(|| quote! { async_iter_tag }),
);
const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT);
const __CONTAINS__: SlotDef = SlotDef::new("Py_sq_contains", "objobjproc")

View File

@ -1,4 +1,5 @@
use crate::callback::IntoPyCallbackOutput;
use crate::exceptions::PyStopAsyncIteration;
use crate::gil::LockGIL;
use crate::impl_::panic::PanicTrap;
use crate::internal_tricks::extract_c_string;
@ -381,3 +382,83 @@ pub trait IterResultOptionKind {
}
impl<Value> IterResultOptionKind for PyResult<Option<Value>> {}
// Autoref-based specialization for handling `__anext__` returning `Option`
pub struct AsyncIterBaseTag;
impl AsyncIterBaseTag {
#[inline]
pub fn convert<Value, Target>(self, py: Python<'_>, value: Value) -> PyResult<Target>
where
Value: IntoPyCallbackOutput<Target>,
{
value.convert(py)
}
}
pub trait AsyncIterBaseKind {
#[inline]
fn async_iter_tag(&self) -> AsyncIterBaseTag {
AsyncIterBaseTag
}
}
impl<Value> AsyncIterBaseKind for &Value {}
pub struct AsyncIterOptionTag;
impl AsyncIterOptionTag {
#[inline]
pub fn convert<Value>(
self,
py: Python<'_>,
value: Option<Value>,
) -> PyResult<*mut ffi::PyObject>
where
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
{
match value {
Some(value) => value.convert(py),
None => Err(PyStopAsyncIteration::new_err(())),
}
}
}
pub trait AsyncIterOptionKind {
#[inline]
fn async_iter_tag(&self) -> AsyncIterOptionTag {
AsyncIterOptionTag
}
}
impl<Value> AsyncIterOptionKind for Option<Value> {}
pub struct AsyncIterResultOptionTag;
impl AsyncIterResultOptionTag {
#[inline]
pub fn convert<Value>(
self,
py: Python<'_>,
value: PyResult<Option<Value>>,
) -> PyResult<*mut ffi::PyObject>
where
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
{
match value {
Ok(Some(value)) => value.convert(py),
Ok(None) => Err(PyStopAsyncIteration::new_err(())),
Err(err) => Err(err),
}
}
}
pub trait AsyncIterResultOptionKind {
#[inline]
fn async_iter_tag(&self) -> AsyncIterResultOptionTag {
AsyncIterResultOptionTag
}
}
impl<Value> AsyncIterResultOptionKind for PyResult<Option<Value>> {}

View File

@ -351,6 +351,7 @@ pub mod class {
/// For compatibility reasons this has not yet been removed, however will be done so
/// once <https://github.com/rust-lang/rust/issues/30827> is resolved.
pub mod pyasync {
#[allow(deprecated)]
pub use crate::pyclass::{IterANextOutput, PyIterANextOutput};
}

View File

@ -155,6 +155,10 @@ where
/// Output of `__anext__`.
///
/// <https://docs.python.org/3/reference/expressions.html#agen.__anext__>
#[deprecated(
since = "0.21.0",
note = "Use `Option` or `PyStopAsyncIteration` instead."
)]
pub enum IterANextOutput<T, U> {
/// An expression which the generator yielded.
Yield(T),
@ -163,40 +167,25 @@ pub enum IterANextOutput<T, U> {
}
/// An [IterANextOutput] of Python objects.
#[deprecated(
since = "0.21.0",
note = "Use `Option` or `PyStopAsyncIteration` instead."
)]
#[allow(deprecated)]
pub type PyIterANextOutput = IterANextOutput<PyObject, PyObject>;
impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput {
fn convert(self, _py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
match self {
IterANextOutput::Yield(o) => Ok(o.into_ptr()),
IterANextOutput::Return(opt) => {
Err(crate::exceptions::PyStopAsyncIteration::new_err((opt,)))
}
}
}
}
impl<T, U> IntoPyCallbackOutput<PyIterANextOutput> for IterANextOutput<T, U>
#[allow(deprecated)]
impl<T, U> IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput<T, U>
where
T: IntoPy<PyObject>,
U: IntoPy<PyObject>,
{
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
match self {
IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_py(py))),
IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_py(py))),
}
}
}
impl<T> IntoPyCallbackOutput<PyIterANextOutput> for Option<T>
where
T: IntoPy<PyObject>,
{
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
match self {
Some(o) => Ok(PyIterANextOutput::Yield(o.into_py(py))),
None => Ok(PyIterANextOutput::Return(py.None().into())),
IterANextOutput::Yield(o) => Ok(o.into_py(py).into_ptr()),
IterANextOutput::Return(o) => Err(crate::exceptions::PyStopAsyncIteration::new_err(
o.into_py(py),
)),
}
}
}