Skip to content

Commit 82e0b26

Browse files
Deploy preview for PR 1214 🛫
1 parent bee43b5 commit 82e0b26

File tree

607 files changed

+1673
-1203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

607 files changed

+1673
-1203
lines changed

pr-preview/pr-1214/_sources/howto/enum.rst.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ The complete :class:`!Weekday` enum now looks like this::
105105

106106
Now we can find out what today is! Observe::
107107

108-
>>> from datetime import date
109-
>>> Weekday.from_date(date.today()) # doctest: +SKIP
108+
>>> import datetime as dt
109+
>>> Weekday.from_date(dt.date.today()) # doctest: +SKIP
110110
<Weekday.TUESDAY: 2>
111111

112112
Of course, if you're reading this on some other day, you'll see that day instead.
@@ -1538,8 +1538,8 @@ TimePeriod
15381538

15391539
An example to show the :attr:`~Enum._ignore_` attribute in use::
15401540

1541-
>>> from datetime import timedelta
1542-
>>> class Period(timedelta, Enum):
1541+
>>> import datetime as dt
1542+
>>> class Period(dt.timedelta, Enum):
15431543
... "different lengths of time"
15441544
... _ignore_ = 'Period i'
15451545
... Period = vars()

pr-preview/pr-1214/_sources/howto/logging-cookbook.rst.txt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,10 +1549,10 @@ to this (remembering to first import :mod:`concurrent.futures`)::
15491549
for i in range(10):
15501550
executor.submit(worker_process, queue, worker_configurer)
15511551

1552-
Deploying Web applications using Gunicorn and uWSGI
1552+
Deploying web applications using Gunicorn and uWSGI
15531553
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15541554

1555-
When deploying Web applications using `Gunicorn <https://gunicorn.org/>`_ or `uWSGI
1555+
When deploying web applications using `Gunicorn <https://gunicorn.org/>`_ or `uWSGI
15561556
<https://uwsgi-docs.readthedocs.io/en/latest/>`_ (or similar), multiple worker
15571557
processes are created to handle client requests. In such environments, avoid creating
15581558
file-based handlers directly in your web application. Instead, use a
@@ -3619,7 +3619,6 @@ detailed information.
36193619

36203620
.. code-block:: python3
36213621
3622-
import datetime
36233622
import logging
36243623
import random
36253624
import sys
@@ -3854,15 +3853,15 @@ Logging to syslog with RFC5424 support
38543853
Although :rfc:`5424` dates from 2009, most syslog servers are configured by default to
38553854
use the older :rfc:`3164`, which hails from 2001. When ``logging`` was added to Python
38563855
in 2003, it supported the earlier (and only existing) protocol at the time. Since
3857-
RFC5424 came out, as there has not been widespread deployment of it in syslog
3856+
RFC 5424 came out, as there has not been widespread deployment of it in syslog
38583857
servers, the :class:`~logging.handlers.SysLogHandler` functionality has not been
38593858
updated.
38603859

38613860
RFC 5424 contains some useful features such as support for structured data, and if you
38623861
need to be able to log to a syslog server with support for it, you can do so with a
38633862
subclassed handler which looks something like this::
38643863

3865-
import datetime
3864+
import datetime as dt
38663865
import logging.handlers
38673866
import re
38683867
import socket
@@ -3880,7 +3879,7 @@ subclassed handler which looks something like this::
38803879

38813880
def format(self, record):
38823881
version = 1
3883-
asctime = datetime.datetime.fromtimestamp(record.created).isoformat()
3882+
asctime = dt.datetime.fromtimestamp(record.created).isoformat()
38843883
m = self.tz_offset.match(time.strftime('%z'))
38853884
has_offset = False
38863885
if m and time.timezone:

