-
-
Notifications
You must be signed in to change notification settings - Fork 365
Description
When we spawn a task, we inject a magic local variable into the top stack frame's local namespace, as part of our tricky control-C magic:
Line 1753 in d4ce2f9
coro.cr_frame.f_locals.setdefault(LOCALS_KEY_KI_PROTECTION_ENABLED, system_task) |
However, this doesn't work for async def
functions defined in C or Cython, because they don't have a Python frame. So we a simple workaround, we detect this case and inject a no-op Python frame where we can insert our magic local (cf #550):
Lines 1747 to 1750 in d4ce2f9
if not hasattr(coro, "cr_frame"): | |
# This async function is implemented in C or Cython | |
async def python_wrapper(orig_coro: Awaitable[RetT]) -> RetT: | |
return await orig_coro |
However, the bug came back at some point with newer versions of Cython:
| File "/root/.pyenv/versions/3.11.6/lib/python3.11/site-packages/trio/_core/_run.py", line 1599, in spawn_impl
| coro.cr_frame.f_locals.setdefault(LOCALS_KEY_KI_PROTECTION_ENABLED, system_task)
| ^^^^^^^^^^^^^^^^^^^^^^
| AttributeError: 'NoneType' object has no attribute 'f_locals'
From the error message, it appears that Cython has added a cr_frame
attribute to its fake coroutine objects... just it's None
. So it's still not really there, but it's enough to sneak past our hasattr(coro, "cr_frame")
test.
I think the fix here is just to replace the if not hasattr(coro, "cr_frame"):
with if getattr(coro, "cr_frame", None) is None:
. But it would be good to also run a test for this in CI. We do have a test for the interpreter's built-in C coroutines (test_async_function_implemented_in_C
) and I originally assumed that would be enough, but obviously it wasn't, so I guess we need to do the legwork to install cython in CI and build a tiny Cython module.
Edit by CoolCat467:
Things to do:
- Fix existing issue (Cython Workaround #2911 is handling)
- Add CI runner for cython so this sort of thing doesn't happen again in the future (Add Cython to CI #2942)