Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit a96e06d

Browse files
authored
bpo-39386: Prevent double awaiting of async iterator (pythonGH-18081)
1 parent 2c49bec commit a96e06d

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

Lib/test/test_asyncgen.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,42 @@ async def main():
11281128

11291129
self.assertEqual([], messages)
11301130

1131+
def test_async_gen_await_anext_twice(self):
1132+
async def async_iterate():
1133+
yield 1
1134+
yield 2
1135+
1136+
async def run():
1137+
it = async_iterate()
1138+
nxt = it.__anext__()
1139+
await nxt
1140+
with self.assertRaisesRegex(
1141+
RuntimeError,
1142+
r"cannot reuse already awaited __anext__\(\)/asend\(\)"
1143+
):
1144+
await nxt
1145+
1146+
await it.aclose() # prevent unfinished iterator warning
1147+
1148+
self.loop.run_until_complete(run())
1149+
1150+
def test_async_gen_await_aclose_twice(self):
1151+
async def async_iterate():
1152+
yield 1
1153+
yield 2
1154+
1155+
async def run():
1156+
it = async_iterate()
1157+
nxt = it.aclose()
1158+
await nxt
1159+
with self.assertRaisesRegex(
1160+
RuntimeError,
1161+
r"cannot reuse already awaited aclose\(\)/athrow\(\)"
1162+
):
1163+
await nxt
1164+
1165+
self.loop.run_until_complete(run())
1166+
11311167

11321168
if __name__ == "__main__":
11331169
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent double awaiting of async iterator.

Objects/genobject.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,9 @@ async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg)
15181518
PyObject *result;
15191519

15201520
if (o->ags_state == AWAITABLE_STATE_CLOSED) {
1521-
PyErr_SetNone(PyExc_StopIteration);
1521+
PyErr_SetString(
1522+
PyExc_RuntimeError,
1523+
"cannot reuse already awaited __anext__()/asend()");
15221524
return NULL;
15231525
}
15241526

@@ -1561,7 +1563,9 @@ async_gen_asend_throw(PyAsyncGenASend *o, PyObject *args)
15611563
PyObject *result;
15621564

15631565
if (o->ags_state == AWAITABLE_STATE_CLOSED) {
1564-
PyErr_SetNone(PyExc_StopIteration);
1566+
PyErr_SetString(
1567+
PyExc_RuntimeError,
1568+
"cannot reuse already awaited __anext__()/asend()");
15651569
return NULL;
15661570
}
15671571

@@ -1795,7 +1799,9 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
17951799

17961800
if (f == NULL || f->f_stacktop == NULL ||
17971801
o->agt_state == AWAITABLE_STATE_CLOSED) {
1798-
PyErr_SetNone(PyExc_StopIteration);
1802+
PyErr_SetString(
1803+
PyExc_RuntimeError,
1804+
"cannot reuse already awaited aclose()/athrow()");
17991805
return NULL;
18001806
}
18011807

@@ -1917,7 +1923,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *args)
19171923
PyObject *retval;
19181924

19191925
if (o->agt_state == AWAITABLE_STATE_CLOSED) {
1920-
PyErr_SetNone(PyExc_StopIteration);
1926+
PyErr_SetString(
1927+
PyExc_RuntimeError,
1928+
"cannot reuse already awaited aclose()/athrow()");
19211929
return NULL;
19221930
}
19231931

0 commit comments

Comments
 (0)