trio icon indicating copy to clipboard operation
trio copied to clipboard

Internal error: assert len(runner.tasks) == 2

Open elektito opened this issue 3 years ago • 2 comments

The following piece of code occasionally results in a TrioInternalError which is a bug per exception description:

import trio
from contextlib import AsyncExitStack

DEFAULT_PG_UNIX_SOCKET = '/var/run/postgresql/.s.PGSQL.5432'

class Connection:
    def __init__(self):
        self._stream = None
        self._nursery = None
        self._stack = None

    async def _run(self):
        await self._connect()
        while True:
            await trio.sleep(1)

    async def _connect(self):
        self.unix_socket_path = DEFAULT_PG_UNIX_SOCKET
        self._stream = await trio.open_unix_socket(
            self.unix_socket_path)

    async def __aenter__(self):
        self._stack = AsyncExitStack()
        nursery = trio.open_nursery()
        self._nursery = await self._stack.enter_async_context(nursery)
        self._nursery.start_soon(self._run)

        return self

    async def __aexit__(self, exc_type, exc, tb):
        #if self._stream:
        await self._stream.close()
        await self._stack.aclose()

async def main():
    async with Connection() as conn:
        pass


if __name__ == '__main__':
    trio.run(main)

If the commented line is uncommented (and the next line indented), this won't happen. The code sometimes results in an AttributeError, but at other times throws this:

Traceback (most recent call last):
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_run.py", line 2203, in unrolled_run
    runner.task_exited(task, final_outcome)
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_run.py", line 1515, in task_exited
    outcome.unwrap()
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/outcome/_impl.py", line 138, in unwrap
    raise captured_error
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_run.py", line 1622, in init
    await self.asyncgens.finalize_remaining(self)
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_asyncgens.py", line 122, in finalize_remaining
    assert len(runner.tasks) == 2
AssertionError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "bug.py", line 42, in <module>
    trio.run(main)
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_run.py", line 1937, in run
    timeout = gen.send(next_send)
  File "/home/mostafa/source/pgtrio/venv/lib/python3.8/site-packages/trio/_core/_run.py", line 2258, in unrolled_run
    raise TrioInternalError("internal error in Trio - please file a bug!") from exc
trio.TrioInternalError: internal error in Trio - please file a bug!

elektito avatar Apr 16 '22 23:04 elektito

Raising an error from within your __aexit__ instead of closing the stack violates Trio's (Python's, really) core assumptions about how context managers are supposed to be nested / exceptions to be handled.

This is not a Trio problem. This is a "Doctor, it hurts when I do that." "Don't do that, then" problem.

smurfix avatar Apr 17 '22 03:04 smurfix

Right. I've fixed the original problem of course. Just filed the issue because the exception told me to! :)

elektito avatar Apr 17 '22 12:04 elektito