Skip to content

Incorrect Context in corotine's except and finally blocks #93740

@ProgramRipper

Description

@ProgramRipper

Bug report

Now we have the following code to show what happen:

import asyncio from contextvars import ContextVar ctx = ContextVar("test") loop = asyncio.new_event_loop() test.set("global") print('expected to be "global":', ctx.get()) async def main(): test.set("inner") print('expected to be "inner":', ctx.get()) try: await asyncio.sleep(5) # may exit here raise Exception("this is the expected case") except BaseException as e: print('in except, expected to be "inner":', ctx.get()) raise e finally: print('in finally, expected to be "inner":', ctx.get()) loop.run_until_complete(main())

If I left it run to the end, the result will as expected:

expected to be "global": global expected to be "inner": inner in except, expected to be "inner": inner in finally, expected to be "inner": inner Traceback (most recent call last): File "/home/programripper/PycharmProjects/test/test.py", line 25, in <module> loop.run_until_complete(main()) File "/home/programripper/.pyenv/versions/3.10.1/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete return future.result() File "/home/programripper/PycharmProjects/test/test.py", line 20, in main raise e File "/home/programripper/PycharmProjects/test/test.py", line 17, in main raise Exception("this is the expected case") Exception: this is the expected case 

But if I interrupt it while running, it turns to:

expected to be "global": global expected to be "inner": inner ^CTraceback (most recent call last): File "/home/programripper/PycharmProjects/test/test.py", line 22, in <module> loop.run_until_complete(main()) File "/home/programripper/.pyenv/versions/3.10.1/lib/python3.10/asyncio/base_events.py", line 628, in run_until_complete self.run_forever() File "/home/programripper/.pyenv/versions/3.10.1/lib/python3.10/asyncio/base_events.py", line 595, in run_forever self._run_once() File "/home/programripper/.pyenv/versions/3.10.1/lib/python3.10/asyncio/base_events.py", line 1845, in _run_once event_list = self._selector.select(timeout) File "/home/programripper/.pyenv/versions/3.10.1/lib/python3.10/selectors.py", line 469, in select fd_event_list = self._selector.poll(timeout, max_ev) KeyboardInterrupt in except, expected to be "inner": global in finally, expected to be "inner": global 

Obviously, the ctx.get() in except and finally blocks didn't work as expected.

What's more, this not only happen by KeyboardInterrupt, but also other operations that will triger except or finally block, such as garbage collect.

As it is hard to trigger gc, so I can't give a minimal case, but a real case https://github.com/GraiaProject/BroadcastControl/blob/6a4a13e3531109bcb82dd4b306e7498d2bff9b0b/src/graia/broadcast/__init__.py#L207:

2022-06-12 14:55:14.445 | ERROR | graia.ariadne.util:loguru_exc_callback_async:103 - Exception: {'message': 'Task was destroyed but it is pending!', 'task': <Task pending name='Task-112' coro=<Broadcast.layered_scheduler() done, defined at /home/programripper/PycharmProjects/test/__pypackages__/3.10/lib/graia/broadcast/__init__.py:97> wait_for=<Future pending cb=[Task.task_wakeup()]>>} Exception ignored in: <coroutine object Broadcast.Executor at 0x7fdf404553f0> Traceback (most recent call last): File "/home/programripper/PycharmProjects/test/__pypackages__/3.10/lib/graia/broadcast/__init__.py", line 207, in Executor dii.ctx.reset(dii_token) File "/home/programripper/PycharmProjects/test/__pypackages__/3.10/lib/graia/broadcast/utilles.py", line 60, in reset return self.current_ctx.reset(token) ValueError: <Token var=<ContextVar name='bcc_dii' at 0x7fdf43259a30> at 0x7fdf4047a940> was created in a different Context 

The "free-flying" task is collected by gc, and trigger finally. But ctx.reset() raised a ValueError, because the token "was created in a different Context".

Though I didn't test, I suppose any exception or other else that trigger except or finally outside a corotine will suffer from this problem.

Your environment

  • CPython versions tested on: 3.8.12, 3.9.9, 3.10.3, 3.11.0b3
  • Operating system and architecture: Linux, Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions