# Removing the asyncio policy system (asyncio.set\_event\_loop\_policy) in Python 3.15

**URL:** <https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553>\
**Category:** Async-SIG\
**Created:** [October 31, 2023, 4:40pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553 "2023-10-31T16:40:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [October 31, 2023, 4:40pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/1 "2023-10-31T16:40:43Z")

</div>

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.

Here is a link to the PR [gh-94597: deprecate asyncio.set\_event\_loop\_policy by graingert · Pull Request #110728 · python/cpython · GitHub](https://github.com/python/cpython/pull/110728)

---

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [October 31, 2023, 4:48pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/2 "2023-10-31T16:48:38Z")

</div>

Here’s an example of some PRs showing how to avoid the deprecated api by using the new `loop_factory` kwarg in run:

> <https://github.com/dask/distributed/pull/7969>
>
> Closes https://github.com/dask/distributed/issues/7492
> 
> it would be nicer to n…ot need the ProactorEventLoop at all, but there's still some issues using it (see failures in https://github.com/dask/distributed/pull/7494)
> 
> This PR backports the new "loop\_factory" kwarg for asyncio.run introduced in Python 3.12 and uses it instead of set\_event\_loop\_policy this PR is still useful even if #7494 as this avoids calling uvloop.install which also mutates the global event loop policy
> 
> \- \[x\] Tests added / passed
> \- \[x\] Passes \`pre-commit run --all-files\`

> <https://github.com/encode/uvicorn/pull/2130>
>
> \<!-- Thanks for contributing to Uvicorn! 💚
> Given this is a project maintained b…y volunteers, please read this template to not waste your time, or ours! 😁 --\>
> 
> \# Summary
> 
> 
> set\_event\_loop\_policy will be deprecated in python 3.13 and uvloop.EventLoopPolicy is deprecated already, this backports and uses the preferred \`loop\_factory\` keyword to configure asyncio.run
> \# Checklist
> 
> \- \[x\] I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
> \- \[x\] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
> \- \[\] I've updated the documentation accordingly.

---

<div class="post-metadata">

**Author:** ![tjreedy](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tjreedy/32/137_2.png) [@tjreedy](https://discuss.python.org/u/tjreedy)\
**Post date:** [October 31, 2023, 7:13pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/3 "2023-10-31T19:13:07Z")

</div>

The [issue](https://github.com/python/cpython/issues/94597) with some discussion. More can be added here.

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [October 31, 2023, 8:48pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/4 "2023-10-31T20:48:38Z")

</div>

Perhaps the most relevant [comment](https://github.com/python/cpython/issues/94597#issuecomment-1270840197) there has this:

> **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.)

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [October 31, 2023, 9:38pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/5 "2023-10-31T21:38:45Z")

</div>

What is the (“default”?) event loop implementation on Windows? ProactorEventLoop? Does it implement all features of SelectorEventLoop on Windows?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [October 31, 2023, 9:56pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/6 "2023-10-31T21:56:04Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [November 1, 2023, 12:34am UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/7 "2023-11-01T00:34:37Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [November 1, 2023, 12:50am UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/8 "2023-11-01T00:50:20Z")

</div>

You call run with `asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop)`

---

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [November 1, 2023, 12:52am UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/9 "2023-11-01T00:52:30Z")

</div>

> [@vstinner](#):
>
> Well, there was also an API to select the “child watcher” implementation. It also goes away, no?

That’s already deprecated in 3.12 asyncio uses the best available child watcher

---

<div class="post-metadata">

**Author:** ![kumaraditya303](https://avatars.discourse-cdn.com/v4/letter/k/a88e4f/32.png) [@kumaraditya303](https://discuss.python.org/u/kumaraditya303)\
**Post date:** [November 1, 2023, 1:09pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/10 "2023-11-01T13:09:21Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [November 1, 2023, 3:34pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/11 "2023-11-01T15:34:49Z")

</div>

> [@kumaraditya303](#):
>
> 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.

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.)

---

<div class="post-metadata">

**Author:** ![itamaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/itamaro/32/20137_2.png) [@itamaro](https://discuss.python.org/u/itamaro)\
**Post date:** [November 1, 2023, 6:37pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/12 "2023-11-01T18:37:50Z")

</div>

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:

1. 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+)
2. 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…

---

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [November 1, 2023, 7:02pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/13 "2023-11-01T19:02:48Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [November 1, 2023, 7:20pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/14 "2023-11-01T19:20:26Z")

</div>

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).

---

<div class="post-metadata">

**Author:** ![graingert](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/graingert/32/26847_2.png) [@graingert](https://discuss.python.org/u/graingert)\
**Post date:** [November 1, 2023, 7:25pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/15 "2023-11-01T19:25:40Z")

</div>

We can make get/set\_event\_loop still work without policies:

> [@graingert](#):
>
> 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)

---

<div class="post-metadata">

**Author:** ![itamaro](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/itamaro/32/20137_2.png) [@itamaro](https://discuss.python.org/u/itamaro)\
**Post date:** [November 1, 2023, 7:59pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/16 "2023-11-01T19:59:50Z")

</div>

> [@graingert](#):
>
> can you break down how many hits you get for get\_event\_loop\_policy vs set\_event\_loop\_policy?

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)

---

<div class="post-metadata">

**Author:** ![Tinche](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tinche/32/12507_2.png) [@Tinche](https://discuss.python.org/u/Tinche)\
**Post date:** [November 1, 2023, 8:40pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/17 "2023-11-01T20:40:23Z")

</div>

I’m in favor of dropping event loop policies, they always felt janky to me and I never actually needed them since `asyncio.run` was added.

> [@guido](#):
>
> 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”.

Why do we need this exactly? So there’s a way to get a platform’s best loop class?

---

<div class="post-metadata">

**Author:** ![guido](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/guido/32/21_2.png) [@guido](https://discuss.python.org/u/guido)\
**Post date:** [November 1, 2023, 9:16pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/18 "2023-11-01T21:16:37Z")

</div>

> [@Tinche](#):
>
> > [@guido](#):
> >
> > 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”.
> 
> Why do we need this exactly? So there’s a way to get a platform’s best loop class?

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?

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [November 1, 2023, 9:22pm UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/19 "2023-11-01T21:22:42Z")

</div>

> [@guido](#):
>
> 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

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?

---

<div class="post-metadata">

**Author:** ![encukou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/encukou/32/2461_2.png) [@encukou](https://discuss.python.org/u/encukou)\
**Post date:** [November 3, 2023, 10:02am UTC](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553/20 "2023-11-03T10:02:28Z")

</div>

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`.

- glibcoro: [glibcoro.py · master · Lawrence D’Oliveiro / glibcoro · GitLab](https://gitlab.com/ldo/glibcoro/-/blob/master/glibcoro.py?ref_type=heads#L526)
- Qt for Python: [eratosthenes\_asyncio.py « eratosthenes « async « examples - pyside/pyside-setup.git - Git super project for PySide](https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/async/eratosthenes/eratosthenes_asyncio.py#n137)

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`

[Next page](https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553.md?page=2)
