Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closing subtasks launched with asyncio.ensure_future

If I have the following:

async def foo():
    for i in range(10):
        print "1"
        await asyncio.sleep(1)

async def bar()
    t = asyncio.ensure_future(foo())
    while not t.done():
        print("waiting")
        await asyncio.sleep(0.5)

async def baz():
    t = asyncio.ensure_future(bar())
    await asyncio.sleep(2.5)
    t.cancel()

And baz() is called in a larger event loop, how can I make sure that foo() is cancelled?

like image 953
Eric Avatar asked Oct 30 '25 07:10

Eric


1 Answers

A try/finally block would catch the CancelledError exception:

task = asyncio.ensure_future(foo())
try:
    [...]
finally:
    task.cancel()  # return False if task is already finished

Some task functions already handle the cancellation of subtasks:

result = await asyncio.wait_for(foo(), 60.)  
result, = await asyncio.gather(foo())

In both examples, foo() is wrapped in a task and would be canceled automatically if the parent task were to be canceled.


EDIT

Here's a simple example of a coroutine controlling the execution of another one:

async def control(coro, timeout=1.):
    task = asyncio.ensure_future(coro)
    while not task.done():
        try:
            print("The task is not finished yet")
            await asyncio.wait([task, asyncio.sleep(timeout)], "FIRST_COMPLETED")
        except asyncio.CancelledError:
            task.cancel()
    return task.result()  # Raise task.exception() if any

Now, await control(foo(), 1.) should work exactly like await foo() (regarding cancellation, exception and result) except it prints The task is not finished yet every second until foo() is done.

like image 139
Vincent Avatar answered Oct 31 '25 20:10

Vincent