File tree

1 file changed

+17
-83
lines changed

1 file changed

+17
-83
lines changed
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import uasyncio as asyncio
3737
     3.2.1 [Wait on multiple events](./TUTORIAL.md#321-wait-on-multiple-events) Pause until 1 of N events is set.
3838
3.3 [Coordinating multiple tasks](./TUTORIAL.md#33-coordinating-multiple-tasks)
3939
     3.3.1 [gather](./TUTORIAL.md#331-gather)
40-
     3.3.2 [TaskGroups](./TUTORIAL.md#332-taskgroups) Not yet in official build.
4140
3.4 [Semaphore](./TUTORIAL.md#34-semaphore)
4241
     3.4.1 [BoundedSemaphore](./TUTORIAL.md#341-boundedsemaphore)
4342
3.5 [Queue](./TUTORIAL.md#35-queue)
@@ -134,6 +133,10 @@ mip.install(":peterhinch/micropython-async/v3/threadsafe")
134133
```
135134
For non-networked targets use `mpremote` as described in
136135
[the official docs](http://docs.micropython.org/en/latest/reference/packages.html#installing-packages-with-mpremote).
136+
```bash
137+
$ mpremote mip install :peterhinch/micropython-async/v3/primitives
138+
$ mpremote mip install :peterhinch/micropython-async/v3/threadsafe
139+
```
137140

138141
###### [Main README](../README.md)
139142

@@ -276,7 +279,7 @@ line `main.py` and runs forever.
276279

277280
## 2.2 Coroutines and Tasks
278281

279-
The fundmental building block of `asyncio` is a coro. This is defined with
282+
The fundamental building block of `asyncio` is a coro. This is defined with
280283
`async def` and usually contains at least one `await` statement. This minimal
281284
example waits 1 second before printing a message:
282285

@@ -285,12 +288,16 @@ async def bar():
285288
await asyncio.sleep(1)
286289
print('Done')
287290
```
288-
289-
V3 `asyncio` introduced the concept of a `Task`. A `Task` instance is created
290-
from a coro by means of the `create_task` method, which causes the coro to be
291-
scheduled for execution and returns a `Task` instance. In many cases, coros and
292-
tasks are interchangeable: the official docs refer to them as `awaitable`, for
293-
the reason that either of them may be the target of an `await`. Consider this:
291+
Just as a function does nothing until called, a coro does nothing until awaited
292+
or converted to a `Task`. The `create_task` method takes a coro as its argument
293+
and returns a `Task` instance, which is scheduled for execution. In
294+
```python
295+
async def foo():
296+
await coro
297+
```
298+
`coro` is run with `await` pausing until `coro` has completed. Sometimes coros
299+
and tasks are interchangeable: the CPython docs refer to them as `awaitable`,
300+
because either may be the target of an `await`. Consider this:
294301

295302
```python
296303
import asyncio
@@ -856,79 +863,6 @@ async def main():
856863

857864
asyncio.run(main())
858865
```
859-
### 3.3.2 TaskGroups
860-
861-
The `TaskGroup` class is unofficially provided by
862-
[this PR](https://.com/micropython/micropython/pull/8791). It is well
863-
suited to applications where one or more of a group of tasks is subject to
864-
runtime exceptions. A `TaskGroup` is instantiated in an asynchronous context
865-
manager. The `TaskGroup` instantiates member tasks. When all have run to
866-
completion, the context manager terminates. Where `gather` is static, a task
867-
group can be dynamic: a task in a group may spawn further group members. Return
868-
values from member tasks cannot be retrieved. Results should be passed in other
869-
ways such as via bound variables, queues etc.
870-
871-
An exception in a member task not trapped by that task is propagated to the
872-
task that created the `TaskGroup`. All tasks in the `TaskGroup` then terminate
873-
in an orderly fashion: cleanup code in any `finally` clause will run. When all
874-
cleanup code has completed, the context manager completes, and execution passes
875-
to an exception handler in an outer scope.
876-
877-
If a member task is cancelled in code, that task terminates in an orderly way
878-
but the other members continue to run.
879-
880-
The following illustrates the basic salient points of using a `TaskGroup`:
881-
```python
882-
import asyncio
883-
async def foo(n):
884-
for x in range(10 + n):
885-
print(f"Task {n} running.")
886-
await asyncio.sleep(1 + n/10)
887-
print(f"Task {n} done")
888-
889-
async def main():
890-
async with asyncio.TaskGroup() as tg: # Context manager pauses until members terminate
891-
for n in range(4):
892-
tg.create_task(foo(n)) # tg.create_task() creates a member task
893-
print("TaskGroup done") # All tasks have terminated
894-
895-
asyncio.run(main())
896-
```
897-
This more complete example illustrates an exception which is not trapped by the
898-
member task. Cleanup code on all members runs when the exception occurs,
899-
followed by exception handling code in `main()`.
900-
```python
901-
import asyncio
902-
fail = True # Set False to demo normal completion
903-
async def foo(n):
904-
print(f"Task {n} running...")
905-
try:
906-
for x in range(10 + n):
907-
await asyncio.sleep(1 + n/10)
908-
if n==0 and x==5 and fail:
909-
raise OSError("Uncaught exception in task.")
910-
print(f"Task {n} done")
911-
finally:
912-
print(f"Task {n} cleanup")
913-
914-
async def main():
915-
try:
916-
async with asyncio.TaskGroup() as tg:
917-
for n in range(4):
918-
tg.create_task(foo(n))
919-
print("TaskGroup done") # Does not get here if a task throws exception
920-
except Exception as e:
921-
print(f'TaskGroup caught exception: "{e}"')
922-
finally:
923-
print("TaskGroup finally")
924-
925-
asyncio.run(main())
926-
```
927-
[This doc](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)
928-
provides background on the theory behind task groups and how they can improve
929-
program structure and reliablity.
930-
931-
###### [Contents](./TUTORIAL.md#contents)
932866

933867
## 3.4 Semaphore
934868

@@ -2061,7 +1995,7 @@ asyncio.run(main())
20611995
```
20621996
The `.readline` method will pause until `\n` is received.
20631997

2064-
###### StreamWriter write methods
1998+
##### StreamWriter write methods
20651999

20662000
Writing to a `StreamWriter` occurs in two stages. The synchronous `.write`
20672001
method concatenates data for later transmission. The asynchronous `.drain`
@@ -2078,7 +2012,7 @@ following methods: `ioctl`, `read`, `readline` and `write`. See
20782012
[Writing device drivers](./TUTORIAL.md#64-writing--device-drivers)
20792013
for details on how such drivers may be written in Python.
20802014

2081-
###### StreamReader read methods
2015+
##### StreamReader read methods
20822016

20832017
The `StreamReader` read methods fall into two categories depending on whether
20842018
they wait for a specific end condition. Thus `.readline` pauses until a newline

0 commit comments

Comments
 (0)