Skip to content

Finish deprecation in asyncio.get_event_loop() #93453

Description

@serhiy-storchaka

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 make asyncio.get_event_loop() an alias of asyncio.get_running_loop().

But maybe we should first deprecate set_event_loop()? It will be a no-op now.

Linked PRs

Activity

  1. lschoe commented on Jun 4, 2022

    @lschoe

    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 -Werror flag), any existing code calling aysncio.get_event_loop() outside a running event loop will break.

    A simple workaround that worked in my case is to call asyncio.get_event_loop_policy().get_event_loop() instead. This will return the loop already attached to the current thread, or create a new one if not existing yet. (This seems better than just calling asyncio.new_event_loop() because that creates an extra loop next to already existing loops.)

    Will this workaround of using asyncio.get_event_loop_policy().get_event_loop() remain available in Python 3.12+?

  2. dantownsend commented on Jun 8, 2022

    @dantownsend

    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 asyncio.get_event_loop_policy().get_event_loop() a valid workaround?

  3. serhiy-storchaka commented on Jun 9, 2022

    @serhiy-storchaka
    MemberAuthor

    Ask @asvetlov. I do not know plans about asyncio.get_event_loop_policy().get_event_loop(). It may be deprecated too: you either use the existing running loop, or explicitly create a new one.

    asyncio.get_event_loop() was initially proposed to remove, but since it is use so much in the code that predates asyncio.get_running_loop(), it was decided to make it just an alias of asyncio.get_running_loop().

  4. dantownsend commented on Jun 9, 2022

    @dantownsend
  5. lschoe commented on Jun 13, 2022

    @lschoe

    Thanks as well. It's good to have something next to asyncio.get_running_loop() because that can be only be called from a running loop;) For lower-level usage of asyncio's event loops, accessing a loop even when it's not running seems still very useful to me. (For higher-level usage stricter, disciplined calls using asyncio.run() etc., not handling e.g. any bare asyncio.Futures is indeed advised.)

    For example, I want to call loop.set_exception_handler(handler) to set my own exception handler for the event loop. To do this once and for all, it's convenient to do this before the loop is running. Not sure what the intended way to do this would be, if one can only access the loop after it started running?

  6. graingert commented on Jun 16, 2022

    @graingert
    Contributor

    @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 asyncio.get_running_loop().set_exception_handler(...) as the first thing in your async def amain(): seems to be the best choice

  7. lschoe commented on Jun 16, 2022

    @lschoe

    Thanks, these are interesting options, indeed for Python 3.11+ which will have this new Runner class.

    Using a loop_factory() does not seem preferable because asyncio.new_event_loop() is still called, which may cause problems.
    If a loop is already attached to the current thread, this will create a new loop next to the existing one. However, only one of these two loops can run at the same time, so this gives a Runtime error if one tries to run the other one.

    With asyncio.get_event_loop() you can join the existing loop, if any. Whether that loop is running or not doesn't matter either.

    For example, one can run some code in Python's asyncio REPL (via python -m asyncio) or in Jupyter notebooks, where there is already a loop running providing top-level await, or one can run the same code from a Python script where there is no loop running yet.

    I've also tried the second way you propose, calling runner.get_loop(), but this also gives a Runtime error when called from the asyncio REPL, for example.

    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 asyncio.get_event_loop() is very useful.

  8. graingert commented on Jun 24, 2022

    @graingert
    Contributor

    But maybe we should first deprecate set_event_loop()? It will be a no-op now.

    @serhiy-storchaka set_event_loop is not a no-op it sets up the child watcher system #93896

  9. gvanrossum commented on Oct 5, 2022

    @gvanrossum
    Member

    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?

  10. 68 remaining items

  11. added a commit that references this issue on Dec 16, 2022
  12. hroncok commented on Dec 21, 2022

    @hroncok
    Contributor

    The documentation of asyncio.get_event_loop() says:

    If there is no running event loop set, the function will return the result of calling get_event_loop_policy().get_event_loop().

    Which by default raises a RuntimeError, so it is rather confusing. Should the documentation be updated to explicitly say something like:

    If there is no running event loop set, the function will return the result of calling get_event_loop_policy().get_event_loop() which raises a RuntimError with DefaultEventLoopPolicy.

    ?

  13. added a commit that references this issue on Dec 21, 2022
  14. gvanrossum commented on Dec 21, 2022

    @gvanrossum
    Member

    Oooh, the decumentation is indeed a mess. There's no place that I can find where the behavior of DefaultEventLoopPolicy.get_event_loop() is currently documented. AbstractEventLoopPolicy is vague (just says it returns a loop and never None), DefaultEventLoopPolicy doesn't say anything specific, and BaseDefaultEventLoopPolicy is undocumented (and private, apparently, though in the light of the PEP 387 discussions it might be implicitly public). The documentation section linked to should be hyperlinked to get_event_loop_policy() and some policy class's get_event_loop() method, which should be documented.

  15. Yhg1s commented on Jan 9, 2023

    @Yhg1s
    Member

    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?

  16. gvanrossum commented on Jan 10, 2023

    @gvanrossum
    Member

    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?

  17. serhiy-storchaka commented on Jan 10, 2023

    @serhiy-storchaka
    MemberAuthor

    I waited for approving #100412. After that, documentation changes in #100412 should be made also in #100410 if relevant.

  18. gvanrossum commented on Jan 10, 2023

    @gvanrossum
    Member

    Okay, I just merged #100412, after cleaning up the docs there a bit. Can you add those docs tweaks to #100410 and then merge it?

  19. added a commit that references this issue on Jan 13, 2023
  20. moved this from In Progress to Done in asyncioon Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions