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())