From acb91e982444f828f3affdcd4c29494da919ca20 Mon Sep 17 00:00:00 2001 From: Leodanis Pozo Ramos Date: Mon, 15 Jul 2024 15:58:55 +0200 Subject: [PATCH 1/5] Sample code for the article on async iterators --- python-async-iterators/README.md | 3 ++ python-async-iterators/async_comp.py | 17 ++++++++ python-async-iterators/async_range_v1.py | 27 +++++++++++++ python-async-iterators/async_range_v2.py | 19 +++++++++ python-async-iterators/async_range_v3.py | 15 +++++++ python-async-iterators/compress.py | 23 +++++++++++ python-async-iterators/counter.py | 39 +++++++++++++++++++ python-async-iterators/inf_integers.py | 22 +++++++++++ python-async-iterators/large_file_iterable.py | 27 +++++++++++++ python-async-iterators/large_file_iterator.py | 32 +++++++++++++++ 10 files changed, 224 insertions(+) create mode 100644 python-async-iterators/README.md create mode 100644 python-async-iterators/async_comp.py create mode 100644 python-async-iterators/async_range_v1.py create mode 100644 python-async-iterators/async_range_v2.py create mode 100644 python-async-iterators/async_range_v3.py create mode 100644 python-async-iterators/compress.py create mode 100644 python-async-iterators/counter.py create mode 100644 python-async-iterators/inf_integers.py create mode 100644 python-async-iterators/large_file_iterable.py create mode 100644 python-async-iterators/large_file_iterator.py diff --git a/python-async-iterators/README.md b/python-async-iterators/README.md new file mode 100644 index 0000000000..f148f84109 --- /dev/null +++ b/python-async-iterators/README.md @@ -0,0 +1,3 @@ +# Asynchronous Iterators and Iterables in Python + +This folder provides the code examples for the Real Python tutorial [Asynchronous Iterators and Iterables in Python](https://realpython.com/python-async-iterator/). diff --git a/python-async-iterators/async_comp.py b/python-async-iterators/async_comp.py new file mode 100644 index 0000000000..f980a102a5 --- /dev/null +++ b/python-async-iterators/async_comp.py @@ -0,0 +1,17 @@ +import asyncio + + +async def async_range(start, end): + for i in range(start, end): + await asyncio.sleep(0.2) + yield i + + +async def main(): + number_list = [i async for i in async_range(0, 5)] + number_dict = {i: str(i) async for i in async_range(0, 5)} + print(number_list) + print(number_dict) + + +asyncio.run(main()) diff --git a/python-async-iterators/async_range_v1.py b/python-async-iterators/async_range_v1.py new file mode 100644 index 0000000000..f78e5801b7 --- /dev/null +++ b/python-async-iterators/async_range_v1.py @@ -0,0 +1,27 @@ +import asyncio + + +class AsyncRange: + def __init__(self, start, end): + self.start = start + self.end = end + + def __aiter__(self): + return self + + async def __anext__(self): + if self.start < self.end: + await asyncio.sleep(0.5) + value = self.start + self.start += 1 + return value + else: + raise StopAsyncIteration + + +async def main(): + async for i in AsyncRange(0, 5): + print(i) + + +asyncio.run(main()) diff --git a/python-async-iterators/async_range_v2.py b/python-async-iterators/async_range_v2.py new file mode 100644 index 0000000000..be4af7d9df --- /dev/null +++ b/python-async-iterators/async_range_v2.py @@ -0,0 +1,19 @@ +import asyncio + + +class AsyncRange: + def __init__(self, start, end): + self.data = range(start, end) + + async def __aiter__(self): + for i in self.data: + await asyncio.sleep(0.5) + yield i + + +async def main(): + async for i in AsyncRange(0, 5): + print(i) + + +asyncio.run(main()) diff --git a/python-async-iterators/async_range_v3.py b/python-async-iterators/async_range_v3.py new file mode 100644 index 0000000000..a7ae84fe82 --- /dev/null +++ b/python-async-iterators/async_range_v3.py @@ -0,0 +1,15 @@ +import asyncio + + +async def async_range(start, end): + for i in range(start, end): + await asyncio.sleep(0.5) + yield i + + +async def main(): + async for i in async_range(0, 5): + print(i) + + +asyncio.run(main()) diff --git a/python-async-iterators/compress.py b/python-async-iterators/compress.py new file mode 100644 index 0000000000..8dcac801b3 --- /dev/null +++ b/python-async-iterators/compress.py @@ -0,0 +1,23 @@ +import asyncio +from pathlib import Path + +import aiofiles +from zipstream import AioZipStream + + +async def stream_generator(files): + async_zipstream = AioZipStream(files) + async for chunk in async_zipstream.stream(): + yield chunk + + +async def main(directory, zip_name="output.zip"): + files = [{"file": file} for file in directory.iterdir()] + + async with aiofiles.open(zip_name, mode="wb") as z: + async for chunk in stream_generator(files): + await z.write(chunk) + + +directory = Path() +asyncio.run(main(directory)) diff --git a/python-async-iterators/counter.py b/python-async-iterators/counter.py new file mode 100644 index 0000000000..d081913cdc --- /dev/null +++ b/python-async-iterators/counter.py @@ -0,0 +1,39 @@ +import asyncio +from random import randint + + +class AsyncCounterIterator: + def __init__(self, name="", end=5): + self.counter = 0 + self.name = name + self.end = end + + def __aiter__(self): + return self + + async def __anext__(self): + if self.counter >= self.end: + raise StopAsyncIteration + self.counter += 1 + await asyncio.sleep(randint(1, 3) / 10) + return self.counter + + +async def task(iterator): + async for item in iterator: + print(item, f"from iterator {iterator.name}") + + +async def main(): + # This code runs sequentially: + # await task(AsyncCounterIterator("#1")) + # await task(AsyncCounterIterator("#2")) + + # This is concurrent: + await asyncio.gather( + task(AsyncCounterIterator("#1")), + task(AsyncCounterIterator("#2")), + ) + + +asyncio.run(main()) diff --git a/python-async-iterators/inf_integers.py b/python-async-iterators/inf_integers.py new file mode 100644 index 0000000000..e63f92beee --- /dev/null +++ b/python-async-iterators/inf_integers.py @@ -0,0 +1,22 @@ +import asyncio + + +async def async_inf_integers(start=0): + current = start + while True: + yield current + current += 1 + await asyncio.sleep(0.5) + + +async def main(stop=5): + generator = async_inf_integers() + while True: + number = await anext(generator) + # Process the number here... + print(number) + if number == stop - 1: + break + + +asyncio.run(main(20)) diff --git a/python-async-iterators/large_file_iterable.py b/python-async-iterators/large_file_iterable.py new file mode 100644 index 0000000000..67e8f338c5 --- /dev/null +++ b/python-async-iterators/large_file_iterable.py @@ -0,0 +1,27 @@ +import asyncio + +import aiofiles + + +class AsyncFileIterable: + def __init__(self, filename, chunk_size=1024): + self.filename = filename + self.chunk_size = chunk_size + + async def __aiter__(self): + async with aiofiles.open(self.filename, mode="rb") as file: + while True: + chunk = await file.read(self.chunk_size) + if not chunk: + break + yield chunk + + +async def main(): + async for chunk in AsyncFileIterable("large-file.md"): + # Process the file chunk here... + await asyncio.sleep(0.2) + print(chunk.decode("utf-8")) + + +asyncio.run(main()) diff --git a/python-async-iterators/large_file_iterator.py b/python-async-iterators/large_file_iterator.py new file mode 100644 index 0000000000..d3c014abe3 --- /dev/null +++ b/python-async-iterators/large_file_iterator.py @@ -0,0 +1,32 @@ +import asyncio + +import aiofiles + + +class AsyncFileIterator: + def __init__(self, filename, chunk_size=1024): + self.filename = filename + self.chunk_size = chunk_size + self.file = None + + def __aiter__(self): + return self + + async def __anext__(self): + if self.file is None: + self.file = await aiofiles.open(self.filename, mode="rb") + chunk = await self.file.read(self.chunk_size) + if not chunk: + await self.file.close() + raise StopAsyncIteration + return chunk + + +async def main(): + async for chunk in AsyncFileIterator("large-file.md"): + # Process the file chunk here... + await asyncio.sleep(0.2) + print(chunk.decode("utf-8")) + + +asyncio.run(main()) From c6d877089550afd8fbcb722e831167854af07ca1 Mon Sep 17 00:00:00 2001 From: Leodanis Pozo Ramos Date: Thu, 18 Jul 2024 15:31:36 +0200 Subject: [PATCH 2/5] TR updates, first round --- python-async-iterators/async_csv.py | 36 +++++++++++++++++++ python-async-iterators/compress.py | 7 ++-- python-async-iterators/data.csv | 5 +++ python-async-iterators/large_file_iterable.py | 6 ++-- python-async-iterators/large_file_iterator.py | 6 ++-- 5 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 python-async-iterators/async_csv.py create mode 100644 python-async-iterators/data.csv diff --git a/python-async-iterators/async_csv.py b/python-async-iterators/async_csv.py new file mode 100644 index 0000000000..9f46b1e706 --- /dev/null +++ b/python-async-iterators/async_csv.py @@ -0,0 +1,36 @@ +import asyncio +import csv + +import aiofiles + + +class AsyncCSVIterator: + def __init__(self, path): + self.path = path + self.file = None + + def __aiter__(self): + return self + + async def __anext__(self): + if self.file is None: + self.file = await aiofiles.open(self.path, mode="r") + lines = await self.file.readlines() + self.reader = csv.reader(lines) + try: + return next(self.reader) + except StopIteration: + await self.file.close() + raise StopAsyncIteration + + +async def main(): + csv_iter = AsyncCSVIterator("data.csv") + # Skip the headers + await anext(csv_iter) + # Process the rest of the rows + async for row in csv_iter: + print(row) + + +asyncio.run(main()) diff --git a/python-async-iterators/compress.py b/python-async-iterators/compress.py index 8dcac801b3..361da4a59f 100644 --- a/python-async-iterators/compress.py +++ b/python-async-iterators/compress.py @@ -12,11 +12,10 @@ async def stream_generator(files): async def main(directory, zip_name="output.zip"): - files = [{"file": file} for file in directory.iterdir()] - - async with aiofiles.open(zip_name, mode="wb") as z: + files = [{"file": path} for path in directory.iterdir() if path.is_file()] + async with aiofiles.open(zip_name, mode="wb") as archive: async for chunk in stream_generator(files): - await z.write(chunk) + await archive.write(chunk) directory = Path() diff --git a/python-async-iterators/data.csv b/python-async-iterators/data.csv new file mode 100644 index 0000000000..3fe4052c2c --- /dev/null +++ b/python-async-iterators/data.csv @@ -0,0 +1,5 @@ +Name,Age,Job +John Doe,30,Software Engineer +Jane Smith,25,Data Scientist +Jim Brown,45,Project Manager +Jessica Jones,40,UX Designer \ No newline at end of file diff --git a/python-async-iterators/large_file_iterable.py b/python-async-iterators/large_file_iterable.py index 67e8f338c5..26bacbfa94 100644 --- a/python-async-iterators/large_file_iterable.py +++ b/python-async-iterators/large_file_iterable.py @@ -4,12 +4,12 @@ class AsyncFileIterable: - def __init__(self, filename, chunk_size=1024): - self.filename = filename + def __init__(self, path, chunk_size=1024): + self.path = path self.chunk_size = chunk_size async def __aiter__(self): - async with aiofiles.open(self.filename, mode="rb") as file: + async with aiofiles.open(self.path, mode="rb") as file: while True: chunk = await file.read(self.chunk_size) if not chunk: diff --git a/python-async-iterators/large_file_iterator.py b/python-async-iterators/large_file_iterator.py index d3c014abe3..b69a825398 100644 --- a/python-async-iterators/large_file_iterator.py +++ b/python-async-iterators/large_file_iterator.py @@ -4,8 +4,8 @@ class AsyncFileIterator: - def __init__(self, filename, chunk_size=1024): - self.filename = filename + def __init__(self, path, chunk_size=1024): + self.path = path self.chunk_size = chunk_size self.file = None @@ -14,7 +14,7 @@ def __aiter__(self): async def __anext__(self): if self.file is None: - self.file = await aiofiles.open(self.filename, mode="rb") + self.file = await aiofiles.open(self.path, mode="rb") chunk = await self.file.read(self.chunk_size) if not chunk: await self.file.close() From 2c5c2dbff68cae2484b0cca9012c23130d3331e5 Mon Sep 17 00:00:00 2001 From: Leodanis Pozo Ramos Date: Fri, 19 Jul 2024 13:45:30 +0200 Subject: [PATCH 3/5] Fix typo on URL --- python-async-iterators/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-async-iterators/README.md b/python-async-iterators/README.md index f148f84109..24c45d15ab 100644 --- a/python-async-iterators/README.md +++ b/python-async-iterators/README.md @@ -1,3 +1,3 @@ # Asynchronous Iterators and Iterables in Python -This folder provides the code examples for the Real Python tutorial [Asynchronous Iterators and Iterables in Python](https://realpython.com/python-async-iterator/). +This folder provides the code examples for the Real Python tutorial [Asynchronous Iterators and Iterables in Python](https://realpython.com/python-async-iterators/). From 686a5aa662ccf7794deac69345b357ed483f15db Mon Sep 17 00:00:00 2001 From: Leodanis Pozo Ramos Date: Fri, 19 Jul 2024 14:08:59 +0200 Subject: [PATCH 4/5] TR updates, second round --- python-async-iterators/async_csv.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python-async-iterators/async_csv.py b/python-async-iterators/async_csv.py index 9f46b1e706..67d64df3ad 100644 --- a/python-async-iterators/async_csv.py +++ b/python-async-iterators/async_csv.py @@ -7,20 +7,20 @@ class AsyncCSVIterator: def __init__(self, path): self.path = path - self.file = None + self.file_was_read = False def __aiter__(self): return self async def __anext__(self): - if self.file is None: - self.file = await aiofiles.open(self.path, mode="r") - lines = await self.file.readlines() - self.reader = csv.reader(lines) + if not self.file_was_read: + async with aiofiles.open(self.path, mode="r") as file: + lines = await file.readlines() + self.reader = csv.reader(lines) + self.file_was_read = True try: return next(self.reader) except StopIteration: - await self.file.close() raise StopAsyncIteration From 0d35b0bc5ad8092b3e95eeccd9b1ff7cd49a75f7 Mon Sep 17 00:00:00 2001 From: Martin Breuss Date: Wed, 31 Jul 2024 11:41:59 +0200 Subject: [PATCH 5/5] Fix typo --- functional-programming-python/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functional-programming-python/README.md b/functional-programming-python/README.md index f39f8f97c7..239b96bb44 100644 --- a/functional-programming-python/README.md +++ b/functional-programming-python/README.md @@ -2,7 +2,7 @@ This folder contains the sample code used in the RealPython tutorial [Functional Programming in Python: When and How to Use It](https://realpython.com/python-functional-programming/). -## Run the Scrips +## Run the Scripts You can run the individual files as Python scripts: