Skip to content

Commit 9bdfb4b

Browse files
Address review comments
Reword the aiter() documentation like the iter() one, avoid using the name "queue" for two different things in the example, describe every field of the iterator structs separately, add calliter_exhaust() for symmetry with acalliter_exhaust(), use bool and designated initializers in new code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5877437 commit 9bdfb4b

3 files changed

Lines changed: 57 additions & 78 deletions

File tree

Doc/library/functions.rst

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,12 @@ are always available. They are listed here in alphabetical order.
6868
aiter(callable, /, stop_value, *, stop_exception=StopAsyncIteration)
6969
aiter(callable, /, *, stop_exception)
7070
71-
Return an :term:`asynchronous iterator` for an :term:`asynchronous iterable`.
72-
Equivalent to calling ``x.__aiter__()``.
71+
Return an :term:`asynchronous iterator` object.
72+
The first argument is interpreted very differently
73+
depending on the presence of the other arguments.
74+
Without other arguments,
75+
the single argument must be an :term:`asynchronous iterable`,
76+
and the result is equivalent to calling ``x.__aiter__()``.
7377

7478
If *stop_value* or *stop_exception* is given,
7579
then the first argument must be a callable object.
@@ -86,8 +90,9 @@ are always available. They are listed here in alphabetical order.
8690
*stop_exception* is an exception class or a tuple of exception classes.
8791
If *stop_value* is not specified,
8892
the iteration stops only when the callable raises an exception.
89-
If the callable raises :exc:`StopAsyncIteration` which does not match
90-
*stop_exception*, it is replaced with a :exc:`RuntimeError`,
93+
If the callable raises :exc:`StopAsyncIteration`
94+
which does not match *stop_exception*,
95+
it is replaced with a :exc:`RuntimeError`,
9196
as for asynchronous generators (see :pep:`525`).
9297

9398
For example, reading fixed-size chunks from an asynchronous stream
@@ -1195,13 +1200,15 @@ are always available. They are listed here in alphabetical order.
11951200
will call *callable* with no arguments for each call to its
11961201
:meth:`~iterator.__next__` method; if the value returned is equal to
11971202
*stop_value*, or if the call raises an exception matching *stop_exception*,
1198-
:exc:`StopIteration` will be raised, otherwise the value will be returned.
1203+
:exc:`StopIteration` will be raised, otherwise the value will
1204+
be returned.
11991205

12001206
*stop_exception* is an exception class or a tuple of exception classes.
12011207
If *stop_value* is not specified,
12021208
the iteration stops only when the callable raises an exception.
1203-
If the callable raises :exc:`StopIteration` which does not match
1204-
*stop_exception*, it is replaced with a :exc:`RuntimeError`,
1209+
If the callable raises :exc:`StopIteration`
1210+
which does not match *stop_exception*,
1211+
it is replaced with a :exc:`RuntimeError`,
12051212
as for generators (see :pep:`479`).
12061213

12071214
See also :ref:`typeiter`.
@@ -1215,16 +1222,18 @@ are always available. They are listed here in alphabetical order.
12151222
for block in iter(partial(f.read, 64), b''):
12161223
process_block(block)
12171224

1218-
*stop_exception* is useful for callables which report exhaustion by raising an
1219-
exception instead of returning a special value.
1225+
*stop_exception* is useful for callables
1226+
which report exhaustion by raising an exception
1227+
instead of returning a special value.
12201228
For example, draining a queue::
12211229

1222-
from queue import Empty
1223-
for item in iter(queue.get_nowait, stop_exception=Empty):
1230+
import queue
1231+
for item in iter(input_queue.get_nowait, stop_exception=queue.Empty):
12241232
process_item(item)
12251233

12261234
.. versionchanged:: next
1227-
Added the *stop_exception* parameter and allowed passing *stop_value* by keyword.
1235+
Added the *stop_exception* parameter
1236+
and allowed passing *stop_value* by keyword.
12281237

12291238

12301239
.. function:: len(object, /)

