The Wayback Machine - https://web.archive.org/web/20220612141657/https://github.com/python/cpython/issues/93740
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
ProgramRipper opened this issue Jun 12, 2022 · 0 comments
Open

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

ProgramRipper opened this issue Jun 12, 2022 · 0 comments
Labels
type-bug

Comments

@ProgramRipper
Copy link

@ProgramRipper ProgramRipper commented Jun 12, 2022

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, caused 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
@ProgramRipper ProgramRipper added the type-bug label Jun 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug
Projects
None yet
Development

No branches or pull requests

1 participant