gh-157361: Implement aiter() in Python instead of C (#157687)

gh-157361: Implement aiter() in Python instead of C

Move both forms of aiter() into the frozen _pybuiltins module and remove
the C async_callable_iterator types. The callable form now returns an
iterator whose __anext__() is a plain coroutine, so call-graph tools can
see through it into the awaited result of the callable.
This commit is contained in:
Kumar Aditya authored and GitHub committed 2026-10-20 15:33:36 +05:30
1 parent 4c73f50ca9
commit cb12c5e514
11 files changed
+211 -511

No files matched your search

+1 -1
View File
@@ -538,7 +538,7 @@ struct _py_func_state {
If you add a new static type to the standard library, you may have to
update one of these numbers.
*/
#define _Py_NUM_MANAGED_PREINITIALIZED_TYPES 122
#define _Py_NUM_MANAGED_PREINITIALIZED_TYPES 120
#define _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES \
(_Py_NUM_MANAGED_PREINITIALIZED_TYPES + 83)
#define _Py_MAX_MANAGED_STATIC_EXT_TYPES 10
-8
View File
@@ -8,20 +8,12 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif
extern PyTypeObject _PyACallIter_Type;
extern PyTypeObject _PyACallIterAwaitable_Type;
// Like PyCallIter_New(), but the iteration also stops when *callable* raises
// an exception matching *stop_exc* (an exception class or a tuple of exception
// classes). *sentinel* can be NULL; NULL *stop_exc* means StopIteration.
extern PyObject *_PyCallIter_NewEx(PyObject *callable, PyObject *sentinel,
PyObject *stop_exc);
// The asynchronous counterpart of _PyCallIter_NewEx(): the result of
// *callable* is awaited, and NULL *stop_exc* means StopAsyncIteration.
extern PyObject *_PyACallIter_New(PyObject *callable, PyObject *sentinel,
PyObject *stop_exc);
#ifdef __cplusplus
}
#endif
+86 -1
View File
@@ -5,11 +5,96 @@ before the import system exists. The names listed in ``__all__`` are
copied into the ``builtins`` module.
"""
__all__ = ['anext']
__all__ = ['aiter', 'anext']
_NOT_GIVEN = sentinel("_NOT_GIVEN")
def aiter(object, /, stop_value=_NOT_GIVEN, *, stop_exception=_NOT_GIVEN):
"""Return an AsyncIterator for an AsyncIterable object.
In the second form, the callable is called and its result is awaited
until it returns the stop value or raises the specified exception.
"""
if stop_value is _NOT_GIVEN and stop_exception is _NOT_GIVEN:
cls = type(object)
try:
# Looked up on the type, like the C slot am_aiter.
aiter_method = cls.__aiter__
except AttributeError:
raise TypeError(
f"'{cls.__name__}' object is not an async iterable"
) from None
iterator = aiter_method(object)
if not hasattr(type(iterator), '__anext__'):
raise TypeError(
f"{cls.__name__}.__aiter__() must return an async iterator, "
f"not {type(iterator).__name__}"
)
return iterator
if not callable(object):
raise TypeError("aiter(): the first argument must be callable")
if stop_exception is _NOT_GIVEN:
stop_exception = StopAsyncIteration
else:
# Like _PyEval_CheckExceptTypeValid(): an exception class or a tuple
# of exception classes.
if isinstance(stop_exception, tuple):
items = stop_exception
else:
items = (stop_exception,)
for item in items:
if not (isinstance(item, type) and issubclass(item, BaseException)):
raise TypeError(
"catching classes that do not inherit from BaseException "
"is not allowed"
)
return async_callable_iterator(object, stop_value, stop_exception)
class async_callable_iterator:
"""The asynchronous counterpart of callable_iterator.
The callable is called and its result is awaited for every
__anext__().
"""
__slots__ = ('_callable', '_stop_value', '_stop_exception')
__module__ = 'builtins'
def __init__(self, callable, stop_value, stop_exception):
# _callable is set to None when the iterator is exhausted.
self._callable = callable
self._stop_value = stop_value
self._stop_exception = stop_exception
def __aiter__(self):
return self
async def __anext__(self):
callable = self._callable
if callable is None:
raise StopAsyncIteration
try:
value = await callable()
except self._stop_exception:
self._exhaust()
raise StopAsyncIteration from None
except (StopIteration, StopAsyncIteration) as exc:
raise RuntimeError(
f"callable raised {type(exc).__name__}") from exc
stop_value = self._stop_value
if stop_value is not _NOT_GIVEN and stop_value == value:
self._exhaust()
raise StopAsyncIteration
return value
def _exhaust(self):
self._callable = None
self._stop_value = _NOT_GIVEN
def anext(async_iterator, default=_NOT_GIVEN, /):
"""Return the next item from the async iterator.
+117
View File
@@ -148,6 +148,45 @@ class CallStackTestBase:
'async generator CallStackTestBase.test_stack_async_gen.<locals>.gen()',
stack_for_gen_nested_call[1])
async def test_stack_anext(self):
# anext() without a default returns the __anext__() coroutine
# itself, so nothing is inserted between it and main().
loop = asyncio.get_running_loop()
blocker = loop.create_future()
async def inner():
await blocker
class AIter:
def __aiter__(self):
return self
async def __anext__(self):
await inner()
return 1
async def main():
await anext(AIter())
task = asyncio.create_task(main(), name='anext task')
await asyncio.sleep(0)
try:
stack = capture_test_stack(fut=task)
finally:
blocker.set_result(None)
await task
self.assertEqual(stack[0], [
'T<anext task>',
[
'a inner',
'a __anext__',
'a main',
],
[]
])
async def test_stack_anext_default(self):
# anext() with a default wraps the awaitable in a coroutine, so the
# call graph of a suspended task sees through it into __anext__().
@@ -188,6 +227,84 @@ class CallStackTestBase:
[]
])
async def test_stack_aiter(self):
# The one-argument form of aiter() returns the object's own
# iterator, so it adds no frame of its own to the call graph.
loop = asyncio.get_running_loop()
blocker = loop.create_future()
async def inner():
await blocker
class AIter:
def __aiter__(self):
return self
async def __anext__(self):
await inner()
return 1
async def main():
async for _ in aiter(AIter()):
break
task = asyncio.create_task(main(), name='aiter task')
await asyncio.sleep(0)
try:
stack = capture_test_stack(fut=task)
finally:
blocker.set_result(None)
await task
self.assertEqual(stack[0], [
'T<aiter task>',
[
'a inner',
'a __anext__',
'a main',
],
[]
])
async def test_stack_aiter_callable(self):
# aiter(callable, ...) returns an iterator whose __anext__() is a
# coroutine, so the call graph of a suspended task sees through it
# into the awaited result of the callable.
loop = asyncio.get_running_loop()
blocker = loop.create_future()
async def inner():
await blocker
async def spam():
await inner()
return 1
async def main():
async for _ in aiter(spam, 1):
pass
task = asyncio.create_task(main(), name='aiter task')
await asyncio.sleep(0)
try:
stack = capture_test_stack(fut=task)
finally:
blocker.set_result(None)
await task
self.assertEqual(stack[0], [
'T<aiter task>',
[
'a inner',
'a spam',
'a __anext__',
'a main',
],
[]
])
def test_ag_frame_used_for_async_generator(self):
# Regression test for gh-148736: the ag_await branch of
# _build_graph_for_future must read ag_frame, not cr_frame.
+1 -1
View File
@@ -6229,7 +6229,7 @@ class TestSignatureDefinitions(unittest.TestCase):
needs_groups = {"range", "slice", "getattr", "next"}
no_signature |= needs_groups
# These have unrepresentable parameter default values of NULL
unsupported_signature = {"aiter", "iter", "dir", "vars"}
unsupported_signature = {"iter", "dir", "vars"}
# These need *args support in Argument Clinic
needs_varargs = {"min", "max", "__build_class__"}
no_signature |= needs_varargs
@@ -0,0 +1,5 @@
Implement :func:`aiter` in Python instead of C, in the frozen ``_pybuiltins``
module. The async iterator returned by ``aiter(callable, ...)`` now has a
plain coroutine ``__anext__()``, so introspection tools such as
:func:`asyncio.print_call_graph` can see through it into the awaited
callable result.
-378
View File
@@ -399,381 +399,3 @@ PyTypeObject PyCallIter_Type = {
calliter_iternext, /* tp_iternext */
calliter_methods, /* tp_methods */
};
/* -------------------------------------- */
static PyObject *
awaitable_getiter(PyObject *owner, PyObject *wrapped)
{
assert(wrapped != NULL);
PyObject *awaitable = _PyCoro_GetAwaitableIter(wrapped);
if (awaitable == NULL) {
return NULL;
}
if (Py_TYPE(awaitable)->tp_iternext == NULL) {
/* _PyCoro_GetAwaitableIter returns a Coroutine, a Generator,
* or an iterator. Of these, only coroutines lack tp_iternext.
*/
assert(PyCoro_CheckExact(awaitable));
unaryfunc getter = Py_TYPE(awaitable)->tp_as_async->am_await;
PyObject *new_awaitable = getter(awaitable);
if (new_awaitable == NULL) {
Py_DECREF(awaitable);
return NULL;
}
Py_SETREF(awaitable, new_awaitable);
if (!PyIter_Check(awaitable)) {
PyErr_Format(PyExc_TypeError,
"%T.__await__() must return an iterable, not %T",
owner, awaitable);
Py_DECREF(awaitable);
return NULL;
}
}
return awaitable;
}
PyDoc_STRVAR(send_doc,
"send(arg) -> send 'arg' into the wrapped iterator,\n\
return next yielded value or raise StopIteration.");
PyDoc_STRVAR(throw_doc,
"throw(value)\n\
throw(typ[,val[,tb]])\n\
\n\
raise exception in the wrapped iterator, return next yielded value\n\
or raise StopIteration.\n\
the (type, val, tb) signature is deprecated, \n\
and may be removed in a future version of Python.");
PyDoc_STRVAR(close_doc,
"close() -> raise GeneratorExit inside generator.");
/* -------------------------------------- */
/* The asynchronous counterpart of calliterobject: the callable is called
and its result is awaited for every __anext__(). */
typedef struct {
PyObject_HEAD
PyObject *it_callable; /* set to NULL when the iterator is exhausted */
PyObject *it_sentinel; /* can be NULL, and is when exhausted */
PyObject *it_stop_exc; /* never NULL */
} acalliterobject;
#define acalliterobject_CAST(op) ((acalliterobject *)(op))
/* The awaitable returned by acalliter_anext(). The callable is only
called when this object is awaited. */
typedef struct {
PyObject_HEAD
PyObject *aw_iterator; /* the iterator which created this object */
PyObject *aw_wrapped; /* the awaitable returned by the callable */
bool aw_closed;
} acallawaitableobject;
#define acallawaitableobject_CAST(op) ((acallawaitableobject *)(op))
PyObject *
_PyACallIter_New(PyObject *callable, PyObject *sentinel, PyObject *stop_exc)
{
if (stop_exc == NULL) {
stop_exc = PyExc_StopAsyncIteration;
}
else if (_PyEval_CheckExceptTypeValid(_PyThreadState_GET(), stop_exc) < 0) {
return NULL;
}
acalliterobject *it = PyObject_GC_New(acalliterobject, &_PyACallIter_Type);
if (it == NULL) {
return NULL;
}
it->it_callable = Py_NewRef(callable);
it->it_sentinel = Py_XNewRef(sentinel);
it->it_stop_exc = Py_NewRef(stop_exc);
_PyObject_GC_TRACK(it);
return (PyObject *)it;
}
static void
acalliter_exhaust(acalliterobject *it)
{
Py_CLEAR(it->it_callable);
Py_CLEAR(it->it_sentinel);
}
static void
acalliter_dealloc(PyObject *op)
{
acalliterobject *it = acalliterobject_CAST(op);
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_callable);
Py_XDECREF(it->it_sentinel);
Py_XDECREF(it->it_stop_exc);
PyObject_GC_Del(it);
}
static int
acalliter_traverse(PyObject *op, visitproc visit, void *arg)
{
acalliterobject *it = acalliterobject_CAST(op);
Py_VISIT(it->it_callable);
Py_VISIT(it->it_sentinel);
Py_VISIT(it->it_stop_exc);
return 0;
}
static PyObject *acallawaitable_new(PyObject *iterator);
static PyObject *
acalliter_anext(PyObject *op)
{
return acallawaitable_new(op);
}
static PyAsyncMethods acalliter_as_async = {
0, /* am_await */
PyObject_SelfIter, /* am_aiter */
acalliter_anext, /* am_anext */
0, /* am_send */
};
PyTypeObject _PyACallIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "async_callable_iterator",
.tp_basicsize = sizeof(acalliterobject),
.tp_dealloc = acalliter_dealloc,
.tp_as_async = &acalliter_as_async,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = acalliter_traverse,
};
/* -------------------------------------- */
static PyObject *
acallawaitable_new(PyObject *iterator)
{
acallawaitableobject *aw = PyObject_GC_New(
acallawaitableobject, &_PyACallIterAwaitable_Type);
if (aw == NULL) {
return NULL;
}
aw->aw_iterator = Py_NewRef(iterator);
aw->aw_wrapped = NULL;
aw->aw_closed = false;
_PyObject_GC_TRACK(aw);
return (PyObject *)aw;
}
static void
acallawaitable_dealloc(PyObject *op)
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
_PyObject_GC_UNTRACK(aw);
Py_XDECREF(aw->aw_iterator);
Py_XDECREF(aw->aw_wrapped);
PyObject_GC_Del(aw);
}
static int
acallawaitable_traverse(PyObject *op, visitproc visit, void *arg)
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
Py_VISIT(aw->aw_iterator);
Py_VISIT(aw->aw_wrapped);
return 0;
}
/* Call the callable. Return 0 on success, -1 on failure. */
static int
acallawaitable_start(acallawaitableobject *aw)
{
acalliterobject *it = acalliterobject_CAST(aw->aw_iterator);
if (aw->aw_closed) {
PyErr_SetString(PyExc_RuntimeError,
"cannot reuse already awaited __anext__()");
return -1;
}
if (it->it_callable == NULL) {
PyErr_SetNone(PyExc_StopAsyncIteration);
return -1;
}
PyObject *awaitable = _PyObject_CallNoArgs(it->it_callable);
if (awaitable == NULL) {
if (PyErr_ExceptionMatches(it->it_stop_exc)) {
PyErr_Clear();
acalliter_exhaust(it);
PyErr_SetNone(PyExc_StopAsyncIteration);
}
else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
/* It would be mistaken for the result of the await (PEP 525). */
_PyErr_FormatFromCause(PyExc_RuntimeError,
"callable raised StopIteration");
}
else if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
/* It would be mistaken for the end of the iteration (PEP 525). */
_PyErr_FormatFromCause(PyExc_RuntimeError,
"callable raised StopAsyncIteration");
}
return -1;
}
aw->aw_wrapped = awaitable;
return 0;
}
/* Turn the exception raised by the wrapped awaitable into the result of
the await. Always returns NULL. */
static PyObject *
acallawaitable_handle_error(acallawaitableobject *aw)
{
acalliterobject *it = acalliterobject_CAST(aw->aw_iterator);
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
PyObject *value;
if (_PyGen_FetchStopIterationValue(&value) < 0) {
return NULL;
}
int ok = 0;
if (it->it_sentinel != NULL) {
ok = PyObject_RichCompareBool(it->it_sentinel, value, Py_EQ);
}
if (ok == 0) {
(void)_PyGen_SetStopIterationValue(value);
}
else if (ok > 0) {
acalliter_exhaust(it);
PyErr_SetNone(PyExc_StopAsyncIteration);
}
Py_DECREF(value);
return NULL;
}
if (PyErr_ExceptionMatches(it->it_stop_exc)) {
PyErr_Clear();
acalliter_exhaust(it);
PyErr_SetNone(PyExc_StopAsyncIteration);
}
else if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
/* It would be mistaken for the end of the iteration (see PEP 525). */
_PyErr_FormatFromCause(PyExc_RuntimeError,
"callable raised StopAsyncIteration");
}
return NULL;
}
static PyObject *
acallawaitable_iternext(PyObject *op)
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
if (aw->aw_wrapped == NULL && acallawaitable_start(aw) < 0) {
return NULL;
}
PyObject *awaitable = awaitable_getiter(op, aw->aw_wrapped);
if (awaitable == NULL) {
return NULL;
}
PyObject *result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);
Py_DECREF(awaitable);
if (result != NULL) {
return result;
}
return acallawaitable_handle_error(aw);
}
static PyObject *
acallawaitable_proxy(acallawaitableobject *aw, char *meth, PyObject *arg)
{
PyObject *awaitable = awaitable_getiter((PyObject *)aw, aw->aw_wrapped);
if (awaitable == NULL) {
return NULL;
}
// When specified, 'arg' may be a tuple (if coming from a METH_VARARGS
// method) or a single object (if coming from a METH_O method).
PyObject *ret = arg == NULL
? PyObject_CallMethod(awaitable, meth, NULL)
: PyObject_CallMethod(awaitable, meth, "O", arg);
Py_DECREF(awaitable);
if (ret != NULL) {
return ret;
}
return acallawaitable_handle_error(aw);
}
static PyObject *
acallawaitable_send(PyObject *op, PyObject *arg)
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
if (aw->aw_wrapped == NULL && acallawaitable_start(aw) < 0) {
return NULL;
}
return acallawaitable_proxy(aw, "send", arg);
}
static PyObject *
acallawaitable_throw(PyObject *op, PyObject *args)
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
if (aw->aw_wrapped == NULL) {
/* Not started, so the exception is raised at the point of the
await, as for a not started coroutine. */
PyObject *typ, *val = NULL, *tb = NULL;
if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) {
return NULL;
}
aw->aw_closed = true;
(void)_PyGen_SetException(typ, val, tb);
return NULL;
}
return acallawaitable_proxy(aw, "throw", args);
}
static PyObject *
acallawaitable_close(PyObject *op, PyObject *Py_UNUSED(dummy))
{
acallawaitableobject *aw = acallawaitableobject_CAST(op);
if (aw->aw_wrapped == NULL) {
/* Not started, so there is nothing to close. */
aw->aw_closed = true;
Py_RETURN_NONE;
}
PyObject *result = acallawaitable_proxy(aw, "close", NULL);
aw->aw_closed = true;
return result;
}
static PyMethodDef acallawaitable_methods[] = {
{"send", acallawaitable_send, METH_O, send_doc},
{"throw", acallawaitable_throw, METH_VARARGS, throw_doc},
{"close", acallawaitable_close, METH_NOARGS, close_doc},
{NULL, NULL} /* Sentinel */
};
static PyAsyncMethods acallawaitable_as_async = {
PyObject_SelfIter, /* am_await */
0, /* am_aiter */
0, /* am_anext */
0, /* am_send */
};
PyTypeObject _PyACallIterAwaitable_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "async_callable_iterator_awaitable",
.tp_basicsize = sizeof(acallawaitableobject),
.tp_dealloc = acallawaitable_dealloc,
.tp_as_async = &acallawaitable_as_async,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = acallawaitable_traverse,
.tp_iter = PyObject_SelfIter,
.tp_iternext = acallawaitable_iternext,
.tp_methods = acallawaitable_methods,
};
-4
View File
@@ -2533,8 +2533,6 @@ _PyObject_FiniState(PyInterpreterState *interp)
}
extern PyTypeObject _PyACallIter_Type;
extern PyTypeObject _PyACallIterAwaitable_Type;
extern PyTypeObject _PyLegacyEventHandler_Type;
extern PyTypeObject _PyLineIterator;
extern PyTypeObject _PyMemoryIter_Type;
@@ -2627,8 +2625,6 @@ static PyTypeObject* static_types[_Py_NUM_MANAGED_PREINITIALIZED_TYPES] = {
&PyWrapperDescr_Type,
&PyZip_Type,
&Py_GenericAliasType,
&_PyACallIter_Type,
&_PyACallIterAwaitable_Type,
&_PyAsyncGenASend_Type,
&_PyAsyncGenAThrow_Type,
&_PyAsyncGenWrappedValue_Type,
-33
View File
@@ -1931,38 +1931,6 @@ builtin_iter_impl(PyObject *module, PyObject *object, PyObject *stop_value,
}
/*[clinic input]
@text_signature "($module, object, /, [stop_value], *, stop_exception=StopAsyncIteration)"
aiter as builtin_aiter
object: object
/
stop_value: object = NULL
*
stop_exception: object = NULL
Return an AsyncIterator for an AsyncIterable object.
In the second form, the callable is called and its result is awaited
until it returns the stop value or raises the specified exception.
[clinic start generated code]*/
static PyObject *
builtin_aiter_impl(PyObject *module, PyObject *object, PyObject *stop_value,
PyObject *stop_exception)
/*[clinic end generated code: output=2865edb3fbc45693 input=2adb37d12adafd0c]*/
{
if (stop_value == NULL && stop_exception == NULL) {
return PyObject_GetAIter(object);
}
if (!PyCallable_Check(object)) {
PyErr_SetString(PyExc_TypeError,
"aiter(): the first argument must be callable");
return NULL;
}
return _PyACallIter_New(object, stop_value, stop_exception);
}
/*[clinic input]
len as builtin_len
@@ -3455,7 +3423,6 @@ static PyMethodDef builtin_methods[] = {
BUILTIN_ISINSTANCE_METHODDEF
BUILTIN_ISSUBCLASS_METHODDEF
BUILTIN_ITER_METHODDEF
BUILTIN_AITER_METHODDEF
BUILTIN_LEN_METHODDEF
BUILTIN_LOCALS_METHODDEF
{"max", _PyCFunction_CAST(builtin_max), METH_FASTCALL | METH_KEYWORDS, max_doc},
+1 -81
View File
@@ -970,86 +970,6 @@ exit:
return return_value;
}
PyDoc_STRVAR(builtin_aiter__doc__,
"aiter($module, object, /, [stop_value], *, stop_exception=StopAsyncIteration)\n"
"--\n"
"\n"
"Return an AsyncIterator for an AsyncIterable object.\n"
"\n"
"In the second form, the callable is called and its result is awaited\n"
"until it returns the stop value or raises the specified exception.");
#define BUILTIN_AITER_METHODDEF \
{"aiter", _PyCFunction_CAST(builtin_aiter), METH_FASTCALL|METH_KEYWORDS, builtin_aiter__doc__},
static PyObject *
builtin_aiter_impl(PyObject *module, PyObject *object, PyObject *stop_value,
PyObject *stop_exception);
static PyObject *
builtin_aiter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 2
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
Py_hash_t ob_hash;
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
.ob_item = { &_Py_ID(stop_value), &_Py_ID(stop_exception), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"", "stop_value", "stop_exception", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "aiter",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[3];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *object;
PyObject *stop_value = NULL;
PyObject *stop_exception = NULL;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
/*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!args) {
goto exit;
}
object = args[0];
if (!noptargs) {
goto skip_optional_pos;
}
if (args[1]) {
stop_value = args[1];
if (!--noptargs) {
goto skip_optional_pos;
}
}
skip_optional_pos:
if (!noptargs) {
goto skip_optional_kwonly;
}
stop_exception = args[2];
skip_optional_kwonly:
return_value = builtin_aiter_impl(module, object, stop_value, stop_exception);
exit:
return return_value;
}
PyDoc_STRVAR(builtin_len__doc__,
"len($module, obj, /)\n"
"--\n"
@@ -1575,4 +1495,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
/*[clinic end generated code: output=ac4800dc25f87f6e input=a9049054013a1b77]*/
/*[clinic end generated code: output=1dacff294aaa7485 input=a9049054013a1b77]*/
@@ -58,8 +58,6 @@ Objects/genobject.c - _PyCoroWrapper_Type -
Objects/interpolationobject.c - _PyInterpolation_Type -
Objects/iterobject.c - PyCallIter_Type -
Objects/iterobject.c - PySeqIter_Type -
Objects/iterobject.c - _PyACallIter_Type -
Objects/iterobject.c - _PyACallIterAwaitable_Type -
Objects/lazyimportobject.c - PyLazyImport_Type -
Objects/listobject.c - PyListIter_Type -
Objects/listobject.c - PyListRevIter_Type -
@@ -74,8 +72,6 @@ Objects/moduleobject.c - PyModule_Type -
Objects/namespaceobject.c - _PyNamespace_Type -
Objects/object.c - _PyNone_Type -
Objects/object.c - _PyNotImplemented_Type -
Objects/object.c - _PyACallIter_Type -
Objects/object.c - _PyACallIterAwaitable_Type -
Objects/odictobject.c - PyODictItems_Type -
Objects/odictobject.c - PyODictIter_Type -
Objects/odictobject.c - PyODictKeys_Type -
1 filename funcname name reason
58 Objects/iterobject.c Objects/listobject.c - _PyACallIterAwaitable_Type PyListIter_Type -
59 Objects/lazyimportobject.c Objects/listobject.c - PyLazyImport_Type PyListRevIter_Type -
60 Objects/listobject.c - PyListIter_Type PyList_Type -
Objects/listobject.c - PyListRevIter_Type -
Objects/listobject.c - PyList_Type -
61 Objects/longobject.c - PyLong_Type -
62 Objects/memoryobject.c - PyMemoryView_Type -
63 Objects/memoryobject.c - _PyManagedBuffer_Type -
72 Objects/object.c Objects/odictobject.c - _PyACallIterAwaitable_Type PyODictIter_Type -
73 Objects/odictobject.c - PyODictItems_Type PyODictKeys_Type -
74 Objects/odictobject.c - PyODictIter_Type PyODictValues_Type -
Objects/odictobject.c - PyODictKeys_Type -
Objects/odictobject.c - PyODictValues_Type -
75 Objects/odictobject.c - PyODict_Type -
76 Objects/picklebufobject.c - PyPickleBuffer_Type -
77 Objects/rangeobject.c - PyLongRangeIter_Type -