-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
Description
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