mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
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:
1 parent
4c73f50ca9
commit
cb12c5e514
11 files changed
+211
-511
No files matched your search
@@ -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,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
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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},
|
||||
|
||||
Generated
+1
-81
@@ -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 -
|
||||
|
||||
|
Reference in new issue
Block a user