pr-preview/pr-1214/_sources/library/asyncio-dev.rst.txt

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,225 @@ Output in debug mode::
248248
File "../t.py", line 4, in bug
249249
raise Exception("not consumed")
250250
Exception: not consumed
251+
252+
253+
Asynchronous generators best practices
254+
======================================
255+
256+
Writing correct and efficient asyncio code requires awareness of certain pitfalls.
257+
This section outlines essential best practices that can save you hours of debugging.
258+
259+
260+
Close asynchronous generators explicitly
261+
----------------------------------------
262+
263+
It is recommended to manually close the
264+
:term:`asynchronous generator <asynchronous generator iterator>`. If a generator
265+
exits early - for example, due to an exception raised in the body of
266+
an ``async for`` loop - its asynchronous cleanup code may run in an
267+
unexpected context. This can occur after the tasks it depends on have completed,
268+
or during the event loop shutdown when the async-generator's garbage collection
269+
hook is called.
270+
271+
To avoid this, explicitly close the generator by calling its
272+
:meth:`~agen.aclose` method, or use the :func:`contextlib.aclosing`
273+
context manager::
274+
275+
import asyncio
276+
import contextlib
277+
278+
async def gen():
279+
yield 1
280+
yield 2
281+
282+
async def func():
283+
async with contextlib.aclosing(gen()) as g:
284+
async for x in g:
285+
break # Don't iterate until the end
286+
287+
asyncio.run(func())
288+
289+
As noted above, the cleanup code for these asynchronous generators is deferred.
290+
The following example demonstrates that the finalization of an asynchronous
291+
generator can occur 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 - an unexpected behavior.
338+
339+
The 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+
350+
351+
Create asynchronous generators only when the event loop is running
352+
------------------------------------------------------------------
353+
354+
It is recommended to create
355+
:term:`asynchronous generators <asynchronous generator iterator>` only after
356+
the event loop has been created.
357+
358+
To ensure that asynchronous generators close reliably, the event loop uses the
359+
:func:`sys.set_asyncgen_hooks` function to register callback functions. These
360+
callbacks update the list of running asynchronous generators to keep it in a
361+
consistent state.
362+
363+
When the :meth:`loop.shutdown_asyncgens() <asyncio.loop.shutdown_asyncgens>`
364+
function is called, the running generators are stopped gracefully and the
365+
list is cleared.
366+
367+
The asynchronous generator invokes the corresponding system hook during its
368+
first iteration. At the same time, the generator records that the hook has
369+
been called and does not call it again.
370+
371+
Therefore, if iteration begins before the event loop is created,
372+
the event loop will not be able to add the generator to its list of active
373+
generators because the hooks are set after the generator attempts to call them.
374+
Consequently, the event loop will not be able to terminate the generator
375+
if necessary.
376+
377+
Consider the following example::
378+
379+
import asyncio
380+
381+
async def agenfn():
382+
try:
383+
yield 10
384+
finally:
385+
await asyncio.sleep(0)
386+
387+
388+
with asyncio.Runner() as runner:
389+
agen = agenfn()
390+
print(runner.run(anext(agen)))
391+
del agen
392+
393+
Output::
394+
395+
10
396+
Exception ignored while closing generator <async_generator object agenfn at 0x000002F71CD10D70>:
397+
Traceback (most recent call last):
398+
File "example.py", line 13, in <module>
399+
del agen
400+
^^^^
401+
RuntimeError: async generator ignored GeneratorExit
402+
403+
This example can be fixed as follows::
404+
405+
import asyncio
406+
407+
async def agenfn():
408+
try:
409+
yield 10
410+
finally:
411+
await asyncio.sleep(0)
412+
413+
async def main():
414+
agen = agenfn()
415+
print(await anext(agen))
416+
del agen
417+
418+
asyncio.run(main())
419+
420+
421+
Avoid concurrent iteration and closure of the same generator
422+
------------------------------------------------------------
423+
424+
Async generators may be reentered while another
425+
:meth:`~agen.__anext__` / :meth:`~agen.athrow` / :meth:`~agen.aclose` call is in
426+
progress. This may lead to an inconsistent state of the async generator and can
427+
cause errors.
428+
429+
Let's consider the following example::
430+
431+
import asyncio
432+
433+
async def consumer():
434+
for idx in range(100):
435+
await asyncio.sleep(0)
436+
message = yield idx
437+
print('received', message)
438+
439+
async def amain():
440+
agenerator = consumer()
441+
await agenerator.asend(None)
442+
443+
fa = asyncio.create_task(agenerator.asend('A'))
444+
fb = asyncio.create_task(agenerator.asend('B'))
445+
await fa
446+
await fb
447+
448+
asyncio.run(amain())
449+
450+
Output::
451+
452+
received A
453+
Traceback (most recent call last):
454+
File "test.py", line 38, in <module>
455+
asyncio.run(amain())
456+
~~~~~~~~~~~^^^^^^^^^
457+
File "Lib/asyncio/runners.py", line 204, in run
458+
return runner.run(main)
459+
~~~~~~~~~~^^^^^^
460+
File "Lib/asyncio/runners.py", line 127, in run
461+
return self._loop.run_until_complete(task)
462+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
463+
File "Lib/asyncio/base_events.py", line 719, in run_until_complete
464+
return future.result()
465+
~~~~~~~~~~~~~^^
466+
File "test.py", line 36, in amain
467+
await fb
468+
RuntimeError: anext(): asynchronous generator is already running
469+
470+
471+
Therefore, it is recommended to avoid using asynchronous generators in parallel
472+
tasks or across multiple event loops.

