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