diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f06b150b..0a81b6b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,6 +91,29 @@ jobs: - name: Run fuzz session run: uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s fuzz + # Every interpreter `requires-python` admits and the trove classifiers + # advertise. `verify` stays single-version because it owns the coverage + # artifact Sonar consumes and the Isabelle replay, neither of which varies by + # interpreter; this job carries the compatibility signal on the hermetic suite + # alone, so a release cannot advertise a version nothing exercises. + interpreters: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v8 + - name: Run unit suite + env: + UV_PYTHON: ${{ matrix.python-version }} + run: uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s tests + # Opt-in, non-blocking, runtime-gated container integration tests (RUN-314). # Kept out of the hermetic `verify` graph; the `docker` marker tests self-skip # when no runtime is present, and this whole job never fails the build. diff --git a/implementations/python/pyproject.toml b/implementations/python/pyproject.toml index 4616679d..dd059339 100644 --- a/implementations/python/pyproject.toml +++ b/implementations/python/pyproject.toml @@ -14,6 +14,8 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ "typer>=0.12.0", diff --git a/implementations/python/tests/test_mcp_server.py b/implementations/python/tests/test_mcp_server.py index 57c73669..89f409ba 100644 --- a/implementations/python/tests/test_mcp_server.py +++ b/implementations/python/tests/test_mcp_server.py @@ -31,7 +31,7 @@ def _text(result) -> str: def _call(server, tool: str, args: dict | None = None) -> str: """Synchronously call a tool and return its text.""" - return asyncio.get_event_loop().run_until_complete(_async_call(server, tool, args or {})) + return asyncio.run(_async_call(server, tool, args or {})) async def _async_call(server, tool: str, args: dict) -> str: @@ -897,7 +897,7 @@ def test_server_has_all_tools(self): # Using the real registration surface (rather than a hand-copied # literal) means a drift between what the server exposes and what # raes_tool_surface advertises cannot pass silently. - registered = asyncio.get_event_loop().run_until_complete(server.list_tools()) + registered = asyncio.run(server.list_tools()) registered_names = {tool.name for tool in registered} assert registered_names, "server registered no tools"