@@ -11,7 +11,7 @@ msgid ""
1111msgstr ""
1212"Project-Id-Version : Python 3.13\n "
1313"Report-Msgid-Bugs-To : \n "
14- "POT-Creation-Date : 2026-03-13 15:26 +0000\n "
14+ "POT-Creation-Date : 2026-03-21 15:06 +0000\n "
1515"PO-Revision-Date : 2025-09-15 01:03+0000\n "
1616"Last-Translator : python-doc bot, 2025\n "
1717"Language-Team : Polish (https://app.transifex.com/python-doc/teams/5390/pl/)\n "
@@ -316,3 +316,263 @@ msgid ""
316316" raise Exception(\" not consumed\" )\n"
317317"Exception: not consumed"
318318msgstr ""
319+
320+ msgid "Asynchronous generators best practices"
321+ msgstr ""
322+
323+ msgid ""
324+ "Writing correct and efficient asyncio code requires awareness of certain "
325+ "pitfalls. This section outlines essential best practices that can save you "
326+ "hours of debugging."
327+ msgstr ""
328+
329+ msgid "Close asynchronous generators explicitly"
330+ msgstr ""
331+
332+ msgid ""
333+ "It is recommended to manually close the :term:`asynchronous generator "
334+ "<asynchronous generator iterator>`. If a generator exits early - for "
335+ "example, due to an exception raised in the body of an ``async for`` loop - "
336+ "its asynchronous cleanup code may run in an unexpected context. This can "
337+ "occur after the tasks it depends on have completed, or during the event loop "
338+ "shutdown when the async-generator's garbage collection hook is called."
339+ msgstr ""
340+
341+ msgid ""
342+ "To avoid this, explicitly close the generator by calling its :meth:`~agen."
343+ "aclose` method, or use the :func:`contextlib.aclosing` context manager::"
344+ msgstr ""
345+
346+ msgid ""
347+ "import asyncio\n"
348+ "import contextlib\n"
349+ "\n"
350+ "async def gen():\n"
351+ " yield 1\n"
352+ " yield 2\n"
353+ "\n"
354+ "async def func():\n"
355+ " async with contextlib.aclosing(gen()) as g:\n"
356+ " async for x in g:\n"
357+ " break # Don't iterate until the end\n"
358+ "\n"
359+ "asyncio.run(func())"
360+ msgstr ""
361+
362+ msgid ""
363+ "As noted above, the cleanup code for these asynchronous generators is "
364+ "deferred. The following example demonstrates that the finalization of an "
365+ "asynchronous generator can occur in an unexpected order::"
366+ msgstr ""
367+
368+ msgid ""
369+ "import asyncio\n"
370+ "work_done = False\n"
371+ "\n"
372+ "async def cursor():\n"
373+ " try:\n"
374+ " yield 1\n"
375+ " finally:\n"
376+ " assert work_done\n"
377+ "\n"
378+ "async def rows():\n"
379+ " global work_done\n"
380+ " try:\n"
381+ " yield 2\n"
382+ " finally:\n"
383+ " await asyncio.sleep(0.1) # immitate some async work\n"
384+ " work_done = True\n"
385+ "\n"
386+ "\n"
387+ "async def main():\n"
388+ " async for c in cursor():\n"
389+ " async for r in rows():\n"
390+ " break\n"
391+ " break\n"
392+ "\n"
393+ "asyncio.run(main())"
394+ msgstr ""
395+
396+ msgid "For this example, we get the following output::"
397+ msgstr ""
398+
399+ msgid ""
400+ "unhandled exception during asyncio.run() shutdown\n"
401+ "task: <Task finished name='Task-3' coro=<<async_generator_athrow without "
402+ "__name__>()> exception=AssertionError()>\n"
403+ "Traceback (most recent call last):\n"
404+ " File \" example.py\" , line 6, in cursor\n"
405+ " yield 1\n"
406+ "asyncio.exceptions.CancelledError\n"
407+ "\n"
408+ "During handling of the above exception, another exception occurred:\n"
409+ "\n"
410+ "Traceback (most recent call last):\n"
411+ " File \" example.py\" , line 8, in cursor\n"
412+ " assert work_done\n"
413+ " ^^^^^^^^^\n"
414+ "AssertionError"
415+ msgstr ""
416+
417+ msgid ""
418+ "The ``cursor()`` asynchronous generator was finalized before the ``rows`` "
419+ "generator - an unexpected behavior."
420+ msgstr ""
421+
422+ msgid ""
423+ "The example can be fixed by explicitly closing the ``cursor`` and ``rows`` "
424+ "async-generators::"
425+ msgstr ""
426+
427+ msgid ""
428+ "async def main():\n"
429+ " async with contextlib.aclosing(cursor()) as cursor_gen:\n"
430+ " async for c in cursor_gen:\n"
431+ " async with contextlib.aclosing(rows()) as rows_gen:\n"
432+ " async for r in rows_gen:\n"
433+ " break\n"
434+ " break"
435+ msgstr ""
436+
437+ msgid "Create asynchronous generators only when the event loop is running"
438+ msgstr ""
439+
440+ msgid ""
441+ "It is recommended to create :term:`asynchronous generators <asynchronous "
442+ "generator iterator>` only after the event loop has been created."
443+ msgstr ""
444+
445+ msgid ""
446+ "To ensure that asynchronous generators close reliably, the event loop uses "
447+ "the :func:`sys.set_asyncgen_hooks` function to register callback functions. "
448+ "These callbacks update the list of running asynchronous generators to keep "
449+ "it in a consistent state."
450+ msgstr ""
451+
452+ msgid ""
453+ "When the :meth:`loop.shutdown_asyncgens() <asyncio.loop.shutdown_asyncgens>` "
454+ "function is called, the running generators are stopped gracefully and the "
455+ "list is cleared."
456+ msgstr ""
457+
458+ msgid ""
459+ "The asynchronous generator invokes the corresponding system hook during its "
460+ "first iteration. At the same time, the generator records that the hook has "
461+ "been called and does not call it again."
462+ msgstr ""
463+
464+ msgid ""
465+ "Therefore, if iteration begins before the event loop is created, the event "
466+ "loop will not be able to add the generator to its list of active generators "
467+ "because the hooks are set after the generator attempts to call them. "
468+ "Consequently, the event loop will not be able to terminate the generator if "
469+ "necessary."
470+ msgstr ""
471+
472+ msgid "Consider the following example::"
473+ msgstr ""
474+
475+ msgid ""
476+ "import asyncio\n"
477+ "\n"
478+ "async def agenfn():\n"
479+ " try:\n"
480+ " yield 10\n"
481+ " finally:\n"
482+ " await asyncio.sleep(0)\n"
483+ "\n"
484+ "\n"
485+ "with asyncio.Runner() as runner:\n"
486+ " agen = agenfn()\n"
487+ " print(runner.run(anext(agen)))\n"
488+ " del agen"
489+ msgstr ""
490+
491+ msgid ""
492+ "10\n"
493+ "Exception ignored while closing generator <async_generator object agenfn at "
494+ "0x000002F71CD10D70>:\n"
495+ "Traceback (most recent call last):\n"
496+ " File \" example.py\" , line 13, in <module>\n"
497+ " del agen\n"
498+ " ^^^^\n"
499+ "RuntimeError: async generator ignored GeneratorExit"
500+ msgstr ""
501+
502+ msgid "This example can be fixed as follows::"
503+ msgstr ""
504+
505+ msgid ""
506+ "import asyncio\n"
507+ "\n"
508+ "async def agenfn():\n"
509+ " try:\n"
510+ " yield 10\n"
511+ " finally:\n"
512+ " await asyncio.sleep(0)\n"
513+ "\n"
514+ "async def main():\n"
515+ " agen = agenfn()\n"
516+ " print(await anext(agen))\n"
517+ " del agen\n"
518+ "\n"
519+ "asyncio.run(main())"
520+ msgstr ""
521+
522+ msgid "Avoid concurrent iteration and closure of the same generator"
523+ msgstr ""
524+
525+ msgid ""
526+ "Async generators may be reentered while another :meth:`~agen.__anext__` / :"
527+ "meth:`~agen.athrow` / :meth:`~agen.aclose` call is in progress. This may "
528+ "lead to an inconsistent state of the async generator and can cause errors."
529+ msgstr ""
530+
531+ msgid "Let's consider the following example::"
532+ msgstr ""
533+
534+ msgid ""
535+ "import asyncio\n"
536+ "\n"
537+ "async def consumer():\n"
538+ " for idx in range(100):\n"
539+ " await asyncio.sleep(0)\n"
540+ " message = yield idx\n"
541+ " print('received', message)\n"
542+ "\n"
543+ "async def amain():\n"
544+ " agenerator = consumer()\n"
545+ " await agenerator.asend(None)\n"
546+ "\n"
547+ " fa = asyncio.create_task(agenerator.asend('A'))\n"
548+ " fb = asyncio.create_task(agenerator.asend('B'))\n"
549+ " await fa\n"
550+ " await fb\n"
551+ "\n"
552+ "asyncio.run(amain())"
553+ msgstr ""
554+
555+ msgid ""
556+ "received A\n"
557+ "Traceback (most recent call last):\n"
558+ " File \" test.py\" , line 38, in <module>\n"
559+ " asyncio.run(amain())\n"
560+ " ~~~~~~~~~~~^^^^^^^^^\n"
561+ " File \" Lib/asyncio/runners.py\" , line 204, in run\n"
562+ " return runner.run(main)\n"
563+ " ~~~~~~~~~~^^^^^^\n"
564+ " File \" Lib/asyncio/runners.py\" , line 127, in run\n"
565+ " return self._loop.run_until_complete(task)\n"
566+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^\n"
567+ " File \" Lib/asyncio/base_events.py\" , line 719, in run_until_complete\n"
568+ " return future.result()\n"
569+ " ~~~~~~~~~~~~~^^\n"
570+ " File \" test.py\" , line 36, in amain\n"
571+ " await fb\n"
572+ "RuntimeError: anext(): asynchronous generator is already running"
573+ msgstr ""
574+
575+ msgid ""
576+ "Therefore, it is recommended to avoid using asynchronous generators in "
577+ "parallel tasks or across multiple event loops."
578+ msgstr ""
0 commit comments