Description
In previous versions, _callMaybeAsync(self, func, /, *args, **kwargs) would run result = func(*args, **kwargs) and then run the result with the event loop if inspect.isawaitable(result) returned True. In 3.11 inspect.iscoroutinefunction(func) is checked ahead of time instead, resulting in a warning about a coroutine not being awaited after trying to run func(*args, **kwargs) synchronously. This prevents using anything other than an async def-defined coroutine for test methods or cleanup functions1, the latter of which may not be defined in local code. This can be worked around by wrapping any known awaitable in a dummy async def _wrapper(aw): return await aw, but that's rather inconvenient and not a pleasant UX.
I have a "fix" for this that I don't consider to be viable that I'll nevertheless attach as a draft PR for discussion. It gives the behavior I would prefer to see in unittest and passes all existing tests, but relies on an ugly hack in asyncio to defeat the non-reentrancy of contextvars.Context.run. Ideally, either Context.run would allow recursive calls (by not running the entrance and exit routines if the context is already entered?), or would expose the underlying ctx_entered flag to allow LBYL when calling Context.run. I don't have a great understanding of contextvars, so I'm not sure how any of these options might break things or if there's a better option that's obvious to someone who knows what's going on here :)
Linked PRs
Footnotes
-
which when initially added explicitly supported exotic awaitables, see https://github.com/python/cpython/blob/76efcb40930d1584e8706f015d0e5475fb16acb5/Lib/unittest/async_case.py#L53-L58 ↩
Metadata
Metadata
Assignees
Labels
Projects
Status
Todo
Activity