]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
chore(test): drop use of asyncio event loop policy
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 29 Aug 2025 16:52:42 +0000 (18:52 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 2 Sep 2025 22:24:39 +0000 (00:24 +0200)
Deprecated since Python 3.14

docs/advanced/async.rst
psycopg/psycopg/_compat.py
psycopg/psycopg/connection_async.py
tests/conftest.py
tests/pool/test_pool_async_noasyncio.py
tests/scripts/bench-411.py
tests/test_concurrency_async.py
tests/utils.py

index 6aa5990c78f06ce4b60b9dffc98cbfc75a9045b5..177075681aec09dde630884f274566ec4f8dc50d 100644 (file)
@@ -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
 
 
 
index e5f9121690027e9af11a68814cfb14d1a743ac47..0a82def5a0d65ab4bc66bc13e69af403fa98b9cb 100644 (file)
@@ -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:
index 8755648a5dd2acdc94fd2b69e26928ec1ac317c7..d37de7c7382466c0c994d2dcaac0b1377063c934 100644 (file)
@@ -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)
index e25ce52184ca73c333d9e73e972f812afab29fc4..4d7a4f6cb0466a2621a2247468286beea6a75a1e 100644 (file)
@@ -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()
     )
 
 
index dd79d9741051e6800f2468ba17ae0581ad2a86d1..602146f6dbf14576298add07bb7105be011c96d4 100644 (file)
@@ -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:
index 98d0986bcd3997830c7944357d7734deef1e7a89..06da98604a94c1d87c72bb62fe150aacc02ae697 100644 (file)
@@ -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
index 8f2123745f52380ca3a23979bbbea6851063c0ff..75277c08d2796dd3d952ad0fd54afee318b09753 100644 (file)
@@ -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:
index 3be844f5e268f8fed1a30b5cc5404561a6fc321d..e6c5799180b136170bc0eba6c8d386d18cfadb96 100644 (file)
@@ -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)