Lib/test/test_iter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def __call__(self):
9393
i = self.i
9494
self.i = i + 1
9595
if i > 100:
96-
raise IndexError # Emergency stop
96+
raise IndexError # stops the iteration
9797
return i
9898

9999
class EmptyIterClass:

Objects/iterobject.c

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,8 @@ PyTypeObject PySeqIter_Type = {
188188

189189
typedef struct {
190190
PyObject_HEAD
191-
/* Both are set to NULL when the iterator is exhausted */
192-
PyObject *it_callable;
193-
PyObject *it_sentinel; /* can be NULL */
191+
PyObject *it_callable; /* set to NULL when the iterator is exhausted */
192+
PyObject *it_sentinel; /* can be NULL, and is when exhausted */
194193
PyObject *it_stop_exc; /* never NULL */
195194
} calliterobject;
196195

@@ -220,6 +219,13 @@ PyCallIter_New(PyObject *callable, PyObject *sentinel)
220219
return _PyCallIter_NewEx(callable, sentinel, NULL);
221220
}
222221

222+
static void
223+
calliter_exhaust(calliterobject *it)
224+
{
225+
Py_CLEAR(it->it_callable);
226+
Py_CLEAR(it->it_sentinel);
227+
}
228+
223229
static void
224230
calliter_dealloc(PyObject *op)
225231
{
@@ -263,14 +269,12 @@ calliter_iternext(PyObject *op)
263269
}
264270

265271
if (ok > 0) {
266-
Py_CLEAR(it->it_callable);
267-
Py_CLEAR(it->it_sentinel);
272+
calliter_exhaust(it);
268273
}
269274
}
270275
else if (PyErr_ExceptionMatches(it->it_stop_exc)) {
271276
PyErr_Clear();
272-
Py_CLEAR(it->it_callable);
273-
Py_CLEAR(it->it_sentinel);
277+
calliter_exhaust(it);
274278
}
275279
else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
276280
/* It would be mistaken for the end of the iteration (see PEP 479). */
@@ -620,9 +624,8 @@ PyAnextAwaitable_New(PyObject *awaitable, PyObject *default_value)
620624

621625
typedef struct {
622626
PyObject_HEAD
623-
/* Both are set to NULL when the iterator is exhausted */
624-
PyObject *it_callable;
625-
PyObject *it_sentinel; /* can be NULL */
627+
PyObject *it_callable; /* set to NULL when the iterator is exhausted */
628+
PyObject *it_sentinel; /* can be NULL, and is when exhausted */
626629
PyObject *it_stop_exc; /* never NULL */
627630
} acalliterobject;
628631

@@ -634,7 +637,7 @@ typedef struct {
634637
PyObject_HEAD
635638
PyObject *aw_iterator; /* the iterator which created this object */
636639
PyObject *aw_wrapped; /* the awaitable returned by the callable */
637-
char aw_closed;
640+
bool aw_closed;
638641
} acallawaitableobject;
639642

