Skip to content

Commit ac31789

Browse files
authored
Merge pull request #557 from realpython/python-async-iterators
Sample code for the article on async iterators
2 parents 0d35b0b + e584418 commit ac31789

File tree

12 files changed

+264
-0
lines changed

12 files changed

+264
-0
lines changed

python-async-iterators/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Asynchronous Iterators and Iterables in Python
2+
3+
This folder provides the code examples for the Real Python tutorial [Asynchronous Iterators and Iterables in Python](https://realpython.com/python-async-iterators/).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import asyncio
2+
3+
4+
async def async_range(start, end):
5+
for i in range(start, end):
6+
await asyncio.sleep(0.2)
7+
yield i
8+
9+
10+
async def main():
11+
number_list = [i async for i in async_range(0, 5)]
12+
number_dict = {i: str(i) async for i in async_range(0, 5)}
13+
print(number_list)
14+
print(number_dict)
15+
16+
17+
asyncio.run(main())
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import asyncio
2+
import csv
3+
4+
import aiofiles
5+
6+
7+
class AsyncCSVIterator:
8+
def __init__(self, path):
9+
self.path = path
10+
self.file_was_read = False
11+
12+
def __aiter__(self):
13+
return self
14+
15+
async def __anext__(self):
16+
if not self.file_was_read:
17+
async with aiofiles.open(self.path, mode="r") as file:
18+
lines = await file.readlines()
19+
self.reader = csv.reader(lines)
20+
self.file_was_read = True
21+
try:
22+
return next(self.reader)
23+
except StopIteration:
24+
raise StopAsyncIteration
25+
26+
27+
async def main():
28+
csv_iter = AsyncCSVIterator("data.csv")
29+
# Skip the headers
30+
await anext(csv_iter)
31+
# Process the rest of the rows
32+
async for row in csv_iter:
33+
print(row)
34+
35+
36+
asyncio.run(main())
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import asyncio
2+
3+
4+
class AsyncRange:
5+
def __init__(self, start, end):
6+
self.start = start
7+
self.end = end
8+
9+
def __aiter__(self):
10+
return self
11+
12+
async def __anext__(self):
13+
if self.start < self.end:
14+
await asyncio.sleep(0.5)
15+
value = self.start
16+
self.start += 1
17+
return value
18+
else:
19+
raise StopAsyncIteration
20+
21+
22+
async def main():
23+
async for i in AsyncRange(0, 5):
24+
print(i)
25+
26+
27+
asyncio.run(main())
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
3+
4+
class AsyncRange:
5+
def __init__(self, start, end):
6+
self.data = range(start, end)
7+
8+
async def __aiter__(self):
9+
for i in self.data:
10+
await asyncio.sleep(0.5)
11+
yield i
12+
13+
14+
async def main():
15+
async for i in AsyncRange(0, 5):
16+
print(i)
17+
18+
19+
asyncio.run(main())
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import asyncio
2+
3+
4+
async def async_range(start, end):
5+
for i in range(start, end):
6+
await asyncio.sleep(0.5)
7+
yield i
8+
9+
10+
async def main():
11+
async for i in async_range(0, 5):
12+
print(i)
13+
14+
15+
asyncio.run(main())

python-async-iterators/compress.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import asyncio
2+
from pathlib import Path
3+
4+
import aiofiles
5+
from zipstream import AioZipStream
6+
7+
8+
async def stream_generator(files):
9+
async_zipstream = AioZipStream(files)
10+
async for chunk in async_zipstream.stream():
11+
yield chunk
12+
13+
14+
async def main(directory, zip_name="output.zip"):
15+
files = [{"file": path} for path in directory.iterdir() if path.is_file()]
16+
async with aiofiles.open(zip_name, mode="wb") as archive:
17+
async for chunk in stream_generator(files):
18+
await archive.write(chunk)
19+
20+
21+
directory = Path()
22+
asyncio.run(main(directory))

python-async-iterators/counter.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import asyncio
2+
from random import randint
3+
4+
5+
class AsyncCounterIterator:
6+
def __init__(self, name="", end=5):
7+
self.counter = 0
8+
self.name = name
9+
self.end = end
10+
11+
def __aiter__(self):
12+
return self
13+
14+
async def __anext__(self):
15+
if self.counter >= self.end:
16+
raise StopAsyncIteration
17+
self.counter += 1
18+
await asyncio.sleep(randint(1, 3) / 10)
19+
return self.counter
20+
21+
22+
async def task(iterator):
23+
async for item in iterator:
24+
print(item, f"from iterator {iterator.name}")
25+
26+
27+
async def main():
28+
# This code runs sequentially:
29+
# await task(AsyncCounterIterator("#1"))
30+
# await task(AsyncCounterIterator("#2"))
31+
32+
# This is concurrent:
33+
await asyncio.gather(
34+
task(AsyncCounterIterator("#1")),
35+
task(AsyncCounterIterator("#2")),
36+
)
37+
38+
39+
asyncio.run(main())

python-async-iterators/data.csv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Name,Age,Job
2+
John Doe,30,Software Engineer
3+
Jane Smith,25,Data Scientist
4+
Jim Brown,45,Project Manager
5+
Jessica Jones,40,UX Designer
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import asyncio
2+
3+
4+
async def async_inf_integers(start=0):
5+
current = start
6+
while True:
7+
yield current
8+
current += 1
9+
await asyncio.sleep(0.5)
10+
11+
12+
async def main(stop=5):
13+
generator = async_inf_integers()
14+
while True:
15+
number = await anext(generator)
16+
# Process the number here...
17+
print(number)
18+
if number == stop - 1:
19+
break
20+
21+
22+
asyncio.run(main(20))

0 commit comments

Comments
 (0)