Skip to content

Commit 83697f0

Browse files
committed
Also replace IterANextOutput by autoref-based specialization to allow returning arbitrary values
1 parent ca7d90d commit 83697f0

File tree

4 files changed

+101
-29
lines changed

4 files changed

+101
-29
lines changed

pyo3-macros-backend/src/pymethod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -799,8 +799,9 @@ const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc")
799799
);
800800
const __AWAIT__: SlotDef = SlotDef::new("Py_am_await", "unaryfunc");
801801
const __AITER__: SlotDef = SlotDef::new("Py_am_aiter", "unaryfunc");
802-
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_conversion(
803-
TokenGenerator(|| quote! { _pyo3::class::pyasync::IterANextOutput::<_, _> }),
802+
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_specialized_conversion(
803+
TokenGenerator(|| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind }),
804+
TokenGenerator(|| quote! { async_iter_tag }),
804805
);
805806
const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT);
806807
const __CONTAINS__: SlotDef = SlotDef::new("Py_sq_contains", "objobjproc")

src/impl_/pymethods.rs

+81
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::callback::IntoPyCallbackOutput;
2+
use crate::exceptions::PyStopAsyncIteration;
23
use crate::gil::LockGIL;
34
use crate::impl_::panic::PanicTrap;
45
use crate::internal_tricks::extract_c_string;
@@ -381,3 +382,83 @@ pub trait IterResultOptionKind {
381382
}
382383

383384
impl<Value> IterResultOptionKind for PyResult<Option<Value>> {}
385+
386+
// Autoref-based specialization for handling `__anext__` returning `Option`
387+
388+
pub struct AsyncIterBaseTag;
389+
390+
impl AsyncIterBaseTag {
391+
#[inline]
392+
pub fn convert<Value, Target>(self, py: Python<'_>, value: Value) -> PyResult<Target>
393+
where
394+
Value: IntoPyCallbackOutput<Target>,
395+
{
396+
value.convert(py)
397+
}
398+
}
399+
400+
pub trait AsyncIterBaseKind {
401+
#[inline]
402+
fn async_iter_tag(&self) -> AsyncIterBaseTag {
403+
AsyncIterBaseTag
404+
}
405+
}
406+
407+
impl<Value> AsyncIterBaseKind for &Value {}
408+
409+
pub struct AsyncIterOptionTag;
410+
411+
impl AsyncIterOptionTag {
412+
#[inline]
413+
pub fn convert<Value>(
414+
self,
415+
py: Python<'_>,
416+
value: Option<Value>,
417+
) -> PyResult<*mut ffi::PyObject>
418+
where
419+
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
420+
{
421+
match value {
422+
Some(value) => value.convert(py),
423+
None => Err(PyStopAsyncIteration::new_err(())),
424+
}
425+
}
426+
}
427+
428+
pub trait AsyncIterOptionKind {
429+
#[inline]
430+
fn async_iter_tag(&self) -> AsyncIterOptionTag {
431+
AsyncIterOptionTag
432+
}
433+
}
434+
435+
impl<Value> AsyncIterOptionKind for Option<Value> {}
436+
437+
pub struct AsyncIterResultOptionTag;
438+
439+
impl AsyncIterResultOptionTag {
440+
#[inline]
441+
pub fn convert<Value>(
442+
self,
443+
py: Python<'_>,
444+
value: PyResult<Option<Value>>,
445+
) -> PyResult<*mut ffi::PyObject>
446+
where
447+
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
448+
{
449+
match value {
450+
Ok(Some(value)) => value.convert(py),
451+
Ok(None) => Err(PyStopAsyncIteration::new_err(())),
452+
Err(err) => Err(err),
453+
}
454+
}
455+
}
456+
457+
pub trait AsyncIterResultOptionKind {
458+
#[inline]
459+
fn async_iter_tag(&self) -> AsyncIterResultOptionTag {
460+
AsyncIterResultOptionTag
461+
}
462+
}
463+
464+
impl<Value> AsyncIterResultOptionKind for PyResult<Option<Value>> {}

src/lib.rs

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

src/pyclass.rs

+16-27
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ where
155155
/// Output of `__anext__`.
156156
///
157157
/// <https://docs.python.org/3/reference/expressions.html#agen.__anext__>
158+
#[deprecated(
159+
since = "0.21.0",
160+
note = "Use `Option` or `PyStopAsyncIteration` instead."
161+
)]
158162
pub enum IterANextOutput<T, U> {
159163
/// An expression which the generator yielded.
160164
Yield(T),
@@ -163,40 +167,25 @@ pub enum IterANextOutput<T, U> {
163167
}
164168

165169
/// An [IterANextOutput] of Python objects.
170+
#[deprecated(
171+
since = "0.21.0",
172+
note = "Use `Option` or `PyStopAsyncIteration` instead."
173+
)]
174+
#[allow(deprecated)]
166175
pub type PyIterANextOutput = IterANextOutput<PyObject, PyObject>;
167176

168-
impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput {
169-
fn convert(self, _py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
170-
match self {
171-
IterANextOutput::Yield(o) => Ok(o.into_ptr()),
172-
IterANextOutput::Return(opt) => {
173-
Err(crate::exceptions::PyStopAsyncIteration::new_err((opt,)))
174-
}
175-
}
176-
}
177-
}
178-
179-
impl<T, U> IntoPyCallbackOutput<PyIterANextOutput> for IterANextOutput<T, U>
177+
#[allow(deprecated)]
178+
impl<T, U> IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput<T, U>
180179
where
181180
T: IntoPy<PyObject>,
182181
U: IntoPy<PyObject>,
183182
{
184-
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
185-
match self {
186-
IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_py(py))),
187-
IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_py(py))),
188-
}
189-
}
190-
}
191-
192-
impl<T> IntoPyCallbackOutput<PyIterANextOutput> for Option<T>
193-
where
194-
T: IntoPy<PyObject>,
195-
{
196-
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
183+
fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
197184
match self {
198-
Some(o) => Ok(PyIterANextOutput::Yield(o.into_py(py))),
199-
None => Ok(PyIterANextOutput::Return(py.None().into())),
185+
IterANextOutput::Yield(o) => Ok(o.into_py(py).into_ptr()),
186+
IterANextOutput::Return(o) => Err(crate::exceptions::PyStopAsyncIteration::new_err(
187+
o.into_py(py),
188+
)),
200189
}
201190
}
202191
}

0 commit comments

Comments
 (0)