File tree
Lib
test
Misc/NEWS.d/next/Core and Builtins
Objects
Original file line numberDiff line numberDiff line change@@ -187,7 +187,7 @@ async def __aexit__(self, typ, value, traceback):
187187# in this implementation
188188try:
189189awaitself.gen.athrow(typ, value, traceback)
190-raiseRuntimeError("generator didn't stop after throw()")
190+raiseRuntimeError("generator didn't stop after athrow()")
191191exceptStopAsyncIterationasexc:
192192returnexcisnotvalue
193193exceptRuntimeErrorasexc:
Original file line numberDiff line numberDiff line change@@ -108,6 +108,31 @@ def sync_iterate(g):
108108res.append(str(type(ex)))
109109returnres
110110111+defasync_iterate(g):
112+res= []
113+whileTrue:
114+an=g.__anext__()
115+try:
116+whileTrue:
117+try:
118+an.__next__()
119+exceptStopIterationasex:
120+ifex.args:
121+res.append(ex.args[0])
122+break
123+else:
124+res.append('EMPTY StopIteration')
125+break
126+exceptStopAsyncIteration:
127+raise
128+exceptExceptionasex:
129+res.append(str(type(ex)))
130+break
131+exceptStopAsyncIteration:
132+res.append('STOP')
133+break
134+returnres
135+111136defasync_iterate(g):
112137res= []
113138whileTrue:
@@ -297,6 +322,37 @@ async def gen():
297322"non-None value .* async generator"):
298323gen().__anext__().send(100)
299324325+deftest_async_gen_exception_11(self):
326+defsync_gen():
327+yield10
328+yield20
329+330+defsync_gen_wrapper():
331+yield1
332+sg=sync_gen()
333+sg.send(None)
334+try:
335+sg.throw(GeneratorExit())
336+exceptGeneratorExit:
337+yield2
338+yield3
339+340+asyncdefasync_gen():
341+yield10
342+yield20
343+344+asyncdefasync_gen_wrapper():
345+yield1
346+asg=async_gen()
347+awaitasg.asend(None)
348+try:
349+awaitasg.athrow(GeneratorExit())
350+exceptGeneratorExit:
351+yield2
352+yield3
353+354+self.compare_generators(sync_gen_wrapper(), async_gen_wrapper())
355+300356deftest_async_gen_api_01(self):
301357asyncdefgen():
302358yield123
Original file line numberDiff line numberDiff line change@@ -36,6 +36,28 @@ async def __aexit__(self, *args):
3636asyncwithmanagerascontext:
3737self.assertIs(manager, context)
383839+@_async_test
40+asyncdeftest_async_gen_propagates_generator_exit(self):
41+# A regression test for https://bugs.python.org/issue33786.
42+43+@asynccontextmanager
44+asyncdefctx():
45+yield
46+47+asyncdefgen():
48+asyncwithctx():
49+yield11
50+51+ret= []
52+exc=ValueError(22)
53+withself.assertRaises(ValueError):
54+asyncwithctx():
55+asyncforvalingen():
56+ret.append(val)
57+raiseexc
58+59+self.assertEqual(ret, [11])
60+3961deftest_exit_is_abstract(self):
4062classMissingAexit(AbstractAsyncContextManager):
4163pass
Original file line numberDiff line numberDiff line change@@ -0,0 +1 @@
1+Fix asynchronous generators to handle GeneratorExit in athrow() correctly
Original file line numberDiff line numberDiff line change@@ -1876,21 +1876,20 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
18761876returnNULL;
1877187718781878check_error:
1879-if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
1879+if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
1880+PyErr_ExceptionMatches(PyExc_GeneratorExit))
1881+ {
18801882o->agt_state=AWAITABLE_STATE_CLOSED;
18811883if (o->agt_args==NULL) {
18821884/* when aclose() is called we don't want to propagate
1883- StopAsyncIteration; just raise StopIteration, signalling
1884- that 'aclose()' is done. */
1885+ StopAsyncIteration or GeneratorExit; just raise
1886+ StopIteration, signalling that this 'aclose()' await
1887+ is done.
1888+ */
18851889PyErr_Clear();
18861890PyErr_SetNone(PyExc_StopIteration);
18871891 }
18881892 }
1889-elseif (PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
1890-o->agt_state=AWAITABLE_STATE_CLOSED;
1891-PyErr_Clear(); /* ignore these errors */
1892-PyErr_SetNone(PyExc_StopIteration);
1893- }
18941893returnNULL;
18951894}
18961895