Skip to content

Fix Python 3.14 compatibility: replace deprecated asyncio.get_event_loop()#2831

Open
Jscoats wants to merge 1 commit intopostlund:masterfrom
Jscoats:fix/python-3.14-asyncio-compat
Open

Fix Python 3.14 compatibility: replace deprecated asyncio.get_event_loop()#2831
Jscoats wants to merge 1 commit intopostlund:masterfrom
Jscoats:fix/python-3.14-asyncio-compat

Conversation

@Jscoats
Copy link

@Jscoats Jscoats commented Mar 6, 2026

Summary

Fixes #2829 — All pyatv CLI scripts (atvremote, atvscript, atvlog, atvproxy) crash on Python 3.14 due to asyncio.get_event_loop() raising RuntimeError when no event loop exists in the current thread.

Changes

CLI entry points (4 scripts):

  • main(): get_event_loop() + run_until_complete()asyncio.run()
  • Internal functions: removed loop parameter threading, each now calls asyncio.get_running_loop() where needed

Core library (10 files):

  • All asyncio.get_event_loop()asyncio.get_running_loop() (in __init__ methods and async functions)

Additional deprecation fixes:

  • asyncio.StreamReader(loop=loop)asyncio.StreamReader() (param removed in 3.10)
  • asyncio.ensure_future(..., loop=loop)asyncio.ensure_future(...) (param removed in 3.10)
  • asyncio.wait([list])asyncio.wait(set(...)) in knock.py (list deprecated in 3.11)

Examples (9 files):

  • Modernized all examples to use asyncio.run() instead of get_event_loop().run_until_complete()

Tests:

  • Updated tests/scripts/conftest.py to call appstart() without loop
  • Added test_asyncio_compat.py — regression tests verifying all 4 CLI scripts bootstrap without a pre-existing event loop

What's NOT changed

  • Public API (pyatv.scan(), pyatv.connect(), pyatv.pair()) — all signatures preserved
  • asyncio.get_running_loop() is available since Python 3.7, so this is safe across all supported versions (3.9+)

Note on asyncio.run() vs loop.run_until_complete()

asyncio.run() additionally finalizes async generators and shuts down the default executor on exit, which is slightly better cleanup behavior. This has no observable effect on CLI usage.

Testing

  • All 1248 existing tests pass (0 failures, 6 skipped) + 4 new regression tests
  • flake8 clean on all changed files
  • grep -rn "get_event_loop" pyatv/ examples/ returns zero matches
  • Live tested against Apple TV 4K (tvOS 26.3): scan, connect, play, pause all working

…oop()

Replace all usage of asyncio.get_event_loop() which raises RuntimeError
in Python 3.14 when no event loop exists in the current thread.

Changes:
- CLI entry points: get_event_loop() + run_until_complete() -> asyncio.run()
- Core library: get_event_loop() -> get_running_loop() in async contexts
  and __init__ methods called within running event loops
- Remove deprecated loop= kwargs from StreamReader, ensure_future
- Fix asyncio.wait() to accept set instead of list (knock.py, atvscript.py)
- Modernize all examples to use asyncio.run()
- Add regression test verifying CLI scripts bootstrap without pre-existing
  event loop
- Update test conftest to match new appstart() signature

Public API (scan, connect, pair) signatures are preserved.

Fixes postlund#2829

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alexchandel
Copy link

Plz merge. This issue makes it impossible to set up pyatv on 3.14.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docker image for latest/0.17 fails to run any pyatv scripts

2 participants