From: Daniele Varrazzo Date: Fri, 29 Aug 2025 16:52:42 +0000 (+0200) Subject: chore(test): drop use of asyncio event loop policy X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=080604a1bced24b4a7ad243273327042ce04b421;p=thirdparty%2Fpsycopg.git chore(test): drop use of asyncio event loop policy Deprecated since Python 3.14 --- diff --git a/docs/advanced/async.rst b/docs/advanced/async.rst index 6aa5990c7..177075681 100644 --- a/docs/advanced/async.rst +++ b/docs/advanced/async.rst @@ -122,15 +122,9 @@ serialized. On Windows, Psycopg is not compatible with the default `~asyncio.ProactorEventLoop`. Please use a different loop, for instance - the `~asyncio.SelectorEventLoop`. + the `~asyncio.SelectorEventLoop`. See `asyncio documentation`__ for details. - For instance, you can use, early in your program: - - .. parsed-literal:: - - `asyncio.set_event_loop_policy`\ ( - `asyncio.WindowsSelectorEventLoopPolicy`\ () - ) + .. __: https://docs.python.org/3.14/library/asyncio-eventloop.html#asyncio.SelectorEventLoop diff --git a/psycopg/psycopg/_compat.py b/psycopg/psycopg/_compat.py index e5f912169..0a82def5a 100644 --- a/psycopg/psycopg/_compat.py +++ b/psycopg/psycopg/_compat.py @@ -11,6 +11,17 @@ if sys.version_info >= (3, 11): else: from typing_extensions import LiteralString, Self +if sys.version_info >= (3, 12): + _asyncio_run_snippet = ( + "running 'asyncio.run(...," + " loop_factory=asyncio.SelectorEventLoop(selectors.SelectSelector()))'" + ) + +else: + _asyncio_run_snippet = ( + "setting 'asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())'" + ) + if sys.version_info >= (3, 13): from typing import TypeVar else: diff --git a/psycopg/psycopg/connection_async.py b/psycopg/psycopg/connection_async.py index 8755648a5..d37de7c73 100644 --- a/psycopg/psycopg/connection_async.py +++ b/psycopg/psycopg/connection_async.py @@ -100,11 +100,13 @@ class AsyncConnection(BaseConnection[Row]): if sys.platform == "win32": loop = asyncio.get_running_loop() if isinstance(loop, asyncio.ProactorEventLoop): + + from ._compat import _asyncio_run_snippet + raise e.InterfaceError( "Psycopg cannot use the 'ProactorEventLoop' to run in async" " mode. Please use a compatible event loop, for instance by" - " setting 'asyncio.set_event_loop_policy" - "(WindowsSelectorEventLoopPolicy())'" + + f" {_asyncio_run_snippet}" ) params = await cls._get_connection_params(conninfo, **kwargs) diff --git a/tests/conftest.py b/tests/conftest.py index e25ce5218..4d7a4f6cb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -71,8 +71,8 @@ def pytest_sessionstart(session): asyncio_options: dict[str, Any] = {} if sys.platform == "win32": - asyncio_options["loop_factory"] = ( - asyncio.WindowsSelectorEventLoopPolicy().new_event_loop + asyncio_options["loop_factory"] = lambda: asyncio.SelectorEventLoop( + selectors.SelectSelector() ) diff --git a/tests/pool/test_pool_async_noasyncio.py b/tests/pool/test_pool_async_noasyncio.py index dd79d9741..602146f6d 100644 --- a/tests/pool/test_pool_async_noasyncio.py +++ b/tests/pool/test_pool_async_noasyncio.py @@ -1,11 +1,12 @@ # These tests relate to AsyncConnectionPool, but are not marked asyncio # because they rely on the pool initialization outside the asyncio loop. -import sys import asyncio import pytest +from tests.utils import asyncio_run as asyncio_run_ + try: import psycopg_pool as pool except ImportError: @@ -67,11 +68,9 @@ def asyncio_run(recwarn, gc_collect): In certain runs, fd objects are leaked and the error will only be caught downstream, by some innocent test calling gc_collect(). """ - if sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) recwarn.clear() try: - yield asyncio.run + yield asyncio_run_ finally: gc_collect() if recwarn: diff --git a/tests/scripts/bench-411.py b/tests/scripts/bench-411.py index 98d0986bc..06da98604 100644 --- a/tests/scripts/bench-411.py +++ b/tests/scripts/bench-411.py @@ -6,6 +6,7 @@ import time import random import asyncio import logging +import selectors from enum import Enum from typing import Any from argparse import ArgumentParser, Namespace @@ -76,13 +77,18 @@ def main() -> None: elif name == Driver.psycopg_async: import psycopg + kwargs: dict[str, Any] = {} if sys.platform == "win32": - if hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): + if sys.version_info >= (3, 12): + kwargs["loop_factory"] = lambda: asyncio.SelectorEventLoop( + selectors.SelectSelector() + ) + else: asyncio.set_event_loop_policy( asyncio.WindowsSelectorEventLoopPolicy() ) - asyncio.run(run_psycopg_async(psycopg, args)) + asyncio.run(run_psycopg_async(psycopg, args), **kwargs) elif name == Driver.asyncpg: import asyncpg # type: ignore diff --git a/tests/test_concurrency_async.py b/tests/test_concurrency_async.py index 8f2123745..75277c08d 100644 --- a/tests/test_concurrency_async.py +++ b/tests/test_concurrency_async.py @@ -410,6 +410,7 @@ def test_type_error_shadow(dsn): import sys import uuid import asyncio +import selectors import psycopg @@ -460,10 +461,17 @@ async def main() -> None: if __name__ == "__main__": + kwargs = {{}} if sys.platform == "win32": - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + if sys.version_info >= (3, 12): + kwargs["loop_factory"] = lambda: asyncio.SelectorEventLoop( + selectors.SelectSelector() + ) + else: + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + try: - asyncio.run(main()) + asyncio.run(main(), **kwargs) finally: assert excs, "No exception raised by workers" for ex in excs: diff --git a/tests/utils.py b/tests/utils.py index 3be844f5e..e6c579918 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,7 +2,10 @@ from __future__ import annotations import re import sys +import asyncio import operator +import selectors +from typing import Any from contextlib import contextmanager from collections.abc import Callable @@ -179,3 +182,22 @@ def set_autocommit(conn, value): return conn.set_autocommit(value) else: raise TypeError(f"not a connection: {conn}") + + +def windows_loop_factory() -> asyncio.AbstractEventLoop: + return asyncio.SelectorEventLoop(selectors.SelectSelector()) + + +def asyncio_run(coro: Any, *, debug: bool | None = None) -> Any: + # loop policies are deprecated from Python 3.14 + # loop_factory was introduced in Python 3.12 + kwargs: dict[str, Any] = {} + if sys.platform == "win32": + if sys.version_info >= (3, 12): + kwargs["loop_factory"] = lambda: asyncio.SelectorEventLoop( + selectors.SelectSelector() + ) + else: + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + return asyncio.run(coro, debug=debug, **kwargs)