-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
Finish deprecation in asyncio.get_event_loop() #93453
Comments
I wonder a bit how much existing code will be broken by this change in Python 3.12? If the deprecation warning in Python 3.10-11 remains unnoticed (like for me, until I ran Python with the A simple workaround that worked in my case is to call Will this workaround of using |
I'm interesting in knowing the answer to @lschoe's question too. A user of one of my libraries reported this deprecation warning. Is using |
Ask @asvetlov. I do not know plans about
|
Thanks @serhiy-storchaka |
Thanks as well. It's good to have something next to For example, I want to call |
@lschoe I think the intended pattern is def loop_factory():
loop = asyncio.new_event_loop()
loop.set_exception_handler(...)
with asyncio.Runner(loop_factory=loop_factory) as runner:
runner.run(amain()) or: with asyncio.Runner() as runner:
runner.get_loop().set_exception_handler(...)
runner.run(amain()) But calling |
Thanks, these are interesting options, indeed for Python 3.11+ which will have this new Runner class. Using a With For example, one can run some code in Python's asyncio REPL (via I've also tried the second way you propose, calling And your third option requires a running loop again (which will not be there yet, if this code is executed upon importing and initializing a module). To deal with such varying circumstances, global access to the event loop attached to the current thread via |
@serhiy-storchaka set_event_loop is not a no-op it sets up the child watcher system #93896 |
This is a subtle issue and I would like to go slowly here (but not so slowly to miss the 3.12 feature cut-off!). I wasn't aware of this issue and need some time to think about the consequences. The growing asymmetry between get_event_loop() and set_event_loop() is definitely bothering me. Should we perhaps also deprecate the behavior of Policy.get_event_loop() to sometimes create a new event loop? |
See also GH-94597. |
In response to @lschoe's comments (e.g. #93453 (comment)), this is indeed a reason to go slowly. I wonder what the remaining use cases are for accessing the current thread's loop regardless of whether it's running or not -- is it just If so, maybe we could recommend The awkward thing is that the use case requires that we still call I would very much like to believe that this is a very very small minority use case or that it's just a bad practice, but nothing I've read here convinces me of that. @lschoe can you point to real-world code that uses this pattern? |
This doesn't work when anything uses asyncio.run, because the first thing asyncio.run does is make a brand new event loop and the last thing it does is set the global event loop to None, which makes subsequent |
Yeah, we need a better understanding of @lschoe's use case. |
The backdrop for the use case is a bit extensive, so I'll try to extract the relevant points for your discussion as directly as possible. The perspective is that of a (Python package) developer of code that heavily relies on asyncio and its event loop, using lots of Futures, Tasks, coroutines etc. The use of these asyncio primitives, and Futures in particular, is hidden from the end user, who will experience most code at the application level preferably as non-async code. The actual package MPyC is for secure multiparty computation, a kind of distributed computation with privacy guarantees. The parties are connected by point-to-point TCP/IP links which are used continuously for exchanging secret-shares of (intermediate) values in the computation. The MPyC code development has a bit of history. It started in Python 2 + twisted, but it's now Python 3 + asyncio since at least five years. The major design decisions pertaining to asyncio's event loop go back to Python 3.6 (currently Python 3.8+ is required). I'll simply sketch a few reasons for accessing the loop while it's not running.
A typical piece of code using
Only at the last line we need to wait for the result, and if the above code is part of async code we would use Summarizing, we are using asyncio's event loop freely, without much concern if the loop happens to be running or not. The main goal being to accommodate mixes of non-async code and async code, and viewing the loop attached to a thread as a unique piece of real estate to do so. |
Hm... If you are okay with delegating creation of the loop to something else, may I recommend using the Runner class added to 3.11? You should be able to do something like r = asyncio.Runner()
loop = r.get_loop()
loop.set_exception_handler(exc_handler) Now you can run either futures or coroutines: loop.run_until_complete(fut) or r.run(coro(arg, ...)) The Runner instance allows reusing the loop as many times as you want. When done, use r.close() You can hide all this detail inside your own abstraction. There's no need to use |
Right, thanks. The Runner class was also suggested by @graingert above. Indeed, this should give enough flexibility to set an exception handler and more generally one gets direct access to the underlying loop. What I overlooked previously is that--unlike I'll try and see how things work out. |
... trying this out was effortless. Double checked things, and turns out that replacing self._loop = asyncio.get_event_loop() # cache event loop with self._loop = asyncio.Runner().get_loop() # cache event loop does the job! Everything runs as before, and because the loop was already cached, Futures attached to it persist in between subsequent runs. One small issue though: if there's a loop already running, |
You probably want to copy the |
Nice, thx, that makes it work combined like this: self._loop = asyncio.mixins._LoopBoundMixin()._get_loop()
if self._loop is None:
self._loop = asyncio.Runner().get_loop() Now it also works when a loop is already running (like in the asyncio REPL, and probably in a Jupyter notebook as well but I didn't test that for 3.11rc2). |
No not like that, you need to copy the _LoopBoundMixin code and subclass it and call |
But I actually want to create Futures attached to a (the?) not-yet running loop. At least that's what I've been doing so. |
@lschoe in that case you probably want to copy the Future code and make a version that attaches the loop with the LoopBoundMixin only when a result is set. Asyncio supports duck type futures, you're not limited to asyncio.Future |
The breaking changes begun by the deprecation warnings in 3.10 have arrived, but according to python/cpython#93453 the scope has changed somewhat (for the better, I think). Don't test on 3.12 until we've adapted to the new plan.
The breaking changes begun by the deprecation warnings in 3.10 have arrived, but according to python/cpython#93453 the scope has changed somewhat (for the better, I think). Don't test on 3.12 until we've adapted to the new plan.
The documentation of asyncio.get_event_loop() says:
Which by default raises a RuntimeError, so it is rather confusing. Should the documentation be updated to explicitly say something like:
? |
Partially revert changes made in pythonGH-93453. asyncio.DefaultEventLoopPolicy.get_event_loop() now emits a DeprecationWarning and creates and sets a new event loop instead of raising a RuntimeError if there is no current event loop set.
Oooh, the decumentation is indeed a mess. There's no place that I can find where the behavior of |
Is it fair to say this is now a documentation issue, or is there more to be done? Should this hold up 3.12.0a4? |
Hm, #100410 isn't merged yet, even though I approved it two weeks ago. @serhiy-storchaka do you have any hesitations? |
Since 3.10
asyncio.get_event_loop()
emits a deprecation warning if used outside of the event loop (see #83710). It is a time to turn a warning into error and makeasyncio.get_event_loop()
an alias ofasyncio.get_running_loop()
.But maybe we should first deprecate
set_event_loop()
? It will be a no-op now.Linked PRs
The text was updated successfully, but these errors were encountered: