Skip to content

asyncio.get_event_loop() DeprecationWarning in Python 3.12 #255

Closed

Description

@ekenyon676
/.venv/lib/python3.12/site-packages/evdev/eventio_async.py:101: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()

asyncio.get_event_loop() produces a DeprecationWarning when called (see above for an example), and there are a couple of instances of get_event_loop being used in this package.

https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_event_loop

It should be relatively simple to switch over to get_running_loop. If it's helpful, I can put up a PR myself to make this change.

Activity

  1. sezanzeb commented on Jun 17, 2026

    @sezanzeb
    Collaborator

    Hm, I'm not getting a deprecation warning in python 3.13.7

    I can't say what asyncio good practices are, but I suppose you'd usually set your event loop before using python-evdev.

  2. ekenyon676 commented on Jun 17, 2026

    @ekenyon676
    ContributorAuthor

    According to the documentation:

    When called from a coroutine or a callback (e.g. scheduled with call_soon or similar API), this function will always return the running event loop.

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

    Because this function has rather complex behavior (especially when custom event loop policies are in use), using the get_running_loop() function is preferred to get_event_loop() in coroutines and callbacks.

    The get_event_loop() behavior is changing and the documentation says to use get_running_loop(), so that would be the good practice. get_event_loop() will eventually (supposedly in Python 3.16) do nothing but return a running/explicitly-set loop, never create one. Since python-evdev depends on the implicit-creation behavior, it will break.

    This code will reproduce the deprecation warning on Python 3.12 and 3.13:

    """Reproduce the evdev DeprecationWarning
    
    Run against published evdev (which still has the get_event_loop() call):
    
        uv run --python 3.12 --with evdev python repro_evdev_real.py
    
    Opens a real input device and closes it with no running/current event loop --
    the normal lifecycle of an InputDevice in a synchronous program. close() reaches
    evdev/eventio_async.py: `loop = asyncio.get_event_loop()`, which warns on 3.12/3.13.
    """
    import sys
    import warnings
    
    warnings.simplefilter("always")  # don't dedupe/suppress DeprecationWarning
    
    from importlib.metadata import version
    
    import evdev
    
    print(f"Python {sys.version.split()[0]}, evdev {version('evdev')}")
    
    # Find a device we can actually open (we're in the `input` group).
    dev = None
    for path in evdev.list_devices():
        try:
            dev = evdev.InputDevice(path)
            break
        except PermissionError:
            continue
    
    if dev is None:
        sys.exit("no openable /dev/input/event* device found")
    
    print(f"opened {dev.path} ({dev.name!r})")
    dev.close()  # <-- get_event_loop() warning fires here
    print("closed")

    In my code, I'm not using any of the async EventIO methods, but the async class is the one that's created. So that's why I get the warning on close.

  3. sezanzeb commented on Jun 17, 2026

    @sezanzeb
    Collaborator

    Thanks, in your snipped I do get the warning. I previously started my own application in a hurry to see if the log appears there, I probably create a loop somewhere in there myself.


    For future reference, here is the deprecation documented: https://docs.python.org/3/library/asyncio-policy.html#asyncio.DefaultEventLoopPolicy

    Changed in version 3.14: The get_event_loop() method of the default asyncio policy now raises a RuntimeError if there is no set event loop.

    So both methods will raise the error from 3.14 on

    And the docs say, that from 3.16 on, policies will be removed https://docs.python.org/3/library/asyncio-policy.html#policies

    i never worked with policies though. How is get_event_loop_policy().get_event_loop() different from asyncio.get_event_loop()?

  4. sezanzeb commented on Jun 17, 2026

    @sezanzeb
    Collaborator

    Does

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

    get_event_loop() will eventually (supposedly in Python 3.16) do nothing but return a running/explicitly-set loop, never create one

    Mean that get_event_loop creates a new event loop? I'll need to look more into this, but I'm kinda busy. If you know the answer already, I'll appreciate your help. I think it's kinda confusing, since the method to create a new loop is called new_event_loop, so I'd expect different behavior from policy.get_event_loop. Maybe this is part of the reason for the deprecations?

    I honestly haven't really worked with asyncio in quite a while. I'll need to understand this to make sure we aren't pushing a breaking change.

    I'll then run input-remappers tests with the change with various python versions, as python-evdev doesn't have lots of tests.

  5. ekenyon676 commented on Jun 18, 2026

    @ekenyon676
    ContributorAuthor

    Does

    If there is no running event loop set, the function will return the result of the get_event_loop_policy().get_event_loop() call.
    
    get_event_loop() will eventually (supposedly in Python 3.16) do nothing but return a running/explicitly-set loop, never create one
    

    Mean that get_event_loop creates a new event loop?

    If there isn't a running loop, it will create one. If there is already a running loop, it uses that.

    I think it's kinda confusing, since the method to create a new loop is called new_event_loop, so I'd expect different behavior from policy.get_event_loop. Maybe this is part of the reason for the deprecations?

    Yes, I think so. It does seem confusing.

    I think the way most people probably use the async EventIO class (including the way the code in the Tutorial works), they will already have a loop, so switching the underlying code over to get_running_loop shouldn't break them. There is a possibility that they're using the class to create a loop though, so it wouldn't be equivalent code.

    I don't think this is urgent, I just think it does need to get addressed since Python doesn't want code to use get_event_loop, and the DeprecationWarning is an indication of that. As I noted above, I actually use the synchronous class calls in my application, so I don't have a natural testbed for this. But I am happy to write something up for review. It may be a few weeks until I can do that.

  6. sezanzeb commented on Jul 9, 2026

    @sezanzeb
    Collaborator

    #258 that's it, right?

  7. ekenyon676 commented on Jul 13, 2026

    @ekenyon676
    ContributorAuthor

    I was thinking you'd want something more like #259.

  8. ekenyon676 commented on Jul 15, 2026

    @ekenyon676
    ContributorAuthor

    Let me know what you think of that PR. If it's acceptable, I can move it out of draft. I can also include my testing scripts and test results if you'd like. I don't see any information on contributing to this repo, but if I've missed something, let me know.

  9. ekenyon676 commented on Aug 19, 2026

    @ekenyon676
    ContributorAuthor

    Thanks for taking the PR a few weeks ago. Do you have an ETA on a release? Thank you!

  10. gvalkov commented on Aug 25, 2026

    @gvalkov
    Owner

    Hi @ekenyon676 . evdev 2.0.0 was released on Aug 23, 2026.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions