Skip to content

Commit 9176e89

Browse files
Merge pull request #10 from aligheshlaghi97/feat/ch8-9
Feat/ch8 9
2 parents 70dcce4 + 2e079fe commit 9176e89

File tree

18 files changed

+552
-19
lines changed

18 files changed

+552
-19
lines changed

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@
22

33
Welcome to the Async Python Playground repository! 🚀
44

5-
This repository serves as a comprehensive resource for learning about asynchronous programming in Python, with a focus on the asyncio module and the latest features introduced in Python 3.11, 3.12 and 3.13. Whether you're a beginner looking to dive into async programming or an experienced developer seeking to explore advanced techniques, this repository has something for everyone.
5+
This repository is a comprehensive guide to mastering asynchronous programming in Python,
6+
covering core concepts, practical tools, and advanced techniques.
7+
From the fundamentals of asyncio to alternative libraries like AnyIO and Trio,
8+
as well as task scheduling solutions such as Celery and APScheduler,
9+
this repository provides a one-stop resource for exploring the async Python ecosystem.
10+
11+
Whether you're a beginner eager to dive into async programming
12+
or an experienced developer looking to refine your skills and explore emerging patterns,
13+
this repository has you covered.
14+
615

716
## Key Features:
817

9-
**Structured Content:** Explore a curated collection of code examples, tutorials, and explanations covering a wide range of async Python topics.<br />
10-
**Clear Documentation:** Each example is accompanied by detailed documentation, guiding you through the concepts, syntax, and best practices of async programming.<br />
11-
**Python latest versions Coverage:** Discover the newest capabilities of async related Python 3.11, 3.12 and 3.13 and how they enhance async programming in Python.<br />
12-
**Interactive Learning:** Experiment with the provided code examples and gain hands-on experience with async Python in action.<br />
13-
**Community Engagement:** Join a growing community of developers interested in async Python, contribute your own examples, and collaborate with others to explore async programming further.<br />
18+
**Structured Content:** Tutorials and examples covering a wide range of async Python topics.<br />
19+
**Clear Documentation:** Detailed explanations to help you understand concepts and best practices.<br />
20+
**Latest Python Features:** Highlights of async improvements in Python 3.11, 3.12, and 3.13.<br />
21+
**Hands-On Learning:** Try out code examples to build practical async skills.<br />
22+
**Collaborative Community:** Share ideas and contribute to the growing async Python ecosystem.<br />
1423

1524
## Documentation
1625
For full documentation, including tutorials and examples, visit the

