For I/O bound tasks, python coroutines make a nice replacement for threads. Unfortunately, there’s no asynchronous API for reading files, as discussed in the Best way to read/write files with AsyncIO thread of the python-tulip mailing list.
Meanwhile, it is essential that a long-running coroutine contain some asynchronous calls, since otherwise it will run all the way to completion before any other event loop tasks are allowed to run. For a long-running coroutine that needs to call a conventional iterator (rather than an asynchronous iterator), I’ve found this converter class to be useful:
class AsyncIteratorExecutor:
"""
Converts a regular iterator into an asynchronous
iterator, by executing the iterator in a thread.
"""
def __init__(self, iterator, loop=None, executor=None):
self.__iterator = iterator
self.__loop = loop or asyncio.get_event_loop()
self.__executor = executor
def __aiter__(self):
return self
async def __anext__(self):
value = await self.__loop.run_in_executor(
self.__executor, next, self.__iterator, self)
if value is self:
raise StopAsyncIteration
return value
For example, it can be used to asynchronously read lines of a text file as follows:
async def cat_file_async(filename):
with open(filename, 'rt') as f:
async for line in AsyncIteratorExecutor(f):
print(line.rstrip())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(
cat_file_async('/path/of/file.txt'))
finally:
loop.close()