Repository navigation
Run async commands when Fire is called from a running event loop - #717
Open
aashish254 wants to merge 1 commit into
Open
aashish254 wants to merge 1 commit into
aashish254 wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #716
What
_CallAndUpdateTraceran coroutine functions withloop.run_until_complete(...)wheneverasyncio.get_running_loop()succeeded. That branch can never succeed:get_running_loop()only returns a loop that is already running in the current thread, and
run_until_completerefuses exactly that case. So callingFireon an async command fromasync code raised
RuntimeError: This event loop is already runningand left the coroutineunawaited:
docs/guide.md:608says "Whenever fire encounters a coroutine function, it runs it, blockinguntil it completes." This makes that sentence true for the running-loop case as well.
Why this shape
The new
_RunCoroutinehelper keeps the no-loop path exactly as it was (asyncio.runin thecalling thread) and only changes the case that was previously guaranteed to fail: the
coroutine is driven by a fresh loop on a one-worker thread, so
Firestill blocks and stillreturns the awaited value. The coroutine is created before the hand-off and awaited only
once, so no "coroutine was never awaited" warning can leak.
The issue suggests returning a clear error instead. I went with running it because the
calling code is synchronous anyway — a notebook, or any
Firecall made from async code —and it now produces the documented result rather than a message telling the user to rewrite
their program.
ThreadPoolExecutoris stdlib, adds no dependency, and the path is onlytaken where the previous behaviour was a hard failure, so nothing that worked before stops
working.
One honest behaviour note: the coroutine runs on a new loop, not the caller's. A coroutine
that awaits objects bound to the caller's loop only works today if the loop has been patched
for nesting (
nest_asyncio); under this change it gets a fresh loop instead. The commonself-contained case is unaffected — I measured both shapes returning the same value with and
without
nest_asyncioapplied.Verification
masterat716bbc2, withwarnings.simplefilter("error", RuntimeWarning):RuntimeError: This event loop is already runningplusRuntimeWarning: coroutine 'target' was never awaited.testFireAsyncio, which is untouched andstill passes: one proves the value comes back from inside a running loop, one proves the
command's own exception still propagates through the thread hand-off (it is not swallowed
or re-wrapped) and that command args are consumed on that path.
tests fail and
testFireAsynciopasses; the unawaited-coroutine warning reappears in thepytest output. So the tests are load-bearing.
.github/scripts/build.shrun locally on the final tree:python -m pytest→ 275 passed, 0 failed (also withipythoninstalled).pylint fire --ignore=test_components_py3.py,parser_fuzz_test.py,console→ the singleremaining message (
E1120infire/formatting_windows.py:45) is present at base too;count unchanged at 1.
python -m ty check ...→ 23 diagnostics, identical to the 23 measured at base.every API used (
asyncio.get_running_loop,asyncio.run,concurrent.futures.ThreadPoolExecutor) exists since 3.7.issue — Await coroutines from functools.partial components #712 keeps
loop.run_until_completefor partials, Catch TypeError from function calls and convert to FireError #654 keeps it inside aTypeErrorwrapper — and my branch contains no asyncio-error-handling change, so the twoideas are independent. Whichever lands second will need a small manual resolution in
fire/core.py, since both replace the lines right belowif inspectutils.IsCoroutineFunction(fn):. feat: skip __init__ when calling classmethods and staticmethods #713 merges clean against this branch(
git merge-tree --write-tree, both orders).