_includes/sidebar.html

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,51 @@
22
<h2>Navigation</h2>
33
<ul>
44
<li><a href="{{ '/' | relative_url }}">Home</a></li>
5-
<li><a href="{{ '/chapter1/' | relative_url }}">Chapter 1: Introduction to Asynchronous Programming</a></li>
6-
<li><a href="{{ '/chapter2/' | relative_url }}">Chapter 2: Getting Started with asyncio</a></li>
7-
<li><a href="{{ '/chapter3/' | relative_url }}">Chapter 3: Handling CPU and I/O Bound Tasks</a></li>
8-
<li><a href="{{ '/chapter4/' | relative_url }}">Chapter 4: Synchronization and Coordination</a></li>
9-
<li><a href="{{ '/chapter5/' | relative_url }}">Chapter 5: Advanced Techniques</a></li>
10-
<li><a href="{{ '/chapter6/' | relative_url }}">Chapter 6: Exploring Features of New Python Versions</a></li>
11-
<li><a href="{{ '/chapter7/' | relative_url }}">Chapter 7: Web Applications</a></li>
5+
<li>
6+
<a href="{{ '/chapter1/' | relative_url }}"
7+
>Chapter 1: Introduction to Asynchronous Programming</a
8+
>
9+
</li>
10+
<li>
11+
<a href="{{ '/chapter2/' | relative_url }}"
12+
>Chapter 2: Getting Started with asyncio</a
13+
>
14+
</li>
15+
<li>
16+
<a href="{{ '/chapter3/' | relative_url }}"
17+
>Chapter 3: Handling CPU and I/O Bound Tasks</a
18+
>
19+
</li>
20+
<li>
21+
<a href="{{ '/chapter4/' | relative_url }}"
22+
>Chapter 4: Synchronization and Coordination</a
23+
>
24+
</li>
25+
<li>
26+
<a href="{{ '/chapter5/' | relative_url }}"
27+
>Chapter 5: Advanced Techniques</a
28+
>
29+
</li>
30+
<li>
31+
<a href="{{ '/chapter6/' | relative_url }}"
32+
>Chapter 6: Exploring Features of New Python Versions</a
33+
>
34+
</li>
35+
<li>
36+
<a href="{{ '/chapter7/' | relative_url }}"
37+
>Chapter 7: Web Applications</a
38+
>
39+
</li>
40+
<li>
41+
<a href="{{ '/chapter8/' | relative_url }}"
42+
>Chapter 8: Introduction to AnyIO and Trio</a
43+
>
44+
</li>
45+
<li>
46+
<a href="{{ '/chapter9/' | relative_url }}"
47+
>Chapter 9: Task Scheduling and Queues: Celery and
48+
APScheduler</a
49+
>
50+
</li>
1251
</ul>
1352
</div>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
3+
4+
async def coro_a():
5+
await asyncio.sleep(0.2)
6+
7+
8+
async def coro_b():
9+
await asyncio.sleep(0.1)
10+
11+
12+
async def fn():
13+
task_a = asyncio.create_task(coro_a())
14+
task_b = asyncio.create_task(coro_b())
15+
16+
return await task_a
17+
18+
19+
asyncio.run(fn())
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import asyncio
2+
3+
4+
async def sleep_coro(delay):
5+
print(f"Started sleeping for {delay} seconds!")
6+
await asyncio.sleep(delay)
7+
print(f"Finished sleeping for {delay} seconds!")
8+
return delay
9+
10+
11+
async def main():
12+
print('Before running task group!')
13+
async with asyncio.TaskGroup() as tg:
14+
task_a = tg.create_task(sleep_coro(2))
15+
task_b = tg.create_task(sleep_coro(1))
16+
print("After running task group!")
17+
print("Both tasks finished!")
18+
print(f"Results are: (task_a: {task_a.result()}, task_b: {task_b.result()})")
19+
20+
asyncio.run(main())
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import trio
2+
3+
4+
async def child1():
5+
print(" child1: started! sleeping now...")
6+
await trio.sleep(1)
7+
print(" child1: exiting!")
8+
9+
10+
async def child2():
11+
print(" child2: started! sleeping now...")
12+
await trio.sleep(1)
13+
print(" child2: exiting!")
14+
15+
16+
async def parent():
17+
print("parent: started!")
18+
async with trio.open_nursery() as nursery:
19+
print("parent: spawning child1...")
20+
nursery.start_soon(child1)
21+
22+
print("parent: spawning child2...")
23+
nursery.start_soon(child2)
24+
25+
print("parent: waiting for children to finish...")
26+
# -- we exit the nursery block here --
27+
print("parent: all done!")
28+
29+
30+
trio.run(parent)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import trio
2+
3+
4+
async def main():
5+
async with trio.open_nursery() as nursery:
6+
send_channel, receive_channel = trio.open_memory_channel(0)
7+
nursery.start_soon(producer, send_channel)
8+
nursery.start_soon(consumer, receive_channel)
9+
print("Done!")
10+
11+
12+
async def producer(send_channel):
13+
async with send_channel:
14+
for value in range(5):
15+
await trio.sleep(1)
16+
await send_channel.send(value)
17+
18+
19+
async def consumer(receive_channel):
20+
async with receive_channel:
21+
async for value in receive_channel:
22+
print(f"Got number {value}")
23+
24+
25+
trio.run(main)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from anyio import sleep, create_task_group, run
2+
3+
4+
async def sometask(num: int) -> None:
5+
print('Task', num, 'running')
6+
await sleep(1)
7+
print('Task', num, 'finished')
8+
9+
10+
async def main() -> None:
11+
async with create_task_group() as tg:
12+
for num in range(5):
13+
tg.start_soon(sometask, num)
14+
15+
print('All tasks finished!')
16+
17+
run(main)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import anyio
2+
3+
4+
async def producer(sender):
5+
async with sender:
6+
for value in range(5):
7+
await sender.send(value)
8+
await anyio.sleep(1)
9+
10+
11+
async def consumer(receiver):
12+
async with receiver:
13+
async for value in receiver:
14+
print(f'Got number {value}')
15+
await anyio.sleep(1)
16+
17+
18+
async def main():
19+
sender, receiver = anyio.create_memory_object_stream()
20+
async with anyio.create_task_group() as tg:
21+
async with sender, receiver:
22+
tg.start_soon(producer, sender.clone())
23+
tg.start_soon(consumer, receiver.clone())
24+
25+
26+
anyio.run(main)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from pathlib import Path
2+
3+
import anyio
4+
from anyio import to_thread
5+
6+
7+
def print_dir_content():
8+
current_dir = Path.cwd()
9+
for file in current_dir.iterdir():
10+
print(file)
11+
12+
13+
async def display_dir_content(event):
14+
await to_thread.run_sync(print_dir_content)
15+
event.set()
16+
17+
18+
async def wait_print_finished(event):
19+
print('wait dir printing is finished')
20+
await event.wait()
21+
print('finished!')
22+
23+
24+
async def main():
25+
event = anyio.Event()
26+
async with anyio.create_task_group() as tg:
27+
tg.start_soon(display_dir_content, event)
28+
tg.start_soon(wait_print_finished, event)
29+
30+
31+
anyio.run(main)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
layout: default
3+
title: "Chapter 8: Introduction to AnyIO and Trio"
4+
permalink: /chapter8/
5+
---
6+
7+
# Introduction to AnyIO and Trio
8+
## What Are AnyIO and Trio?
9+
Let's imagine that our primary objective is achieving concurrency for I/O bound tasks.
10+
While we have been exploring `asyncio` in Python to support this goal,
11+
additional tools such as `anyio` and `trio` offer valuable alternatives for enhancing I/O parallelism
12+
which leverage the benefits of the `Structured Concurrency` design pattern.
13+
Their appeal lies in their user-friendly nature, enhanced features, and reduced learning curve,
14+
making them attractive options for improving task efficiency.
15+
16+
### Why to use them?
17+
18+
- **Improved Developer Productivity**: Simplified APIs and tools reduce the learning curve and development time.
19+
20+
- **Cross-Framework Compatibility**: AnyIO ensures that our code can run on different async backends without modification.
21+
22+
- **Enhanced Safety**: Trio’s structured concurrency model reduces the likelihood of subtle bugs in concurrent code.
23+
24+
25+
26+
## Structured Concurrency Design Pattern
27+
As Jan Plesnik explains [here](https://applifting.io/blog/python-structured-concurrency),
28+
to avoid orphaned tasks—tasks that lose their reference—we need to embrace structured concurrency.
29+
In Python 3.11, the introduction of `asyncio.TaskGroup` provides a way to manage this effectively.
30+
Similarly, Trio offers nurseries, which serve the same purpose.
31+
An example of an orphaned task can be seen in the following case, where `task_b` loses its reference.
32+
(Example from Jan's article.)
33+
34+
```python
35+
# ex_8_1
36+
{% include_relative ex_8_1.py %}
37+
```
38+
This image illustrates what happens under the hood
39+
(from [here](https://belief-driven-design.com/looking-at-java-21-structured-concurrency-39a81/)):
40+
![test1](https://github.com/user-attachments/assets/48215a3a-538f-4a0b-8143-34268958d794)
41+
42+
And now imagine this example with `asyncio.TaskGroup` (which is similar to example_2_8).
43+
As you see, both `task_a` and `test_b` will finish and then the task-group context manager exits.
44+
45+
```python
46+
# ex_8_2
47+
{% include_relative ex_8_2.py %}
48+
```
49+
50+
And now we have full control over tasks and this is how it will look like.
51+
![test2](https://github.com/user-attachments/assets/3d82ae86-3fc2-42e6-b342-94bfea9f342b)
52+
53+
## Exploring Trio
54+
`Trio`'s primary aim is to simplify the comprehension and enhance the performance of concurrency operations.
55+
It offers a more efficient GIL and higher-level APIs, resulting in code that is easier to understand and troubleshoot.
56+
57+
Now let's see our first example from [trio's official document](https://trio.readthedocs.io/en/stable/)
58+
and get familiar with it.
59+
As you can see, there is a parent async function which uses nurseries to spawn two child tasks and run them.
60+
```python
61+
# ex_8_3
62+
{% include_relative ex_8_3.py %}
63+
```
64+
65+
A very interesting topic would be to see how `consumer-producer` is handled in `trio`, described in official document
66+
[here](https://trio.readthedocs.io/en/stable/reference-core.html#using-channels-to-pass-values-between-tasks)
67+
and compare it with our `ex_5_5`, a consumer-producer variant in `asyncio`.
68+
`open_memory_channel` is an API used to create queue and manage consumer-producer in `trio`.
69+
Let's rewrite our `ex_5_5` in `trio` syntax:
70+
```python
71+
# ex_8_4
72+
{% include_relative ex_8_4.py %}
73+
```
74+
By utilizing `open_memory_channel`, we get two channels initiated and then call consumer/producer with their respective channels.
75+
Also, the `consumer` and `producer` async functions contain a context manager to signal the completion of the production/consumption processes.
76+
77+
78+
## Getting Started with AnyIO
79+
AnyIO is a handy library that makes async programming easier by letting you write code that works with both `asyncio` and `Trio`.
80+
It hides the differences between these frameworks, so your code runs smoothly no matter which event loop you use.
81+
82+
83+
The starter example from [AnyIO official document](https://anyio.readthedocs.io/en/stable/tasks.html#creating-and-managing-tasks) is provided here:
84+
```python
85+
# ex_8_5
86+
{% include_relative ex_8_5.py %}
87+
```
88+
89+
It runs on top of `asyncio` and if you want to run it on top of `Trio` just do this in run section:
90+
```python
91+
run(main, backend='trio')
92+
```
93+
94+
95+
And let's see a typical `consumer-producer` pattern in `AnyIO` which looks like `Trio`'s example:
96+
```python
97+
# ex_8_6
98+
{% include_relative ex_8_6.py %}
99+
```
100+
101+
If you want to do some blocking task, `anyio.to_thread` API seem do to a good job.
102+
One example would be to read the file names in the current path.
103+
[from this article](https://lewoudar.medium.com/anyio-all-you-need-for-async-programming-stuff-4cd084d0f6bd)
104+
```python
105+
# ex_8_7
106+
{% include_relative ex_8_7.py %}
107+
```
108+
109+
## When to Choose AnyIO or Trio for Your Projects
110+
Choosing between AnyIO and Trio often depends on the goals of your project:
111+
112+
- **AnyIO** is perfect when you need a framework-agnostic approach,
113+
letting you write code compatible with `asyncio` and `Trio`.
114+
- **Trio** shines when you want a structured concurrency model
115+
with a focus on reliability and simplicity.
116+
117+
If you're already familiar with `asyncio`, `AnyIO` offers a smooth transition to explore `Trio`'s advantages.
118+
If you're starting fresh, `Trio`’s user-friendly design makes it a great first choice.

0 commit comments

Comments
 (0)