From cb3b00f515892b75dc7079c1222a8c352fb43065 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 16 Sep 2026 15:08:28 -0700 Subject: [PATCH] chore: Clean up redundant pytest-asyncio patches These all went into testlib, so they can be removed from all the test suites. --- packages/runtime-sdk/tests/test_in_workerd.py | 3 +++ .../fastapi-tests/src/worker.py | 9 -------- .../asgi-ws-disconnect/pyproject.toml | 5 +++- .../workerd-test/asgi-ws-disconnect/worker.py | 21 ++--------------- .../tests/workerd-test/asgi/pyproject.toml | 5 +++- .../tests/workerd-test/asgi/worker.py | 22 ++---------------- .../tests/workerd-test/sdk/pyproject.toml | 5 +++- .../tests/workerd-test/sdk/worker.py | 23 ++----------------- .../tests/workerd-test/wsgi/pyproject.toml | 5 +++- .../tests/workerd-test/wsgi/worker.py | 22 ++---------------- packages/testlib/testlib/entrypoint.py | 11 +++++++++ 11 files changed, 38 insertions(+), 93 deletions(-) diff --git a/packages/runtime-sdk/tests/test_in_workerd.py b/packages/runtime-sdk/tests/test_in_workerd.py index 8f8e85ab..dfaefe36 100644 --- a/packages/runtime-sdk/tests/test_in_workerd.py +++ b/packages/runtime-sdk/tests/test_in_workerd.py @@ -6,6 +6,7 @@ import pytest from conftest import ( COMPAT_CONFIGS, + TESTLIB, CompatConfig, configure_compatibility, ) @@ -91,6 +92,8 @@ def test_in_workerd( # noqa: PLR0913, PLR0917 (too-many-arguments) target = tmp_path / test_dir.name disk_service_dir = target / DISK_SERVICE_NAME shutil.copytree(test_dir, target, ignore=shutil.ignore_patterns(".venv")) + # Worker projects depend on testlib via a relative wheel path (../testlib). + shutil.copytree(TESTLIB, tmp_path / "testlib") disk_service_dir.mkdir(exist_ok=True) configure_compatibility(target / "wrangler.jsonc", compat_config) diff --git a/packages/runtime-sdk/tests/web-frameworks-test/fastapi-tests/src/worker.py b/packages/runtime-sdk/tests/web-frameworks-test/fastapi-tests/src/worker.py index 0d336c4e..45de65bb 100644 --- a/packages/runtime-sdk/tests/web-frameworks-test/fastapi-tests/src/worker.py +++ b/packages/runtime-sdk/tests/web-frameworks-test/fastapi-tests/src/worker.py @@ -30,21 +30,12 @@ from fastapi.responses import Response as FastAPIResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel -from pyodide.webloop import WebLoop from starlette.background import BackgroundTask from starlette.middleware.gzip import GZipMiddleware from testlib.entrypoint import TestRunner import asgi - -async def _noop(*args): - pass - - -WebLoop.shutdown_asyncgens = _noop -WebLoop.shutdown_default_executor = _noop - STATIC_DIR = Path(__file__).parent / "static" diff --git a/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/pyproject.toml b/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/pyproject.toml index f7d33188..325e057d 100644 --- a/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +++ b/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/pyproject.toml @@ -2,4 +2,7 @@ name = "test" version = "0.0.0" requires-python = ">=3.12" -dependencies = ["pytest", "pytest-asyncio<1.2.0"] +dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib"] + +[tool.uv.sources] +testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } diff --git a/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/worker.py b/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/worker.py index d1b9e5e8..e847ba31 100644 --- a/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/worker.py +++ b/packages/runtime-sdk/tests/workerd-test/asgi-ws-disconnect/worker.py @@ -1,29 +1,12 @@ -import asyncio import os -import sys from urllib.parse import urlsplit -import pytest -from pyodide.webloop import WebLoop +from testlib.entrypoint import run_pytest import asgi from workers import WorkerEntrypoint -async def _noop(*args): - pass - - -# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them -WebLoop.shutdown_asyncgens = _noop -WebLoop.shutdown_default_executor = _noop - -# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks, -# which raises InvalidStateError under Pyodide's WebLoop. -if sys.version_info < (3, 13): - asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined] - - async def _drain_lifespan(receive, send): message = await receive() if message["type"] == "lifespan.startup": @@ -180,4 +163,4 @@ async def fetch(self, request): async def test(self): os.chdir("/session/metadata/tests") args = [".", "-vv"] - assert pytest.main(args) == 0 + run_pytest(args) diff --git a/packages/runtime-sdk/tests/workerd-test/asgi/pyproject.toml b/packages/runtime-sdk/tests/workerd-test/asgi/pyproject.toml index f7d33188..325e057d 100644 --- a/packages/runtime-sdk/tests/workerd-test/asgi/pyproject.toml +++ b/packages/runtime-sdk/tests/workerd-test/asgi/pyproject.toml @@ -2,4 +2,7 @@ name = "test" version = "0.0.0" requires-python = ">=3.12" -dependencies = ["pytest", "pytest-asyncio<1.2.0"] +dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib"] + +[tool.uv.sources] +testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } diff --git a/packages/runtime-sdk/tests/workerd-test/asgi/worker.py b/packages/runtime-sdk/tests/workerd-test/asgi/worker.py index 87c7bc21..68e94ca9 100644 --- a/packages/runtime-sdk/tests/workerd-test/asgi/worker.py +++ b/packages/runtime-sdk/tests/workerd-test/asgi/worker.py @@ -1,28 +1,10 @@ -import asyncio import os -import sys -import pytest -from pyodide.webloop import WebLoop +from testlib.entrypoint import run_pytest import asgi from workers import WorkerEntrypoint - -async def noop(*args): - pass - - -# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them -WebLoop.shutdown_asyncgens = noop -WebLoop.shutdown_default_executor = noop - -# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks, -# which raises InvalidStateError under Pyodide's WebLoop. -if sys.version_info < (3, 13): - asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined] - - # --------------------------------------------------------------------------- # ASGI apps # --------------------------------------------------------------------------- @@ -291,4 +273,4 @@ async def fetch(self, request): async def test(self): os.chdir("/session/metadata/tests") args = [".", "-vv"] - assert pytest.main(args) == 0 + run_pytest(args) diff --git a/packages/runtime-sdk/tests/workerd-test/sdk/pyproject.toml b/packages/runtime-sdk/tests/workerd-test/sdk/pyproject.toml index f7d33188..325e057d 100644 --- a/packages/runtime-sdk/tests/workerd-test/sdk/pyproject.toml +++ b/packages/runtime-sdk/tests/workerd-test/sdk/pyproject.toml @@ -2,4 +2,7 @@ name = "test" version = "0.0.0" requires-python = ">=3.12" -dependencies = ["pytest", "pytest-asyncio<1.2.0"] +dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib"] + +[tool.uv.sources] +testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } diff --git a/packages/runtime-sdk/tests/workerd-test/sdk/worker.py b/packages/runtime-sdk/tests/workerd-test/sdk/worker.py index 6417bcf1..b09d8afd 100644 --- a/packages/runtime-sdk/tests/workerd-test/sdk/worker.py +++ b/packages/runtime-sdk/tests/workerd-test/sdk/worker.py @@ -5,34 +5,15 @@ # behaviour doesn't need to strictly be held consistent. In fact it uses the JS fetch, so it's not # going to follow the SDK at all. -import asyncio import os -import sys from functools import wraps -import pytest -from pyodide.webloop import WebLoop +from testlib.entrypoint import run_pytest from workers import ( WorkerEntrypoint, ) - -async def noop(*args): - pass - - -# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them -WebLoop.shutdown_asyncgens = noop -WebLoop.shutdown_default_executor = noop - -# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks, -# which raises InvalidStateError under Pyodide's WebLoop. -# Ignore this error to prevent pytest-asyncio from crashing. -if sys.version_info < (3, 13): - asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined] - - RESPONSE_HANDLER = None @@ -68,4 +49,4 @@ async def test(self): args = [".", "-vv"] if self.env.color: args.append("--color=yes") - assert pytest.main(args) == 0 + run_pytest(args) diff --git a/packages/runtime-sdk/tests/workerd-test/wsgi/pyproject.toml b/packages/runtime-sdk/tests/workerd-test/wsgi/pyproject.toml index f7d33188..325e057d 100644 --- a/packages/runtime-sdk/tests/workerd-test/wsgi/pyproject.toml +++ b/packages/runtime-sdk/tests/workerd-test/wsgi/pyproject.toml @@ -2,4 +2,7 @@ name = "test" version = "0.0.0" requires-python = ">=3.12" -dependencies = ["pytest", "pytest-asyncio<1.2.0"] +dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib"] + +[tool.uv.sources] +testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } diff --git a/packages/runtime-sdk/tests/workerd-test/wsgi/worker.py b/packages/runtime-sdk/tests/workerd-test/wsgi/worker.py index 7f44cada..0b227f74 100644 --- a/packages/runtime-sdk/tests/workerd-test/wsgi/worker.py +++ b/packages/runtime-sdk/tests/workerd-test/wsgi/worker.py @@ -1,29 +1,11 @@ import asyncio import os -import sys -import pytest from pyodide.ffi import run_sync -from pyodide.webloop import WebLoop +from testlib.entrypoint import run_pytest from workers import WorkerEntrypoint, wsgi - -async def noop(*args): - pass - - -# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them -WebLoop.shutdown_asyncgens = noop -WebLoop.shutdown_default_executor = noop - -# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks, -# which raises InvalidStateError under Pyodide's WebLoop. -# Ignore this error to prevent pytest-asyncio from crashing. -if sys.version_info < (3, 13): - asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined] - - # --------------------------------------------------------------------------- # WSGI apps # --------------------------------------------------------------------------- @@ -140,4 +122,4 @@ async def test(self, ctrl): args = [".", "-vv"] if self.env.color: args.append("--color=yes") - assert pytest.main(args) == 0 + run_pytest(args) diff --git a/packages/testlib/testlib/entrypoint.py b/packages/testlib/testlib/entrypoint.py index 4df2bb55..be61060a 100644 --- a/packages/testlib/testlib/entrypoint.py +++ b/packages/testlib/testlib/entrypoint.py @@ -131,6 +131,17 @@ def patch_asyncio(): asyncio.runners._cancel_all_tasks = orig_cancel_all_tasks # type: ignore[attr-defined] +def run_pytest(pytest_args): + """Run pytest inside a worker with the asyncio patches applied. + + Intended for workerd `test()` handlers that invoke pytest directly rather + than via `TestRunner`. Fails the calling test if pytest reports failures. + """ + with restore_loop(), patch_asyncio(): + exit_code = pytest.main(pytest_args) + assert exit_code == 0, f"pytest exit code {exit_code}" + + @dataclass class TestRunnerResult: payload: Any