python-trio / trio-asyncio

a re-implementation of the asyncio mainloop on top of Trio

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Error leaving open_loop() with background Trio sandwich running

oremanj opened this issue · comments

The platform: CPython 3.6.7 on Linux, Trio 0.11.0, trio-asyncio 0.10.0.

The setup: Inside a trio-asyncio async loop, I have an asyncio task running in the background, that's in turn awaiting a long-running Trio task (using trio_as_aio appropriately). I exit the async with trio_asyncio.open_loop(): block with the background task still running. In code:

import trio
import trio_asyncio
import asyncio

@trio_asyncio.trio_as_aio
async def trio_grandchild():
    await trio.sleep_forever()

async def asyncio_child():
    await asyncio.sleep(1)
    await trio_grandchild()

@trio_asyncio.aio_as_trio
async def asyncio_spawner():
    asyncio.ensure_future(asyncio_child())

async def trio_main():
    async with trio_asyncio.open_loop() as loop:
        await asyncio_spawner()
        await trio.sleep(2)

if __name__ == "__main__":
    trio.run(trio_main)

The error, after 2 seconds:

Exception in default exception handler
Traceback (most recent call last):
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/base.py", line 300, in __run_trio
    res = await proc(*args)
  File "t.py", line 7, in trio_grandchild
    await trio.sleep_forever()
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_timeouts.py", line 51, in sleep_forever
    await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_core/_traps.py", line 166, in wait_task_rescheduled
    return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/outcome/_sync.py", line 111, in unwrap
    raise captured_error
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_core/_run.py", line 740, in raise_cancel
    raise Cancelled._init()
trio.Cancelled

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/asyncio/base_events.py", line 1299, in call_exception_handler
    self.default_exception_handler(context)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/async_.py", line 47, in default_exception_handler
    raise RuntimeError(message)
RuntimeError: Task was destroyed but it is pending!
Exception in default exception handler
Traceback (most recent call last):
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/base.py", line 300, in __run_trio
    res = await proc(*args)
  File "t.py", line 7, in trio_grandchild
    await trio.sleep_forever()
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_timeouts.py", line 51, in sleep_forever
    await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_core/_traps.py", line 166, in wait_task_rescheduled
    return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/outcome/_sync.py", line 111, in unwrap
    raise captured_error
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio/_core/_run.py", line 740, in raise_cancel
    raise Cancelled._init()
trio.Cancelled

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/asyncio/base_events.py", line 1299, in call_exception_handler
    self.default_exception_handler(context)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/async_.py", line 49, in default_exception_handler
    raise exception
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/handles.py", line 135, in _call_async
    await self._callback(self)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/base.py", line 306, in __run_trio
    f.set_exception(exc)
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/base.py", line 399, in call_soon
    return self._queue_handle(Handle(callback, args, self, context=context, is_sync=True))
  File "/usr/scratch/labtestvenv/lib/python3.6/site-packages/trio_asyncio/async_.py", line 18, in _queue_handle
    self._check_closed()
  File "/usr/lib/python3.6/asyncio/base_events.py", line 366, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

(In a classic display of asyncio robustness, this is just stderr -- the script exits successfully.)

This seems to occur because a Cancelled exception escapes from BaseTrioEventLoop.__run_trio after the open_loop() block's __aexit__ cancels its enclosing nursery's cancel scope. The future that __run_trio is working with isn't cancelled, so the cancellation check in __run_trio doesn't fire.