Skip to content

Commit e1c7ecd

Browse files
committed
Also replace IterANextOutput by autoref-based specialization to allow returning arbitrary values
1 parent be8b6e9 commit e1c7ecd

File tree

4 files changed

+88
-67
lines changed

4 files changed

+88
-67
lines changed

pyo3-macros-backend/src/pymethod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,8 +794,9 @@ const __NEXT__: SlotDef = SlotDef::new("Py_tp_iternext", "iternextfunc")
794794
);
795795
const __AWAIT__: SlotDef = SlotDef::new("Py_am_await", "unaryfunc");
796796
const __AITER__: SlotDef = SlotDef::new("Py_am_aiter", "unaryfunc");
797-
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_conversion(
798-
TokenGenerator(|| quote! { _pyo3::class::pyasync::IterANextOutput::<_, _> }),
797+
const __ANEXT__: SlotDef = SlotDef::new("Py_am_anext", "unaryfunc").return_specialized_conversion(
798+
TokenGenerator(|| quote! { AsyncIterBaseKind, AsyncIterOptionKind, AsyncIterResultOptionKind }),
799+
TokenGenerator(|| quote! { async_iter_tag }),
799800
);
800801
const __LEN__: SlotDef = SlotDef::new("Py_mp_length", "lenfunc").ret_ty(Ty::PySsizeT);
801802
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: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,6 @@ pub mod class {
343343
pub use crate::pyclass::CompareOp;
344344
}
345345

346-
/// Old module which contained some implementation details of the `#[pyproto]` module.
347-
///
348-
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::IterANextOutput` instead
349-
/// of `use pyo3::class::pyasync::IterANextOutput`.
350-
///
351-
/// For compatibility reasons this has not yet been removed, however will be done so
352-
/// once <https://github.com/rust-lang/rust/issues/30827> is resolved.
353-
pub mod pyasync {
354-
pub use crate::pyclass::{IterANextOutput, PyIterANextOutput};
355-
}
356-
357346
/// Old module which contained some implementation details of the `#[pyproto]` module.
358347
///
359348
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::PyTraverseError` instead

src/pyclass.rs

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
//! `PyClass` and related traits.
2-
use crate::{
3-
callback::IntoPyCallbackOutput, ffi, impl_::pyclass::PyClassImpl, IntoPy, PyCell, PyObject,
4-
PyResult, PyTypeInfo, Python,
5-
};
2+
use crate::{ffi, impl_::pyclass::PyClassImpl, PyCell, PyTypeInfo};
63
use std::{cmp::Ordering, os::raw::c_int};
74

85
mod create_type_object;
@@ -85,55 +82,6 @@ impl CompareOp {
8582
}
8683
}
8784

88-
/// Output of `__anext__`.
89-
///
90-
/// <https://docs.python.org/3/reference/expressions.html#agen.__anext__>
91-
pub enum IterANextOutput<T, U> {
92-
/// An expression which the generator yielded.
93-
Yield(T),
94-
/// A `StopAsyncIteration` object.
95-
Return(U),
96-
}
97-
98-
/// An [IterANextOutput] of Python objects.
99-
pub type PyIterANextOutput = IterANextOutput<PyObject, PyObject>;
100-
101-
impl IntoPyCallbackOutput<*mut ffi::PyObject> for PyIterANextOutput {
102-
fn convert(self, _py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
103-
match self {
104-
IterANextOutput::Yield(o) => Ok(o.into_ptr()),
105-
IterANextOutput::Return(opt) => {
106-
Err(crate::exceptions::PyStopAsyncIteration::new_err((opt,)))
107-
}
108-
}
109-
}
110-
}
111-
112-
impl<T, U> IntoPyCallbackOutput<PyIterANextOutput> for IterANextOutput<T, U>
113-
where
114-
T: IntoPy<PyObject>,
115-
U: IntoPy<PyObject>,
116-
{
117-
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
118-
match self {
119-
IterANextOutput::Yield(o) => Ok(IterANextOutput::Yield(o.into_py(py))),
120-
IterANextOutput::Return(o) => Ok(IterANextOutput::Return(o.into_py(py))),
121-
}
122-
}
123-
}
124-
125-
impl<T> IntoPyCallbackOutput<PyIterANextOutput> for Option<T>
126-
where
127-
T: IntoPy<PyObject>,
128-
{
129-
fn convert(self, py: Python<'_>) -> PyResult<PyIterANextOutput> {
130-
match self {
131-
Some(o) => Ok(PyIterANextOutput::Yield(o.into_py(py))),
132-
None => Ok(PyIterANextOutput::Return(py.None().into())),
133-
}
134-
}
135-
}
136-
13785
/// A workaround for [associated const equality](https://github.com/rust-lang/rust/issues/92827).
13886
///
13987
/// This serves to have True / False values in the [`PyClass`] trait's `Frozen` type.

0 commit comments

Comments
 (0)