640643
#define acallawaitableobject_CAST(op) ((acallawaitableobject *)(op))
@@ -704,28 +707,13 @@ static PyAsyncMethods acalliter_as_async = {
704707

705708
PyTypeObject _PyACallIter_Type = {
706709
PyVarObject_HEAD_INIT(&PyType_Type, 0)
707-
"async_callable_iterator", /* tp_name */
708-
sizeof(acalliterobject), /* tp_basicsize */
709-
0, /* tp_itemsize */
710-
/* methods */
711-
acalliter_dealloc, /* tp_dealloc */
712-
0, /* tp_vectorcall_offset */
713-
0, /* tp_getattr */
714-
0, /* tp_setattr */
715-
&acalliter_as_async, /* tp_as_async */
716-
0, /* tp_repr */
717-
0, /* tp_as_number */
718-
0, /* tp_as_sequence */
719-
0, /* tp_as_mapping */
720-
0, /* tp_hash */
721-
0, /* tp_call */
722-
0, /* tp_str */
723-
PyObject_GenericGetAttr, /* tp_getattro */
724-
0, /* tp_setattro */
725-
0, /* tp_as_buffer */
726-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
727-
0, /* tp_doc */
728-
acalliter_traverse, /* tp_traverse */
710+
.tp_name = "async_callable_iterator",
711+
.tp_basicsize = sizeof(acalliterobject),
712+
.tp_dealloc = acalliter_dealloc,
713+
.tp_as_async = &acalliter_as_async,
714+
.tp_getattro = PyObject_GenericGetAttr,
715+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
716+
.tp_traverse = acalliter_traverse,
729717
};
730718

731719
/* -------------------------------------- */
@@ -740,7 +728,7 @@ acallawaitable_new(PyObject *iterator)
740728
}
741729
aw->aw_iterator = Py_NewRef(iterator);
742730
aw->aw_wrapped = NULL;
743-
aw->aw_closed = 0;
731+
aw->aw_closed = false;
744732
_PyObject_GC_TRACK(aw);
745733
return (PyObject *)aw;
746734
}
@@ -903,7 +891,7 @@ acallawaitable_throw(PyObject *op, PyObject *args)
903891
if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) {
904892
return NULL;
905893
}
906-
aw->aw_closed = 1;
894+
aw->aw_closed = true;
907895
(void)_PyGen_SetException(typ, val, tb);
908896
return NULL;
909897
}
@@ -917,11 +905,11 @@ acallawaitable_close(PyObject *op, PyObject *Py_UNUSED(dummy))
917905

918906
if (aw->aw_wrapped == NULL) {
919907
/* Not started, so there is nothing to close. */
920-
aw->aw_closed = 1;
908+
aw->aw_closed = true;
921909
Py_RETURN_NONE;
922910
}
923911
PyObject *result = acallawaitable_proxy(aw, "close", NULL);
924-
aw->aw_closed = 1;
912+
aw->aw_closed = true;
925913
return result;
926914
}
927915

@@ -941,32 +929,14 @@ static PyAsyncMethods acallawaitable_as_async = {
941929

942930
PyTypeObject _PyACallIterAwaitable_Type = {
943931
PyVarObject_HEAD_INIT(&PyType_Type, 0)
944-
"async_callable_iterator_awaitable", /* tp_name */
945-
sizeof(acallawaitableobject), /* tp_basicsize */
946-
0, /* tp_itemsize */
947-
/* methods */
948-
acallawaitable_dealloc, /* tp_dealloc */
949-
0, /* tp_vectorcall_offset */
950-
0, /* tp_getattr */
951-
0, /* tp_setattr */
952-
&acallawaitable_as_async, /* tp_as_async */
953-
0, /* tp_repr */
954-
0, /* tp_as_number */
955-
0, /* tp_as_sequence */
956-
0, /* tp_as_mapping */
957-
0, /* tp_hash */
958-
0, /* tp_call */
959-
0, /* tp_str */
960-
PyObject_GenericGetAttr, /* tp_getattro */
961-
0, /* tp_setattro */
962-
0, /* tp_as_buffer */
963-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
964-
0, /* tp_doc */
965-
acallawaitable_traverse, /* tp_traverse */
966-
0, /* tp_clear */
967-
0, /* tp_richcompare */
968-
0, /* tp_weaklistoffset */
969-
PyObject_SelfIter, /* tp_iter */
970-
acallawaitable_iternext, /* tp_iternext */
971-
acallawaitable_methods, /* tp_methods */
932+
.tp_name = "async_callable_iterator_awaitable",
933+
.tp_basicsize = sizeof(acallawaitableobject),
934+
.tp_dealloc = acallawaitable_dealloc,
935+
.tp_as_async = &acallawaitable_as_async,
936+
.tp_getattro = PyObject_GenericGetAttr,
937+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
938+
.tp_traverse = acallawaitable_traverse,
939+
.tp_iter = PyObject_SelfIter,
940+
.tp_iternext = acallawaitable_iternext,
941+
.tp_methods = acallawaitable_methods,
972942
};

0 commit comments

Comments
 (0)