Skip to content

Instantly share code, notes, and snippets.

@tdryer
Last active January 18, 2017 04:52
Show Gist options
  • Save tdryer/205e9bf6f165bce966f5d7bf82e63910 to your computer and use it in GitHub Desktop.
Save tdryer/205e9bf6f165bce966f5d7bf82e63910 to your computer and use it in GitHub Desktop.
Coroutine (mis)use in asyncio and tornado

Coroutine (mis)use in asyncio and tornado

asyncio

In asyncio, calling a coroutine without scheduling it (using yield from or otherwise) does not run its code.

According to the asyncio docs:

Calling a coroutine does not start its code running – the coroutine object returned by the call doesn't do anything until you schedule its execution. There are two basic ways to start it running: call await coroutine or yield from coroutine from another coroutine (assuming the other coroutine is already running!), or schedule its execution using the ensure_future() function or the AbstractEventLoop.create_task() method.

tornado

In tornado, calling a coroutine without observing the returned Future object (using yield or otherwise) runs it asynchronously (provided that there's an IOLoop running) but will not raise any exception. When the Future object is garbage collected, a stack trace will be logged.

According to the tornado docs:

When exceptions occur inside a coroutine, the exception information will be stored in the Future object. You must examine the result of the Future object, or the exception may go unnoticed by your code. This means yielding the function if called from another coroutine, using something like IOLoop.run_sync for top-level calls, or passing the Future to IOLoop.add_future.

import asyncio
@asyncio.coroutine
def my_coroutine():
print('coroutine started')
yield from asyncio.sleep(0.1)
print('coroutine finished')
@asyncio.coroutine
def async_main():
# CORRECT: Schedule the coroutine for execution.
yield from my_coroutine()
# INCORRECT: The coroutine will not run.
#my_coroutine()
yield from asyncio.sleep(1) # Make sure nothing else is going to happen.
def main():
asyncio.get_event_loop().run_until_complete(async_main())
# INCORRECT: The coroutine will not run.
#my_coroutine()
if __name__ == '__main__':
main()
from tornado import gen, ioloop
@gen.coroutine
def my_coroutine():
print('coroutine started')
yield gen.sleep(0.1)
print('coroutine finished')
@gen.coroutine
def async_main():
# CORRECT: Yield the coroutine to examine the result of the future.
yield my_coroutine()
# INCORRECT: Coroutine will execute asynchronously, and exceptions will be
# suppressed but logged.
#my_coroutine()
yield gen.sleep(1) # Make sure nothing else is going to happen.
def main():
io_loop = ioloop.IOLoop.current().run_sync(async_main)
# INCORRECT: Coroutine will only execute until it yields, and exceptions
# will be suppressed but logged.
#my_coroutine()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment