Skip to content

Commit 535898a

Browse files
committed
Also replace IterANextOutput by autoref-based specialization to allow returning arbitrary values
1 parent 5c74e88 commit 535898a

File tree

4 files changed

+104
-30
lines changed

4 files changed

+104
-30
lines changed

pyo3-macros-backend/src/pymethod.rs

Lines changed: 3 additions & 2 deletions
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/callback.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Utilities for a Python callable object that invokes a Rust function.
22
33
use crate::err::{PyErr, PyResult};
4-
use crate::exceptions::PyOverflowError;
4+
use crate::exceptions::{PyOverflowError, PyStopAsyncIteration};
55
use crate::ffi::{self, Py_hash_t};
66
use crate::{IntoPy, PyObject, Python};
77
use std::isize;
@@ -260,3 +260,86 @@ pub trait IterResultOptionKind {
260260
}
261261

262262
impl<Value> IterResultOptionKind for PyResult<Option<Value>> {}
263+
264+
// Autoref-based specialization for handling `__anext__` returning `Option`
265+
266+
#[doc(hidden)]
267+
pub struct AsyncIterBaseTag;
268+
269+
impl AsyncIterBaseTag {
270+
#[inline]
271+
pub fn convert<Value, Target>(self, py: Python<'_>, value: Value) -> PyResult<Target>
272+
where
273+
Value: IntoPyCallbackOutput<Target>,
274+
{
275+
value.convert(py)
276+
}
277+
}
278+
279+
#[doc(hidden)]
280+
pub trait AsyncIterBaseKind {
281+
fn async_iter_tag(&self) -> AsyncIterBaseTag {
282+
AsyncIterBaseTag
283+
}
284+
}
285+
286+
impl<Value> AsyncIterBaseKind for &Value {}
287+
288+
#[doc(hidden)]
289+
pub struct AsyncIterOptionTag;
290+
291+
impl AsyncIterOptionTag {
292+
#[inline]
293+
pub fn convert<Value>(
294+
self,
295+
py: Python<'_>,
296+
value: Option<Value>,
297+
) -> PyResult<*mut ffi::PyObject>
298+
where
299+
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
300+
{
301+
match value {
302+
Some(value) => value.convert(py),
303+
None => Err(PyStopAsyncIteration::new_err(())),
304+
}
305+
}
306+
}
307+
308+
#[doc(hidden)]
309+
pub trait AsyncIterOptionKind {
310+
fn async_iter_tag(&self) -> AsyncIterOptionTag {
311+
AsyncIterOptionTag
312+
}
313+
}
314+
315+
impl<Value> AsyncIterOptionKind for Option<Value> {}
316+
317+
#[doc(hidden)]
318+
pub struct AsyncIterResultOptionTag;
319+
320+
impl AsyncIterResultOptionTag {
321+
#[inline]
322+
pub fn convert<Value>(
323+
self,
324+
py: Python<'_>,
325+
value: PyResult<Option<Value>>,
326+
) -> PyResult<*mut ffi::PyObject>
327+
where
328+
Value: IntoPyCallbackOutput<*mut ffi::PyObject>,
329+
{
330+
match value {
331+
Ok(Some(value)) => value.convert(py),
332+
Ok(None) => Err(PyStopAsyncIteration::new_err(())),
333+
Err(err) => Err(err),
334+
}
335+
}
336+
}
337+
338+
#[doc(hidden)]
339+
pub trait AsyncIterResultOptionKind {
340+
fn async_iter_tag(&self) -> AsyncIterResultOptionTag {
341+
AsyncIterResultOptionTag
342+
}
343+
}
344+
345+
impl<Value> AsyncIterResultOptionKind for PyResult<Option<Value>> {}

src/lib.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 16 additions & 27 deletions
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)