From 87872bcf979040979f646e6fc4e4106821a20dec Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sun, 13 Sep 2026 09:59:06 +0530 Subject: [PATCH] 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. --- Include/internal/pycore_interp_structs.h | 2 +- Include/internal/pycore_iterobject.h | 8 - Lib/_pybuiltins.py | 87 +++- Lib/test/test_asyncio/test_graph.py | 117 ++++++ Lib/test/test_inspect/test_inspect.py | 2 +- ...09-13-10-00-00.gh-issue-157361.aiterpy.rst | 5 + Objects/iterobject.c | 378 ------------------ Objects/object.c | 4 - Python/bltinmodule.c | 33 -- Python/clinic/bltinmodule.c.h | 82 +--- Tools/c-analyzer/cpython/globals-to-fix.tsv | 4 - 11 files changed, 211 insertions(+), 511 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-00-00.gh-issue-157361.aiterpy.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 6c907e0cf79894..0623adce693d46 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -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 diff --git a/Include/internal/pycore_iterobject.h b/Include/internal/pycore_iterobject.h index 90b444976e2f19..b02eaa83b53514 100644 --- a/Include/internal/pycore_iterobject.h +++ b/Include/internal/pycore_iterobject.h @@ -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 diff --git a/Lib/_pybuiltins.py b/Lib/_pybuiltins.py index 01da295f53d0c7..dfcbeb57d07ebf 100644 --- a/Lib/_pybuiltins.py +++ b/Lib/_pybuiltins.py @@ -5,11 +5,96 @@ 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. diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py index 1326ef50c149b0..7f6e0cc477774e 100644 --- a/Lib/test/test_asyncio/test_graph.py +++ b/Lib/test/test_asyncio/test_graph.py @@ -148,6 +148,45 @@ async def main(): 'async generator CallStackTestBase.test_stack_async_gen..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', + [ + '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 @@ async def main(): [] ]) + 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', + [ + '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', + [ + '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. diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 25276fc40cb028..ea7a0ae458d2d0 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6174,7 +6174,7 @@ def test_builtins_have_signatures(self): "next", "vars"} no_signature |= needs_groups # These have unrepresentable parameter default values of NULL - unsupported_signature = {"aiter", "iter"} + unsupported_signature = {"iter"} # These need *args support in Argument Clinic needs_varargs = {"min", "max", "__build_class__"} no_signature |= needs_varargs diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-00-00.gh-issue-157361.aiterpy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-00-00.gh-issue-157361.aiterpy.rst new file mode 100644 index 00000000000000..b0a3c584da911f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-00-00.gh-issue-157361.aiterpy.rst @@ -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. diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 2d5e3709a27dfb..fcef1cc9c30143 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -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, -}; diff --git a/Objects/object.c b/Objects/object.c index e3f29b71301695..5b06916d96d7d1 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2520,8 +2520,6 @@ _PyObject_FiniState(PyInterpreterState *interp) } -extern PyTypeObject _PyACallIter_Type; -extern PyTypeObject _PyACallIterAwaitable_Type; extern PyTypeObject _PyLegacyEventHandler_Type; extern PyTypeObject _PyLineIterator; extern PyTypeObject _PyMemoryIter_Type; @@ -2614,8 +2612,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, diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 965cf20fe61778..cbd11ef6a66314 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1928,38 +1928,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 @@ -3448,7 +3416,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}, diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 5858ca9ff88ec2..9a22ee4af16e75 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -931,86 +931,6 @@ builtin_iter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject 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" @@ -1501,4 +1421,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=b56739f2e13f616a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c9eee63c37544586 input=a9049054013a1b77]*/ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 67ced170243e4a..251af27908cfc3 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -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 -