Skip to content

Commit 97ccca0

Browse files
Add example for broken finalizers
1 parent 5256508 commit 97ccca0

1 file changed

Lines changed: 64 additions & 3 deletions

File tree

Doc/library/asyncio-dev.rst

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,67 @@ context manager::
286286

287287
asyncio.run(func())
288288

289+
As said above, the cleanup code for these asynchronous generators is defered.
290+
We can construct an example program to show that the finalization of the
291+
asynchronous generator is executed in an unexpected order::
292+
293+
import asyncio
294+
work_done = False
295+
296+
async def cursor():
297+
try:
298+
yield 1
299+
finally:
300+
assert work_done
301+
302+
async def rows():
303+
global work_done
304+
try:
305+
yield 2
306+
finally:
307+
await asyncio.sleep(0.1) # immitate some async work
308+
work_done = True
309+
310+
311+
async def main():
312+
async for c in cursor():
313+
async for r in rows():
314+
break
315+
break
316+
317+
asyncio.run(main())
318+
319+
For this example we get the following output::
320+
321+
unhandled exception during asyncio.run() shutdown
322+
task: <Task finished name='Task-3' coro=<<async_generator_athrow without __name__>()> exception=AssertionError()>
323+
Traceback (most recent call last):
324+
File "example.py", line 6, in cursor
325+
yield 1
326+
asyncio.exceptions.CancelledError
327+
328+
During handling of the above exception, another exception occurred:
329+
330+
Traceback (most recent call last):
331+
File "example.py", line 8, in cursor
332+
assert work_done
333+
^^^^^^^^^
334+
AssertionError
335+
336+
The ``cursor()`` asynchronous generator was finalized before the ``rows``
337+
generator, which we did not expect.
338+
339+
Example can be fixed by explicitly closing the
340+
``cursor`` and ``rows`` async-generators::
341+
342+
async def main():
343+
async with contextlib.aclosing(cursor()) as cursor_gen:
344+
async for c in cursor_gen:
345+
async with contextlib.aclosing(rows()) as rows_gen:
346+
async for r in rows_gen:
347+
break
348+
break
349+
289350

290351
Only create a generator when a loop is already running
291352
------------------------------------------------------
@@ -349,13 +410,13 @@ Output::
349410
File "test.py", line 38, in <module>
350411
asyncio.run(amain())
351412
~~~~~~~~~~~^^^^^^^^^
352-
File "Lib\asyncio\runners.py", line 204, in run
413+
File "Lib/asyncio/runners.py", line 204, in run
353414
return runner.run(main)
354415
~~~~~~~~~~^^^^^^
355-
File "Lib\asyncio\runners.py", line 127, in run
416+
File "Lib/asyncio/runners.py", line 127, in run
356417
return self._loop.run_until_complete(task)
357418
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
358-
File "Lib\asyncio\base_events.py", line 719, in run_until_complete
419+
File "Lib/asyncio/base_events.py", line 719, in run_until_complete
359420
return future.result()
360421
~~~~~~~~~~~~~^^
361422
File "test.py", line 36, in amain

0 commit comments

Comments
 (0)