Skip to content

Commit b0c92e4

Browse files
[3.14] gh-138072: Fix typos and grammatical errors and improve clarity in asyncio howto document (GH-138895) (#140091)
Co-authored-by: Morteza24 <[email protected]>
1 parent e1caa1e commit b0c92e4

File tree

1 file changed

+24
-25
lines changed

1 file changed

+24
-25
lines changed

Doc/howto/a-conceptual-overview-of-asyncio.rst

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ model of how :mod:`asyncio` fundamentally works, helping you understand the
99
how and why behind the recommended patterns.
1010

1111
You might be curious about some key :mod:`!asyncio` concepts.
12-
You'll be comfortably able to answer these questions by the end of this
13-
article:
12+
By the end of this article, you'll be able to comfortably answer these questions:
1413

1514
- What's happening behind the scenes when an object is awaited?
1615
- How does :mod:`!asyncio` differentiate between a task which doesn't need
17-
CPU-time (such as a network request or file read) as opposed to a task that
16+
CPU time (such as a network request or file read) as opposed to a task that
1817
does (such as computing n-factorial)?
1918
- How to write an asynchronous variant of an operation, such as
2019
an async sleep or database request.
@@ -35,7 +34,7 @@ A conceptual overview part 1: the high-level
3534
--------------------------------------------
3635

3736
In part 1, we'll cover the main, high-level building blocks of :mod:`!asyncio`:
38-
the event loop, coroutine functions, coroutine objects, tasks and ``await``.
37+
the event loop, coroutine functions, coroutine objects, tasks, and ``await``.
3938

4039
==========
4140
Event Loop
@@ -56,7 +55,7 @@ Once it pauses or completes, it returns control to the event loop.
5655
The event loop will then select another job from its pool and invoke it.
5756
You can *roughly* think of the collection of jobs as a queue: jobs are added and
5857
then processed one at a time, generally (but not always) in order.
59-
This process repeats indefinitely with the event loop cycling endlessly
58+
This process repeats indefinitely, with the event loop cycling endlessly
6059
onwards.
6160
If there are no more jobs pending execution, the event loop is smart enough to
6261
rest and avoid needlessly wasting CPU cycles, and will come back when there's
@@ -276,7 +275,7 @@ in this case, a call to resume ``plant_a_tree()``.
276275

277276
Generally speaking, when the awaited task finishes (``dig_the_hole_task``),
278277
the original task or coroutine (``plant_a_tree()``) is added back to the event
279-
loops to-do list to be resumed.
278+
loop's to-do list to be resumed.
280279

281280
This is a basic, yet reliable mental model.
282281
In practice, the control handoffs are slightly more complex, but not by much.
@@ -310,7 +309,7 @@ Consider this program::
310309
The first statement in the coroutine ``main()`` creates ``task_b`` and schedules
311310
it for execution via the event loop.
312311
Then, ``coro_a()`` is repeatedly awaited. Control never cedes to the
313-
event loop which is why we see the output of all three ``coro_a()``
312+
event loop, which is why we see the output of all three ``coro_a()``
314313
invocations before ``coro_b()``'s output:
315314

316315
.. code-block:: none
@@ -338,8 +337,8 @@ This behavior of ``await coroutine`` can trip a lot of people up!
338337
That example highlights how using only ``await coroutine`` could
339338
unintentionally hog control from other tasks and effectively stall the event
340339
loop.
341-
:func:`asyncio.run` can help you detect such occurences via the
342-
``debug=True`` flag which accordingly enables
340+
:func:`asyncio.run` can help you detect such occurrences via the
341+
``debug=True`` flag, which enables
343342
:ref:`debug mode <asyncio-debug-mode>`.
344343
Among other things, it will log any coroutines that monopolize execution for
345344
100ms or longer.
@@ -348,8 +347,8 @@ The design intentionally trades off some conceptual clarity around usage of
348347
``await`` for improved performance.
349348
Each time a task is awaited, control needs to be passed all the way up the
350349
call stack to the event loop.
351-
That might sound minor, but in a large program with many ``await``'s and a deep
352-
callstack that overhead can add up to a meaningful performance drag.
350+
That might sound minor, but in a large program with many ``await`` statements and a deep
351+
call stack, that overhead can add up to a meaningful performance drag.
353352

354353
------------------------------------------------
355354
A conceptual overview part 2: the nuts and bolts
@@ -372,7 +371,7 @@ resume a coroutine.
372371
If the coroutine was paused and is now being resumed, the argument ``arg``
373372
will be sent in as the return value of the ``yield`` statement which originally
374373
paused it.
375-
If the coroutine is being used for the first time (as opposed to being resumed)
374+
If the coroutine is being used for the first time (as opposed to being resumed),
376375
``arg`` must be ``None``.
377376

378377
.. code-block::
@@ -403,14 +402,14 @@ If the coroutine is being used for the first time (as opposed to being resumed)
403402
returned_value = e.value
404403
print(f"Coroutine main() finished and provided value: {returned_value}.")
405404
406-
:ref:`yield <yieldexpr>`, like usual, pauses execution and returns control
405+
:ref:`yield <yieldexpr>`, as usual, pauses execution and returns control
407406
to the caller.
408407
In the example above, the ``yield``, on line 3, is called by
409408
``... = await rock`` on line 11.
410409
More broadly speaking, ``await`` calls the :meth:`~object.__await__` method of
411410
the given object.
412411
``await`` also does one more very special thing: it propagates (or "passes
413-
along") any ``yield``\ s it receives up the call-chain.
412+
along") any ``yield``\ s it receives up the call chain.
414413
In this case, that's back to ``... = coroutine.send(None)`` on line 16.
415414

416415
The coroutine is resumed via the ``coroutine.send(42)`` call on line 21.
@@ -462,12 +461,12 @@ computation's status and result.
462461
The term is a nod to the idea of something still to come or not yet happened,
463462
and the object is a way to keep an eye on that something.
464463

465-
A future has a few important attributes. One is its state which can be either
466-
"pending", "cancelled" or "done".
464+
A future has a few important attributes. One is its state, which can be either
465+
"pending", "cancelled", or "done".
467466
Another is its result, which is set when the state transitions to done.
468467
Unlike a coroutine, a future does not represent the actual computation to be
469468
done; instead, it represents the status and result of that computation, kind of
470-
like a status light (red, yellow or green) or indicator.
469+
like a status light (red, yellow, or green) or indicator.
471470

472471
:class:`asyncio.Task` subclasses :class:`asyncio.Future` in order to gain
473472
these various capabilities.
@@ -490,8 +489,8 @@ We'll go through an example of how you could leverage a future to create your
490489
own variant of asynchronous sleep (``async_sleep``) which mimics
491490
:func:`asyncio.sleep`.
492491

493-
This snippet registers a few tasks with the event loop and then awaits a
494-
coroutine wrapped in a task: ``async_sleep(3)``.
492+
This snippet registers a few tasks with the event loop and then awaits the task
493+
created by ``asyncio.create_task``, which wraps the ``async_sleep(3)`` coroutine.
495494
We want that task to finish only after three seconds have elapsed, but without
496495
preventing other tasks from running.
497496

@@ -540,8 +539,8 @@ will monitor how much time has elapsed and, accordingly, call
540539
# Block until the future is marked as done.
541540
await future
542541

543-
Below, we'll use a rather bare object, ``YieldToEventLoop()``, to ``yield``
544-
from ``__await__`` in order to cede control to the event loop.
542+
Below, we use a rather bare ``YieldToEventLoop()`` object to ``yield``
543+
from its ``__await__`` method, ceding control to the event loop.
545544
This is effectively the same as calling ``asyncio.sleep(0)``, but this approach
546545
offers more clarity, not to mention it's somewhat cheating to use
547546
``asyncio.sleep`` when showcasing how to implement it!
@@ -552,13 +551,13 @@ The ``watcher_task``, which runs the coroutine ``_sleep_watcher(...)``, will
552551
be invoked once per full cycle of the event loop.
553552
On each resumption, it'll check the time and if not enough has elapsed, then
554553
it'll pause once again and hand control back to the event loop.
555-
Eventually, enough time will have elapsed, and ``_sleep_watcher(...)`` will
556-
mark the future as done, and then itself finish too by breaking out of the
554+
Once enough time has elapsed, ``_sleep_watcher(...)``
555+
marks the future as done and completes by exiting its
557556
infinite ``while`` loop.
558557
Given this helper task is only invoked once per cycle of the event loop,
559558
you'd be correct to note that this asynchronous sleep will sleep *at least*
560559
three seconds, rather than exactly three seconds.
561-
Note this is also of true of ``asyncio.sleep``.
560+
Note this is also true of ``asyncio.sleep``.
562561

563562
::
564563

@@ -601,6 +600,6 @@ For reference, you could implement it without futures, like so::
601600
else:
602601
await YieldToEventLoop()
603602

604-
But, that's all for now. Hopefully you're ready to more confidently dive into
603+
But that's all for now. Hopefully you're ready to more confidently dive into
605604
some async programming or check out advanced topics in the
606605
:mod:`rest of the documentation <asyncio>`.

0 commit comments

Comments
 (0)