diff --git a/newsfragments/160.bugfix.rst b/newsfragments/160.bugfix.rst new file mode 100644 index 00000000..47652591 --- /dev/null +++ b/newsfragments/160.bugfix.rst @@ -0,0 +1,2 @@ +Fix importing ``trio_asyncio`` on Python 3.14 and later, where asyncio renamed +``BaseDefaultEventLoopPolicy``. diff --git a/tests/test_misc.py b/tests/test_misc.py index be83b047..71805e0f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,3 +1,4 @@ +import subprocess import pytest import trio_asyncio import asyncio @@ -8,6 +9,22 @@ from exceptiongroup import ExceptionGroup, BaseExceptionGroup +def test_import_with_private_base_event_loop_policy() -> None: + script = """ +import asyncio.events + +base_policy = getattr(asyncio.events, "BaseDefaultEventLoopPolicy", None) +if base_policy is None: + base_policy = asyncio.events._BaseDefaultEventLoopPolicy +else: + del asyncio.events.BaseDefaultEventLoopPolicy +asyncio.events._BaseDefaultEventLoopPolicy = base_policy + +import trio_asyncio +""" + subprocess.run([sys.executable, "-c", script], check=True) + + class Seen: flag = 0 diff --git a/trio_asyncio/_loop.py b/trio_asyncio/_loop.py index 58edfc5f..48175fcf 100644 --- a/trio_asyncio/_loop.py +++ b/trio_asyncio/_loop.py @@ -119,7 +119,15 @@ def _in_trio_context_other_than_sync_loop(): return False -class _TrioPolicy(asyncio.events.BaseDefaultEventLoopPolicy): +# Python 3.14 renamed this private implementation class with no compatibility +# alias. Keep supporting older Python versions while accepting the new name. +try: + _BaseDefaultEventLoopPolicy = asyncio.events.BaseDefaultEventLoopPolicy +except AttributeError: + _BaseDefaultEventLoopPolicy = asyncio.events._BaseDefaultEventLoopPolicy + + +class _TrioPolicy(_BaseDefaultEventLoopPolicy): @staticmethod def _loop_factory(): raise RuntimeError("Event loop creations shouldn't get here")