GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Deprecate get_event_loop() · Issue #83710 · python/cpython

Deprecate get_event_loop() #83710

New issue
Copy link
New issue
Copy link
Closed
Closed
Deprecate get_event_loop()#83710
Copy link
Labels
3.10 (EOL)end of lifeend of lifetopic-asynciotype-featureA feature request or enhancementA feature request or enhancement

Description

@asvetlov
asvetlov
opened on Feb 2, 2020
Issue body actions
BPO 39529
Nosy @asvetlov, @bdarnell, @serhiy-storchaka, @1st1, @minrk, @tirkarthi, @aeros, @johnmellor
PRs
  • bpo-39529: Deprecate creating new event loop in asyncio.get_event_loop() #23554
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2021-04-25.10:43:30.491>
    created_at = <Date 2020-02-02.11:11:37.189>
    labels = ['type-feature', '3.10', 'expert-asyncio']
    title = 'Deprecate get_event_loop()'
    updated_at = <Date 2022-02-25.11:20:12.956>
    user = 'https://github.com/asvetlov'

    bugs.python.org fields:

    activity = <Date 2022-02-25.11:20:12.956>
    actor = 'johnmellor'
    assignee = 'none'
    closed = True
    closed_date = <Date 2021-04-25.10:43:30.491>
    closer = 'serhiy.storchaka'
    components = ['asyncio']
    creation = <Date 2020-02-02.11:11:37.189>
    creator = 'asvetlov'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 39529
    keywords = ['patch']
    message_count = 21.0
    messages = ['361229', '361245', '361247', '361248', '361249', '361609', '362487', '362674', '381860', '381881', '382044', '391851', '394732', '394821', '407429', '407430', '407432', '407441', '407448', '407453', '407600']
    nosy_count = 8.0
    nosy_names = ['asvetlov', 'Ben.Darnell', 'serhiy.storchaka', 'yselivanov', 'minrk', 'xtreak', 'aeros', 'johnmellor']
    pr_nums = ['23554']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue39529'
    versions = ['Python 3.10']

    Activity

    Next
    1. asvetlov commented on Feb 2, 2020

      @asvetlov
      asvetlov
      on Feb 2, 2020
      ContributorAuthor
      More actions

      Yuri proposed it for Python 3.8 but at that time the change was premature.
      Now we can reconsider it for 3.9

      The problem is that asyncio.get_event_loop() not only returns a loop but also creates it on-demand if the thread is main and the loop doesn't exist.

      It leads to weird errors when get_event_loop() is called at import-time and asyncio.run() is used for asyncio code execution.

      get_running_loop() is a much better alternative when used *inside* a running loop, run() should be preferred for calling async code at top-level. Low-level new_event_loop()/loop.run_until_complete() are still present to run async code if top-level run() is not suitable for any reason.

      asyncio.run() was introduced in 3.7, deprecation on get_event_loop() in 3.8 was able to complicate support of 3.5/3.6 by third-party libraries.
      3.5 now reached EOL, 3.6 is in the security-fix mode and going close to EOL. Most people are migrated to newer versions already if they care.
      The maintenance burden of the introduced deprecation should be pretty low.

    2. serhiy-storchaka commented on Feb 2, 2020

      @serhiy-storchaka
      serhiy-storchaka
      on Feb 2, 2020
      Member
      More actions

      Will asyncio.get_event_loop() be removed or made an alias of asyncio.get_running_loop()? The latter could minimize the disruption of the existing code.

    3. asvetlov commented on Feb 2, 2020

      @asvetlov
      asvetlov
      on Feb 2, 2020
      ContributorAuthor
      More actions

      Currently, I'm talking about adding a deprecation only.
      The asyncio.get_event_loop() function will stay in Python for a while in deprecated status. I don't know the exact period but it should be 3 releases at least I guess, with possibly extending to 5 releases if needed.

    4. asvetlov commented on Feb 2, 2020

      @asvetlov
      asvetlov
      on Feb 2, 2020
      ContributorAuthor
      More actions

      Serhiy, maybe I had not understood your proposal properly.

      If you are asking about deprecating get_event_loop() call from outside of async code but implicit call get_running_loop() without a warning if called from async function -- it sounds like a brilliant idea.

      Something like:

      def get_event_loop():
          current_loop = _get_running_loop()
          if current_loop is not None:
              return current_loop
          warnings.warn("...", DeprecationWarning)  
          return get_event_loop_policy().get_event_loop()
    5. asvetlov
      added
      topic-asyncio
      on Feb 2, 2020
    6. serhiy-storchaka commented on Feb 2, 2020

      @serhiy-storchaka
      serhiy-storchaka
      on Feb 2, 2020
      Member
      More actions

      Yes, it is what I meant.

      Many code written before 3.7 use get_event_loop() because there was no get_running_loop() yet. Now, to avoid deprecation (and making the code more robust) it should be rewritten with using get_running_loop() or get_event_loop() depending on Python version. It is cumbersome, and causes a code churn.

    7. 1st1 commented on Feb 7, 2020

      @1st1
      1st1
      on Feb 7, 2020
      Member
      More actions

      I think we still use get_event_loop() in asyncio code, no? If we do, we should start by raising deprecation warnings in those call sites, e.g. if a Future or Lock is created outside of a coroutine and no explicit event loop is passed. We should do this in 3.9. We can then think about deprecating get_event_loop in 3.10.

    8. aeros commented on Feb 23, 2020

      @aeros
      aeros
      on Feb 23, 2020
      Contributor
      More actions

      FWIW, I agree with get_event_loop() being problematic with its error messages, and having a bit too much functionality in a single function from an API design perspective. It commonly comes up as an issue among asyncio users in communities that I'm active in.

      Typically, I advise them to use get_running_loop() where possible, as it can be directly substituted within a coroutine func. For __init__ methods, I recommend setting their internal loop attribute to None and then setting it to get_running_loop() in either a coro start() method or coro factory method. Outside of directly managing the event loop, this works for many use cases. When it's needed to actually create one, I advise them to use new_event_loop(), but mention that it's already handled if they're using asyncio.run().

      Andrew Svetlov wrote:

      The asyncio.get_event_loop() function will stay in Python for a while in deprecated status. I don't know the exact period but it should be 3 releases at least I guess, with possibly extending to 5 releases if needed.

      With how many libraries that rely on it, I suspect it will likely be a very slow transition from deprecation to removal. 4 versions seems like a reasonable period to me, but I think that 3 may be too short (assuming we retain the newer annual release cycle).

      Yury Selivanov wrote:

      I think we still use get_event_loop() in asyncio code, no?

      Indeed, we currently use it in several places throughout asyncio. From a brief glace using git grep "get_event_loop()":

      Lib/asyncio/futures.py:76: self._loop = events.get_event_loop()
      Lib/asyncio/futures.py:390: loop = events.get_event_loop()
      Lib/asyncio/locks.py:81: self._loop = events.get_event_loop()
      Lib/asyncio/locks.py:177: self._loop = events.get_event_loop()
      Lib/asyncio/locks.py:244: self._loop = events.get_event_loop()
      Lib/asyncio/locks.py:375: self._loop = events.get_event_loop()
      Lib/asyncio/queues.py:35: self._loop = events.get_event_loop()
      Lib/asyncio/streams.py:45: loop = events.get_event_loop()
      Lib/asyncio/streams.py:82: loop = events.get_event_loop()
      Lib/asyncio/streams.py:104: loop = events.get_event_loop()
      Lib/asyncio/streams.py:120: loop = events.get_event_loop()
      Lib/asyncio/streams.py:147: self._loop = events.get_event_loop()
      Lib/asyncio/streams.py:403: self._loop = events.get_event_loop()
      Lib/asyncio/subprocess.py:206: loop = events.get_event_loop()
      Lib/asyncio/subprocess.py:227: loop = events.get_event_loop()
      Lib/asyncio/tasks.py:69: loop = events.get_event_loop()
      Lib/asyncio/tasks.py:129: loop = events.get_event_loop()
      Lib/asyncio/tasks.py:590: loop = events.get_event_loop()
      Lib/asyncio/tasks.py:669: loop = events.get_event_loop()
      Lib/asyncio/tasks.py:751: loop = events.get_event_loop()

      For brevity, I omitted the docs, tests, and the function definition for get_event_loop().

      Based on Serhiy's idea (of making get_event_loop() an alias for get_running_loop() without warning inside of a coro func, but warning for using it outside of one), many of the above could remain as is to reduce some code churn. We just have to make sure the documentation is updated to reflect get_event_loop() becoming an alias for get_running_loop(), at the same time as the deprecation warning is added for using it outside of a coro func. Otherwise, I suspect it could lead to significant confusion from users that have warnings enabled.

      That being said, I think we should eventually remove asyncio.get_event_loop() entirely from the asyncio internals, including the ones that wouldn't raise deprecation warnings (if/when it's made an alias to get_running_loop()) for improved clarity. Personally, I find that even the name get_event_loop() is rather vague; get_running_loop() is much more obvious as to its purpose and what it does from a readability perspective.

      Yury Selivanov wrote:

      If we do, we should start by raising deprecation warnings in those call sites, e.g. if a Future or Lock is created outside of a coroutine and no explicit event loop is passed.

      For asyncio.Lock (plus other synchronization primitives) and asyncio.Queue, this would be added in #18195. Currently waiting on emanu (author of the PR) to finish up some changes, but it's mostly complete. Could I work on adding it to asyncio.Future and other classes in the meantime?

      One point to be clarified though: you mention "created outside of a coroutine and no explicit event loop is passed". However, there are already several deprecations in place for passing an explicit event loop for most (if not all) of the __init__ methods for objects across asyncio's high-level API. In those cases, should the deprecation for creating the object outside of a coroutine function care about whether or not an explicit event loop is passed?

      I can see why it would matter for the lower level parts of the API (such as asyncio.Future) where passing the event loop explicitly is still allowed, but IMO it shouldn't be a factor for ones where passing the event loop explicitly is already deprecated. Especially considering that the loop argument will be removed from those entirely in 3.10 (according to the version listed in the current deprecation warnings added in 3.8).

    9. 1st1 commented on Feb 26, 2020

      @1st1
      1st1
      on Feb 26, 2020
      Member
      More actions

      For asyncio.Lock (plus other synchronization primitives) and asyncio.Queue, this would be added in #18195. Currently waiting on emanu (author of the PR) to finish up some changes, but it's mostly complete. Could I work on adding it to asyncio.Future and other classes in the meantime?

      I think the approach should be different:

        # leading underscore is significant:
        loop = asyncio._get_running_loop()  
        if loop is None:
          issue_deprecation_warning()
          loop = asyncio.get_event_loop()
    10. serhiy-storchaka commented on Nov 25, 2020

      @serhiy-storchaka
      serhiy-storchaka
      on Nov 25, 2020
      Member
      More actions

      I am writing a code (it is not trivial to emit correct warning) and I have a question. What to do if the loop was set by set_event_loop()? Currently get_event_loop() can return a loop without creating a new loop while get_running_loop() raises an error. Should get_event_loop() still support this in future or set_event_loop() will be deprecated?

    11. asvetlov commented on Nov 26, 2020

      @asvetlov
      asvetlov
      on Nov 26, 2020
      ContributorAuthor
      More actions

      I think the deprecation of set_event_loop() is a good idea.
      The function is not required by asyncio.run() implementation.

    12. serhiy-storchaka commented on Nov 29, 2020

      @serhiy-storchaka
      serhiy-storchaka
      on Nov 29, 2020
      Member
      More actions

      The most visible effect in tests is that asyncio.gatcher() and some other synchronous functions will need a running loop if any of arguments is a coroutine.

    13. serhiy-storchaka commented on Apr 25, 2021

      @serhiy-storchaka
      serhiy-storchaka
      on Apr 25, 2021
      Member
      More actions

      New changeset 172c0f2 by Serhiy Storchaka in branch 'master':
      bpo-39529: Deprecate creating new event loop in asyncio.get_event_loop() (GH-23554)
      172c0f2

    14. serhiy-storchaka
      added
      3.10 (EOL)end of lifeend of life
      on Apr 25, 2021
    15. 7 remaining items

    16. minrk commented on Dec 1, 2021

      @minrk
      minrkmannequin
      on Dec 1, 2021
      Mannequin
      More actions

      The comments in this thread suggest that set_event_loop should also be deprecated, but it hasn't been. It doesn't seem to have any use without get_event_loop().

      I'm trying to understand the consequences of these changes for IPython, and make the changes intended by asyncio folks, but am not quite clear, yet.

      If I understand it correctly, this means that the whole concept of a 'current' event loop is deprecated while no event loop is running?

      My interpretation of these changes is that it means any persistent handles on any event loop while it isn't running is fully the responsibility of individual libraries (e.g. tornado, IPython).

      This is coming up in IPython where we need a handle on the event loop and advance it with run_until_complete for each iteration (it should be the same loop to maintain persistent state across advances, so asyncio.run() would not be appropriate). We previously relied on get_event_loop to manage this handle, but I think we have to now shift to tracking our own handle, and can no longer rely on standard APIs to track a shared instance across packages.

    17. minrk commented on Dec 1, 2021

      @minrk
      minrkmannequin
      on Dec 1, 2021
      Mannequin
      More actions

      Further digging reveals that policy.get_event_loop() is not deprecated while asyncio.get_event_loop() is. Is that intentional? Does that mean switching our calls to get_event_loop_policy().get_event_loop() should continue to work without deprecation?

    18. asvetlov commented on Dec 1, 2021

      @asvetlov
      asvetlov
      on Dec 1, 2021
      ContributorAuthor
      More actions

      IMHO, asyncio.set_event_loop() and policy.get_event_loop()/policy.set_event_loop() are not deprecated by oversight.

      In IPython, I think you could use new_event_loop() for getting a new loop instance.
      Then, save the loop reference somewhere as a direct attribute, threading.local or ContextVar.
      Calling loop.run_until_complete() looks pretty normal in your situation.

      At my job, we have Runner class, the basic usage is:
      with Runner() as runner:
      runner.run(async_func())

      The implementation is pretty close to asyncio.run() but runner.run(...) can be called multiple times. Async context manager interface is responsible for resource closing.

      Maybe I should extract the implementation into a third-party library, I've found this concept useful for CLI applications at least.

    19. minrk commented on Dec 1, 2021

      @minrk
      minrkmannequin
      on Dec 1, 2021
      Mannequin
      More actions

      Thank you! I think I have enough information to update.

      IMHO, asyncio.set_event_loop()...[is] not deprecated by oversight.

      I'm curious, what is an appropriate use of asyncio.set_event_loop() if you can never get the event loop with get_event_loop()? If you always have to pass the handle around anyway, I'm not sure what the use case for a write-only global would be.

    20. asvetlov commented on Dec 1, 2021

      @asvetlov
      asvetlov
      on Dec 1, 2021
      ContributorAuthor
      More actions

      Ages ago, get_event_loop() did not return the current running loop if called from async function context; it returned a loop instance registered with previous set_event_loop(...) call instead.

      Later we fixed get_event_loop() behavior in 3.5.4 IIRC and added get_running_loop() in 3.7

      set_event_loop() doesn't make sense now from my perspective.

    21. minrk commented on Dec 1, 2021

      @minrk
      minrkmannequin
      on Dec 1, 2021
      Mannequin
      More actions

      Oops, I interpreted "not deprecated by oversight" as the opposite of what you meant. Sorry! All clear, now.

    22. bdarnell commented on Dec 3, 2021

      @bdarnell
      bdarnellmannequin
      on Dec 3, 2021
      Mannequin
      More actions

      In IPython, I think you could use new_event_loop() for getting a new loop instance.
      Then, save the loop reference somewhere as a direct attribute, threading.local or ContextVar.
      Calling loop.run_until_complete() looks pretty normal in your situation.

      That works if you're a leaf in the dependency tree, but what about a library like Tornado (which IPython depends on)? If intermediate libraries (say Tornado and Twisted) introduce their own thread-local loops for backwards compatibility of their existing interfaces, the ecosystem gets fragmented again. Having the thread-local live in asyncio is useful for seamless interoperability across frameworks.

      asyncio.run() is entirely self-reliant in that it creates all needed resources at the start and closes them in finalization, rather than depending on existing resources. I believe this to be significantly safer and better guaranteed to function as intended, although perhaps at some cost to convenience in cases like your own where there only needs to be one event loop.

      Whether or not this is more likely to function as intended depends on assumptions about user expectations. In Tornado (and other async systems I have used in the past), it is the norm to set up handlers on an event loop while it is not running and then start it afterwards. The behavior of asyncio.run is surprising to me. Maybe I'm weird, but regardless of which behavior is surprising to the fewest people, my point is that this change is very disruptive to Tornado and most applications using it, contrary to the claim that the maintenance burden should be pretty low.

      I realize it may be too late to change course, but my preference would have been to resolve the conflict between get_event_loop and asyncio.run by making asyncio.run essentially an alias for asyncio.get_event_loop().run_until_complete. This would relax the isolation of asyncio.run, but that doesn't seem very important to me. The comparable idioms in Tornado (using the IOLoop.run_sync method) all work this way and I've never seen any confusion related to this or a situation in which creating a brand-new event loop in run_sync would be desirable.

    23. ezio-melotti
      transferred this issue fromon Apr 10, 2022
    24. SomberNight
      added 2 commits that reference this issue on Apr 29, 2022

      asyncio: stop using get_event_loop(). introduce ~singleton loop.

      b6a14a2

      asyncio: stop using get_event_loop(). introduce ~singleton loop.

      2c57c78
    25. graingert commented on Jun 1, 2022

      @graingert
      graingert
      on Jun 1, 2022
      Contributor
      More actions

      get_event_loop was deprecated in 3.10, does this mean it will be an alias for get_running_loop in 3.12?

    26. serhiy-storchaka commented on Jun 1, 2022

      @serhiy-storchaka
      serhiy-storchaka
      on Jun 1, 2022
      Member
      More actions

      It was not deprecated. Only its use outside of the event loop was deprecated. It will be an alias of get_running_loop.

    27. graingert commented on Jun 1, 2022

      @graingert
      graingert
      on Jun 1, 2022
      Contributor
      More actions

      @serhiy-storchaka Yes, on 3.12 will it be an alias of get_running_loop?

    28. danigm
      added a commit that references this issue on Dec 15, 2022

      Make run_sync compatible with python 3.10.9

      0bd859c
    29. TheButterZone
      added a commit that references this issue on Aug 31, 2026

      asyncio: stop using get_event_loop(). introduce ~singleton loop.

      7f107cd
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.10 (EOL)end of lifeend of lifetopic-asynciotype-featureA feature request or enhancementA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        You are visitor 01402503 — GeezerVison does not store the pages you look up.
        Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.