pr-preview/pr-1214/_sources/library/asyncio-eventloop.rst.txt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.. _asyncio-event-loop:
55

66
==========
7-
Event Loop
7+
Event loop
88
==========
99

1010
**Source code:** :source:`Lib/asyncio/events.py`,
@@ -105,7 +105,7 @@ This documentation page contains the following sections:
105105

106106
.. _asyncio-event-loop-methods:
107107

108-
Event Loop Methods
108+
Event loop methods
109109
==================
110110

111111
Event loops have **low-level** APIs for the following:
@@ -361,7 +361,7 @@ clocks to track time.
361361
The :func:`asyncio.sleep` function.
362362

363363

364-
Creating Futures and Tasks
364+
Creating futures and tasks
365365
^^^^^^^^^^^^^^^^^^^^^^^^^^
366366

367367
.. method:: loop.create_future()
@@ -962,7 +962,7 @@ Transferring files
962962
.. versionadded:: 3.7
963963

964964

965-
TLS Upgrade
965+
TLS upgrade
966966
^^^^^^^^^^^
967967

968968
.. method:: loop.start_tls(transport, protocol, \
@@ -1431,7 +1431,7 @@ Executing code in thread or process pools
14311431
:class:`~concurrent.futures.ThreadPoolExecutor`.
14321432

14331433

1434-
Error Handling API
1434+
Error handling API
14351435
^^^^^^^^^^^^^^^^^^
14361436

14371437
Allows customizing how exceptions are handled in the event loop.
@@ -1534,7 +1534,7 @@ Enabling debug mode
15341534
The :ref:`debug mode of asyncio <asyncio-debug-mode>`.
15351535

15361536

1537-
Running Subprocesses
1537+
Running subprocesses
15381538
^^^^^^^^^^^^^^^^^^^^
15391539

15401540
Methods described in this subsections are low-level. In regular
@@ -1672,7 +1672,7 @@ async/await code consider using the high-level
16721672
are going to be used to construct shell commands.
16731673

16741674

1675-
Callback Handles
1675+
Callback handles
16761676
================
16771677

16781678
.. class:: Handle
@@ -1715,7 +1715,7 @@ Callback Handles
17151715
.. versionadded:: 3.7
17161716

17171717

1718-
Server Objects
1718+
Server objects
17191719
==============
17201720

17211721
Server objects are created by :meth:`loop.create_server`,
@@ -1858,7 +1858,7 @@ Do not instantiate the :class:`Server` class directly.
18581858
.. _asyncio-event-loops:
18591859
.. _asyncio-event-loop-implementations:
18601860

1861-
Event Loop Implementations
1861+
Event loop implementations
18621862
==========================
18631863

18641864
asyncio ships with two different event loop implementations:
@@ -1971,10 +1971,10 @@ callback uses the :meth:`loop.call_later` method to reschedule itself
19711971
after 5 seconds, and then stops the event loop::
19721972

19731973
import asyncio
1974-
import datetime
1974+
import datetime as dt
19751975

19761976
def display_date(end_time, loop):
1977-
print(datetime.datetime.now())
1977+
print(dt.datetime.now())
19781978
if (loop.time() + 1.0) < end_time:
19791979
loop.call_later(1, display_date, end_time, loop)
19801980
else:

pr-preview/pr-1214/_sources/library/asyncio-protocol.rst.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ The subprocess is created by the :meth:`loop.subprocess_exec` method::
10371037
# low-level APIs.
10381038
loop = asyncio.get_running_loop()
10391039

1040-
code = 'import datetime; print(datetime.datetime.now())'
1040+
code = 'import datetime as dt; print(dt.datetime.now())'
10411041
exit_future = asyncio.Future(loop=loop)
10421042

10431043
# Create the subprocess controlled by DateProtocol;

0 commit comments

Comments
 (0)