I have opened a PR deprecating asyncio.set_event_loop_policy and scheduling its removal for Python 3.15. I also plan to submit more PRs deprecating asyncio.get_event_loop_policy and the policy classes
In 3.15 I propose that asyncio.new_event_loop will import and return the new asyncio.EventLoop and asyncio.get_event_loop/asyncio.set_event_loop will be re-implemented to store in a threadlocal independent of the policy system matching the default policy behaviour (unless asyncio.get_event_loop becomes an alias for asyncio.get_running_loop as originally planed)
The current leading usecase for asyncio.set_event_loop_policy, running uvloop, is already deprecated in favour of uvloop.run. The next most popular usecase, using a SelectorEventLoop on win32 is available in the loop_factory kwarg of asyncio.run, eg asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop). A number of patterns rely on the âspooky action at a distanceâ behaviour of the policy system by setting the policy in one location so that a user in another location will use the SelectorEventLoop without having to configure it, this will still be available by running asyncio.EventLoop = asyncio.SelectorEventLoop of course this is not recommended but naturally will be supported python behaviour.
Deprecating policies: Yes please. The policies no longer serve a real purpose. Loops are always per thread, there is no need to have a âcurrent loopâ when no loop is currently running. The only thing we still need is a loop factory, so perhaps instead of an API for getting/setting a global âpolicyâ, we could have an API for getting/setting a global âloop factoryâ.
(Thatâs me summarizing the feeling at the 2022 core dev sprint.)
The default event loop on Windows is indeed ProactorEventLoop.
I donât know how to answer your second question â I believe it can do everything SelectorEventLoop can do, but better, except for things involving Windows âsocket file descriptorsâ. I think the latter is the only reason weâre keeping it around.
Without policies, how do I use SelectorEventLoop on Windows? From what I saw, policies were mostly useful to opt-in for SelectorEventLoop on Windows, no? Well, there was also an API to select the âchild watcherâ implementation. It also goes away, no?
I would like to avoid adding more global things like a global default loop factory, practically asyncio.run it needs to be called only once in an entire application so explicit specifying loop factory seems better.
IMO we need to do all this in a planned fashion like it was done for child watcher which is now entirely deprecated. For policy, first design a way in which current âusualâ asyncio users can use asyncio without knowing about it at all, currently if a user knows nothing about child watchers it all magically works âgood enoughâ same should be for policy. Then you can start deprecating all the policy things in one go like getting, setting it and all the classes etc.
Most users already donât know or donât care about policies. The policy is a way to hold a dynamic global default. Instead, Thomas has introduced a static global default, asyncio.EventLoop. On UNIX this is asyncio.SelectorEventLoop and on Windows it is asyncio.ProactorEventLoop. This comes down to the same default that was being used before if no explicit policy was set.
Requiring users to pass an event loop factory to run() seems overkill. Allowing them has been possible for a while, we just make it the only way to override an event loop. (Youâre not supposed to overwrite asyncio.EventLoop, of course.)
As a data point when considering this deprecation, I grepped the primary Meta monorepo for usage of (g|s)et_event_loop_policy, and got O(100) hits in the internal codebase and O(300) hits in third-party libraries we use.
It shouldnât be a huge pain for us to handle this removal, but would be great if:
3rd party libraries have enough time to remove their usage and release versions that support pre-3.15 python releases (so we could upgrade the internally before we start upgrading to 3.15+)
The docs are clear about how to avoid these APIs where they are used (ideally it would be a mechanical change we can automate with LibCST, bonus points if the changes can be applied to code that uses 3.10+)
For comparison, I see thousands of hits for get_event_loop(, which is already deprecated - this one is going to be a more painful removalâŠ
can you break down how many hits you get for get_event_loop_policy vs set_event_loop_policy?
can you also see how many hits are get_event_loop_policy().get_event_loop() which was used to incorrectly avoid the get_event_loop() DeprecationWarning?
Also asyncio.get_event_loop() is only deprecated when no event loop is set. If a loop is set (running or not) it does not warn. Not sure if this matter, since setting an event loop may also be deprecated in favor of using asyncio.run(..., loop_factory=...).
Thomas, if weâre deprecating set_event_loop_policy(), that means weâre also deprecating everything that comes with policies (e.g. get_event_loop_policy() and the policy classes). Would we still support asyncio.set_event_loop()? How?
I have a feeling that we may be way too optimistic with our deprecation schedule for policies â Iâve already heard people say they want to be able to have code that, without version checks, works on all supported versions, which currently means back to 3.8. And only emitting deprecation warnings for set_event_loop_policy() isnât going to catch all uses (some people may just be inspecting the policy and deciding things based on what they find there or call its methods).
sure!
for get_event_loop_policy itâs under 50 for internal and close to 300 for third-party
for set_event_loop_policy itâs close to 100 for internal and under 200 for third-party
for get_event_loop_policy().get_event_loop() itâs only a few for internal and close to 200 for third-party
(sorry itâs all approx numbers, I am unable to share exact numbers)
Good question. It was a year ago and I donât recall. More recently I suggested that we might still want to support set_event_loop(), which sets the actual event loop to use, not the class. The platformâs best loop class should be EventLoop, so we donât need a way to set it.
I guess thereâs a general API design issue here where for some reason we tend to like passing a loop factory around instead of a loop object â e.g. the run() function and Runner class do this, even though both of these just instantiate the class once, without additional parameters. Does anyone recall or understand why? Thereâs a comment in asyncio/runners.py in _lazy_init() that suggests thereâs some special treatment for child watchers â but maybe thatâs no longer an issue either now?
I donât know if itâs currently true for us, but quite often these kinds of loops have thread affinity for where they start. So if you wanted to change the event loop used on a different thread, youâd want to pass it the factory rather than the loop.
Maybe there are thread locals or contextvars somewhere? Or there could be in a 3rd party loop?
As a data point, asyncio GUI integration tools (which need to listen to native windowing events in addition to IO) currently tend to use set_event_loop_policy.
I assume they can adapt to the new way of doing things. But please keep them in mind for documentation & porting notes. Itâs not just uvloop & SelectorEventLoop