diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 4d0972b5b59..803c0d67106 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -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") diff --git a/src/callback.rs b/src/callback.rs index 0a085baea6b..2edb9cd5ac0 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,7 +1,7 @@ //! Utilities for a Python callable object that invokes a Rust function. use crate::err::{PyErr, PyResult}; -use crate::exceptions::PyOverflowError; +use crate::exceptions::{PyOverflowError, PyStopAsyncIteration}; use crate::ffi::{self, Py_hash_t}; use crate::{IntoPy, PyObject, Python}; use std::isize; @@ -260,3 +260,86 @@ pub trait IterResultOptionKind { } impl IterResultOptionKind for PyResult> {} + +// Autoref-based specialization for handling `__anext__` returning `Option` + +#[doc(hidden)] +pub struct AsyncIterBaseTag; + +impl AsyncIterBaseTag { + #[inline] + pub fn convert(self, py: Python<'_>, value: Value) -> PyResult + where + Value: IntoPyCallbackOutput, + { + value.convert(py) + } +} + +#[doc(hidden)] +pub trait AsyncIterBaseKind { + fn async_iter_tag(&self) -> AsyncIterBaseTag { + AsyncIterBaseTag + } +} + +impl AsyncIterBaseKind for &Value {} + +#[doc(hidden)] +pub struct AsyncIterOptionTag; + +impl AsyncIterOptionTag { + #[inline] + pub fn convert( + self, + py: Python<'_>, + value: Option, + ) -> PyResult<*mut ffi::PyObject> + where + Value: IntoPyCallbackOutput<*mut ffi::PyObject>, + { + match value { + Some(value) => value.convert(py), + None => Err(PyStopAsyncIteration::new_err(())), + } + } +} + +#[doc(hidden)] +pub trait AsyncIterOptionKind { + fn async_iter_tag(&self) -> AsyncIterOptionTag { + AsyncIterOptionTag + } +} + +impl AsyncIterOptionKind for Option {} + +#[doc(hidden)] +pub struct AsyncIterResultOptionTag; + +impl AsyncIterResultOptionTag { + #[inline] + pub fn convert( + self, + py: Python<'_>, + value: PyResult>, + ) -> 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), + } + } +} + +#[doc(hidden)] +pub trait AsyncIterResultOptionKind { + fn async_iter_tag(&self) -> AsyncIterResultOptionTag { + AsyncIterResultOptionTag + } +} + +impl AsyncIterResultOptionKind for PyResult> {} diff --git a/src/lib.rs b/src/lib.rs index f787b23a2ba..091be583ee1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -351,6 +351,7 @@ pub mod class { /// For compatibility reasons this has not yet been removed, however will be done so /// once is resolved. pub mod pyasync { + #[allow(deprecated)] pub use crate::pyclass::{IterANextOutput, PyIterANextOutput}; } diff --git a/src/pyclass.rs b/src/pyclass.rs index b1c49be28ba..eb4a5595ca9 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -155,6 +155,10 @@ where /// Output of `__anext__`. /// /// +#[deprecated( + since = "0.21.0", + note = "Use `Option` or `PyStopAsyncIteration` instead." +)] pub enum IterANextOutput { /// An expression which the generator yielded. Yield(T), @@ -163,40 +167,25 @@ pub enum IterANextOutput { } /// An [IterANextOutput] of Python objects. +#[deprecated( + since = "0.21.0", + note = "Use `Option` or `PyStopAsyncIteration` instead." +)] +#[allow(deprecated)] pub type PyIterANextOutput = IterANextOutput; -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 IntoPyCallbackOutput for IterANextOutput +#[allow(deprecated)] +impl IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput where T: IntoPy, U: IntoPy, { - fn convert(self, py: Python<'_>) -> PyResult { - match self { - IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_py(py))), - IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_py(py))), - } - } -} - -impl IntoPyCallbackOutput for Option -where - T: IntoPy, -{ - fn convert(self, py: Python<'_>) -> PyResult { + fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> { 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), + )), } } }