Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute coroutine from `call_soon` callback function

I have following situation:

  • Some internal class (which I have no control of) is executing my callback function using call_soon.
  • Within my callback I would like to call another courotune, but end up with "frozen" callback.

I will use modified Hello World with call_soon() to demonstrate this:

import asyncio

def hello_world(loop):
    print('Hello')
    # Call some coroutine.
    yield from asyncio.sleep(5, loop=loop)
    print('World')
    loop.stop()

loop = asyncio.get_event_loop()

# Schedule a call to hello_world()
loop.call_soon(hello_world, loop)

# Blocking call interrupted by loop.stop()
loop.run_forever()
loop.close()

When I run this, nothing is being printed and the program never ends.

Ctrl+C

Traceback (most recent call last):
  File "../soon.py", line 15, in <module>
    loop.run_forever()
  File "/usr/lib/python3.4/asyncio/base_events.py", line 276, in run_forever
    self._run_once()
  File "/usr/lib/python3.4/asyncio/base_events.py", line 1136, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.4/selectors.py", line 432, in select
    fd_event_list = self._epoll.poll(timeout, max_ev)
KeyboardInterrupt

What is actually going on and why?

Any correct way to do this?

like image 671
Michael Avatar asked Aug 31 '15 09:08

Michael


1 Answers

The example you mentioned demonstrate how to schedule a callback.

If you use the yield from syntax, the function is actually a coroutine and it has to be decorated accordingly:

@asyncio.coroutine
def hello_world(loop):
    print('Hello')
    yield from asyncio.sleep(5, loop=loop)
    print('World')
    loop.stop()

Then you can schedule the coroutine as a task using ensure_future:

loop = asyncio.get_event_loop()
coro = hello_world(loop)
asyncio.ensure_future(coro)
loop.run_forever()
loop.close()

Or equivalently, using run_until_complete:

loop = asyncio.get_event_loop()
coro = hello_world(loop)
loop.run_until_complete(coro)

In two weeks, python 3.5 will officially be released and you'll be able to use the new async/await syntax:

async def hello_world(loop):
    print('Hello')
    await asyncio.sleep(5, loop=loop)
    print('World')

EDIT: It is a bit ugly, but nothing prevents you from creating a callback that schedules your coroutine:

loop = asyncio.get_event_loop()
coro = hello_world(loop)
callback = lambda: asyncio.ensure_future(coro)
loop.call_soon(callback)
loop.run_forever()
loop.close()
like image 187
Vincent Avatar answered Nov 04 '22 04:11

Vincent