From d623444633ad364660530d68d128db84bc202f37 Mon Sep 17 00:00:00 2001 From: JP-Ellis Date: Wed, 14 Oct 2026 21:37:23 +1100 Subject: [PATCH 1/2] chore: require python 3.11 or newer Python 3.10 reached end of life on 2026-10-04, and the docs toolchain has already moved on: mkdocstrings 1.0.7, griffe-pydantic 1.3.2, griffe-warnings-deprecated 1.1.2 and mkdocs-llmstxt 0.5.1 all require 3.11. mkdocstrings 1.0.7 carries the fix for pymdown-extensions 12.2, whose new `Highlight` signature breaks the docs build on every branch. Raise `requires-python` to 3.11 for pact-python, pact-python-ffi, pact-python-cli and the examples. CI tests the oldest and newest supported versions, now 3.11 and 3.15; the hatch matrices and classifiers gain 3.15. The abi3 FFI wheel and the CLI wheels build against cp311. With ruff targeting 3.11: - `Self` comes from `typing`, so the `typing-extensions` dependency is gone from the examples, the FFI `types` group and the root `types` group. The runtime dependency stays for `deprecated` on Python < 3.13. - `datetime.UTC` replaces `timezone.utc`. - The examples drop their workaround for ISO 8601 offsets without a colon, which `datetime.fromisoformat` accepts from 3.11. Assisted-by: Claude Code:claude-opus-5-5 --- .github/workflows/docs.yml | 2 +- .github/workflows/release-cli.yml | 2 +- .github/workflows/release-ffi.yml | 2 +- .github/workflows/test.yml | 10 +++++----- examples/catalog/pyproject.toml | 2 +- examples/http/aiohttp_and_flask/README.md | 2 +- examples/http/aiohttp_and_flask/consumer.py | 14 +------------- examples/http/aiohttp_and_flask/provider.py | 4 ++-- examples/http/aiohttp_and_flask/pyproject.toml | 4 ++-- examples/http/aiohttp_and_flask/test_consumer.py | 4 ++-- examples/http/aiohttp_and_flask/test_provider.py | 10 +++++----- examples/http/requests_and_fastapi/README.md | 2 +- examples/http/requests_and_fastapi/consumer.py | 10 +--------- examples/http/requests_and_fastapi/provider.py | 6 +++--- examples/http/requests_and_fastapi/pyproject.toml | 4 ++-- .../http/requests_and_fastapi/test_consumer.py | 4 ++-- .../http/requests_and_fastapi/test_provider.py | 10 +++++----- .../http/service_consumer_provider/pyproject.toml | 4 ++-- examples/http/xml_example/README.md | 2 +- examples/http/xml_example/consumer.py | 3 +-- examples/http/xml_example/pyproject.toml | 4 ++-- examples/v2/src/consumer.py | 7 ------- pact-python-cli/pyproject.toml | 6 +++--- pact-python-ffi/pyproject.toml | 8 ++++---- pact-python-ffi/src/pact_ffi/__init__.py | 2 +- pyproject.toml | 13 ++++++------- scripts/release.py | 3 +-- src/pact/_server.py | 3 +-- src/pact/interaction/_base.py | 3 +-- src/pact/interaction/_http_interaction.py | 3 +-- src/pact/interaction/_sync_message_interaction.py | 2 +- src/pact/pact.py | 3 +-- src/pact/verifier.py | 3 +-- .../util/interaction_definition.py | 3 +-- tests/compatibility_suite/util/provider.py | 3 +-- 35 files changed, 65 insertions(+), 102 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9a6e6dedf..12c4c31c9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,7 +15,7 @@ on: - ready_for_review env: - STABLE_PYTHON_VERSION: '3.10' + STABLE_PYTHON_VERSION: '3.11' FORCE_COLOR: '1' HATCH_VERBOSE: '1' diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 1a631bd61..31aed7024 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -53,7 +53,7 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }} env: - STABLE_PYTHON_VERSION: '310' + STABLE_PYTHON_VERSION: '311' HATCH_VERBOSE: '1' FORCE_COLOR: '1' CIBW_BUILD_FRONTEND: build diff --git a/.github/workflows/release-ffi.yml b/.github/workflows/release-ffi.yml index 08c20ca5f..d1ab46d05 100644 --- a/.github/workflows/release-ffi.yml +++ b/.github/workflows/release-ffi.yml @@ -54,7 +54,7 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }} env: - STABLE_PYTHON_VERSION: '310' + STABLE_PYTHON_VERSION: '311' HATCH_VERBOSE: '1' FORCE_COLOR: '1' CIBW_BUILD_FRONTEND: build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91006b4c1..b5dba06a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ permissions: contents: read env: - STABLE_PYTHON_VERSION: '3.10' + STABLE_PYTHON_VERSION: '3.11' PYTEST_ADDOPTS: --color=yes HATCH_VERBOSE: '1' FORCE_COLOR: '1' @@ -67,8 +67,8 @@ jobs: - ubuntu-latest - windows-latest python-version: - - '3.10' - - '3.14' + - '3.11' + - '3.15' steps: - name: Checkout code @@ -143,8 +143,8 @@ jobs: - ubuntu-latest - windows-latest python-version: - - '3.10' - - '3.14' + - '3.11' + - '3.15' steps: - name: Checkout code diff --git a/examples/catalog/pyproject.toml b/examples/catalog/pyproject.toml index 7de8d8171..e0ebf8366 100644 --- a/examples/catalog/pyproject.toml +++ b/examples/catalog/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ "python-multipart~=0.0", ] description = "Pact Python catalog: Well-documented patterns and use cases" -requires-python = ">=3.10" +requires-python = ">=3.11" [dependency-groups] test = ["pact-python", "pytest~=9.0", "uvicorn~=0.30"] diff --git a/examples/http/aiohttp_and_flask/README.md b/examples/http/aiohttp_and_flask/README.md index 1a6671a16..59a02a0a3 100644 --- a/examples/http/aiohttp_and_flask/README.md +++ b/examples/http/aiohttp_and_flask/README.md @@ -36,7 +36,7 @@ Use the above links to view additional documentation within. ## Prerequisites -- Python 3.10 or higher +- Python 3.11 or higher - A dependency manager ([uv](https://docs.astral.sh/uv/) recommended, [pip](https://pip.pypa.io/en/stable/) also works) ## Running the Example diff --git a/examples/http/aiohttp_and_flask/consumer.py b/examples/http/aiohttp_and_flask/consumer.py index 5c50aaefe..c98c9945d 100644 --- a/examples/http/aiohttp_and_flask/consumer.py +++ b/examples/http/aiohttp_and_flask/consumer.py @@ -27,7 +27,6 @@ from __future__ import annotations import logging -import sys from dataclasses import dataclass from datetime import datetime from typing import TYPE_CHECKING, Any @@ -36,8 +35,7 @@ if TYPE_CHECKING: from types import TracebackType - - from typing_extensions import Self + from typing import Self logger = logging.getLogger(__name__) @@ -200,11 +198,6 @@ async def get_user(self, user_id: int) -> User: response.raise_for_status() data: dict[str, Any] = await response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = ( - data["created_on"][:-2] + ":" + data["created_on"][-2:] - ) return User( id=data["id"], name=data["name"], @@ -238,11 +231,6 @@ async def create_user( ): response.raise_for_status() data = await response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = ( - data["created_on"][:-2] + ":" + data["created_on"][-2:] - ) logger.debug("Created user %s", data["id"]) return User( id=data["id"], diff --git a/examples/http/aiohttp_and_flask/provider.py b/examples/http/aiohttp_and_flask/provider.py index 694f70de4..40b3aad58 100644 --- a/examples/http/aiohttp_and_flask/provider.py +++ b/examples/http/aiohttp_and_flask/provider.py @@ -29,7 +29,7 @@ import logging from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import TYPE_CHECKING, Any, ClassVar, Literal from flask import Flask, Response, abort, jsonify, request @@ -273,7 +273,7 @@ def create_user() -> tuple[Response, int]: new_user = User( id=UserDb.new_user_id(), name=user["name"], - created_on=datetime.now(tz=timezone.utc), + created_on=datetime.now(tz=UTC), email=user.get("email"), ip_address=user.get("ip_address"), hobbies=user.get("hobbies", []), diff --git a/examples/http/aiohttp_and_flask/pyproject.toml b/examples/http/aiohttp_and_flask/pyproject.toml index dba9a40ea..3fe5a4f26 100644 --- a/examples/http/aiohttp_and_flask/pyproject.toml +++ b/examples/http/aiohttp_and_flask/pyproject.toml @@ -6,8 +6,8 @@ name = "example-aiohttp-and-flask" description = "Example of using an aiohttp client and Flask server with Pact Python" version = "1.0.0" -requires-python = ">=3.10" -dependencies = ["aiohttp~=3.0", "flask~=3.0", "typing-extensions~=4.0"] +requires-python = ">=3.11" +dependencies = ["aiohttp~=3.0", "flask~=3.0"] [dependency-groups] test = ["pact-python", "pytest~=9.0", "pytest-asyncio~=1.0"] diff --git a/examples/http/aiohttp_and_flask/test_consumer.py b/examples/http/aiohttp_and_flask/test_consumer.py index e77e3f0fa..3a5e4fc64 100644 --- a/examples/http/aiohttp_and_flask/test_consumer.py +++ b/examples/http/aiohttp_and_flask/test_consumer.py @@ -16,7 +16,7 @@ from __future__ import annotations import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import TYPE_CHECKING, Any import aiohttp @@ -124,7 +124,7 @@ async def test_create_user(pact: Pact) -> None: response: dict[str, Any] = { "id": match.int(1000), "name": "Bob", - "created_on": match.datetime(datetime.now(tz=timezone.utc)), + "created_on": match.datetime(datetime.now(tz=UTC)), } ( diff --git a/examples/http/aiohttp_and_flask/test_provider.py b/examples/http/aiohttp_and_flask/test_provider.py index 8c99e6f39..0eaf523ec 100644 --- a/examples/http/aiohttp_and_flask/test_provider.py +++ b/examples/http/aiohttp_and_flask/test_provider.py @@ -19,7 +19,7 @@ import contextlib import dataclasses import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from threading import Thread from typing import TYPE_CHECKING, Any, Literal @@ -112,7 +112,7 @@ def default_mock_db() -> dict[int, User]: id=1, name="Alice", email="alice@example.com", - created_on=datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc), + created_on=datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC), ip_address="1.2.3.4", hobbies=["pact testing", "programming", "qa"], admin=False, @@ -121,7 +121,7 @@ def default_mock_db() -> dict[int, User]: id=2, name="Bob", email=None, # Edge case: email is None - created_on=datetime(1999, 12, 31, 23, 59, 59, tzinfo=timezone.utc), + created_on=datetime(1999, 12, 31, 23, 59, 59, tzinfo=UTC), ip_address="", hobbies=[], admin=True, @@ -130,7 +130,7 @@ def default_mock_db() -> dict[int, User]: id=10, name="Charlie", email="charlie@example.com", - created_on=datetime(2025, 8, 8, 8, 8, 8, tzinfo=timezone.utc), + created_on=datetime(2025, 8, 8, 8, 8, 8, tzinfo=UTC), ip_address="3.4.5.6", hobbies=[""], admin=False, @@ -139,7 +139,7 @@ def default_mock_db() -> dict[int, User]: id=42, name="Dana", email="dana+test@example.com", - created_on=datetime(2022, 2, 22, 2, 22, 22, tzinfo=timezone.utc), + created_on=datetime(2022, 2, 22, 2, 22, 22, tzinfo=UTC), ip_address="255.255.255.255", hobbies=["edge", "case", "testing"], admin=False, diff --git a/examples/http/requests_and_fastapi/README.md b/examples/http/requests_and_fastapi/README.md index 1b8a4e08a..21f94cc8a 100644 --- a/examples/http/requests_and_fastapi/README.md +++ b/examples/http/requests_and_fastapi/README.md @@ -45,7 +45,7 @@ This example is intended for software engineers and engineering managers who wan ## Prerequisites -- Python 3.10 or higher +- Python 3.11 or higher - A dependency manager ([uv](https://docs.astral.sh/uv/) recommended, [pip](https://pip.pypa.io/en/stable/) also works) ## Running the Example diff --git a/examples/http/requests_and_fastapi/consumer.py b/examples/http/requests_and_fastapi/consumer.py index a25392fab..2f7d3a313 100644 --- a/examples/http/requests_and_fastapi/consumer.py +++ b/examples/http/requests_and_fastapi/consumer.py @@ -27,7 +27,6 @@ from __future__ import annotations import logging -import sys from dataclasses import dataclass from datetime import datetime from typing import TYPE_CHECKING, Any @@ -36,8 +35,7 @@ if TYPE_CHECKING: from types import TracebackType - - from typing_extensions import Self + from typing import Self logger = logging.getLogger(__name__) @@ -197,9 +195,6 @@ def get_user(self, user_id: int) -> User: response.raise_for_status() data: dict[str, Any] = response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = data["created_on"][:-2] + ":" + data["created_on"][-2:] return User( id=data["id"], name=data["name"], @@ -233,9 +228,6 @@ def create_user( response.raise_for_status() data = response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = data["created_on"][:-2] + ":" + data["created_on"][-2:] logger.debug("Created user %s", data["id"]) return User( id=data["id"], diff --git a/examples/http/requests_and_fastapi/provider.py b/examples/http/requests_and_fastapi/provider.py index 1275a3806..3a63d813f 100644 --- a/examples/http/requests_and_fastapi/provider.py +++ b/examples/http/requests_and_fastapi/provider.py @@ -29,7 +29,7 @@ from __future__ import annotations import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any, ClassVar from fastapi import FastAPI, HTTPException, status @@ -52,7 +52,7 @@ class User(BaseModel): id: int name: str - created_on: datetime = Field(default_factory=lambda: datetime.now(tz=timezone.utc)) + created_on: datetime = Field(default_factory=lambda: datetime.now(tz=UTC)) email: str | None = None ip_address: str | None = None hobbies: list[str] = Field(default_factory=list) @@ -195,7 +195,7 @@ async def create_user(data: dict[str, Any]) -> User: user = User( id=UserDb.new_user_id(), name=data["name"], - created_on=datetime.now(tz=timezone.utc), + created_on=datetime.now(tz=UTC), email=data.get("email"), ip_address=data.get("ip_address"), hobbies=data.get("hobbies", []), diff --git a/examples/http/requests_and_fastapi/pyproject.toml b/examples/http/requests_and_fastapi/pyproject.toml index 03b32dfdf..721e4cd95 100644 --- a/examples/http/requests_and_fastapi/pyproject.toml +++ b/examples/http/requests_and_fastapi/pyproject.toml @@ -6,8 +6,8 @@ name = "example-requests-and-fastapi" description = "Example of using a requests client and FastAPI server with Pact Python" version = "1.0.0" -requires-python = ">=3.10" -dependencies = ["fastapi~=0.0", "requests~=2.0", "typing-extensions~=4.0"] +requires-python = ">=3.11" +dependencies = ["fastapi~=0.0", "requests~=2.0"] [dependency-groups] test = ["pact-python", "pytest~=9.0", "uvicorn~=0.29"] diff --git a/examples/http/requests_and_fastapi/test_consumer.py b/examples/http/requests_and_fastapi/test_consumer.py index 6f69a8c64..10a280d23 100644 --- a/examples/http/requests_and_fastapi/test_consumer.py +++ b/examples/http/requests_and_fastapi/test_consumer.py @@ -16,7 +16,7 @@ from __future__ import annotations import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import TYPE_CHECKING, Any import pytest @@ -125,7 +125,7 @@ def test_create_user(pact: Pact) -> None: response: dict[str, Any] = { "id": match.int(1000), "name": "Bob", - "created_on": match.datetime(datetime.now(tz=timezone.utc)), + "created_on": match.datetime(datetime.now(tz=UTC)), } ( diff --git a/examples/http/requests_and_fastapi/test_provider.py b/examples/http/requests_and_fastapi/test_provider.py index ffd5a445b..c18cea405 100644 --- a/examples/http/requests_and_fastapi/test_provider.py +++ b/examples/http/requests_and_fastapi/test_provider.py @@ -19,7 +19,7 @@ import contextlib import dataclasses import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from threading import Thread from typing import TYPE_CHECKING, Any, Literal @@ -114,7 +114,7 @@ def default_mock_db() -> dict[int, User]: id=1, name="Alice", email="alice@example.com", - created_on=datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc), + created_on=datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC), ip_address="1.2.3.4", hobbies=["pact testing", "programming", "qa"], admin=False, @@ -123,7 +123,7 @@ def default_mock_db() -> dict[int, User]: id=2, name="Bob", email=None, # Edge case: email is None - created_on=datetime(1999, 12, 31, 23, 59, 59, tzinfo=timezone.utc), + created_on=datetime(1999, 12, 31, 23, 59, 59, tzinfo=UTC), ip_address="", hobbies=[], admin=True, @@ -132,7 +132,7 @@ def default_mock_db() -> dict[int, User]: id=10, name="Charlie", email="charlie@example.com", - created_on=datetime(2025, 8, 8, 8, 8, 8, tzinfo=timezone.utc), + created_on=datetime(2025, 8, 8, 8, 8, 8, tzinfo=UTC), ip_address="3.4.5.6", hobbies=[""], admin=False, @@ -141,7 +141,7 @@ def default_mock_db() -> dict[int, User]: id=42, name="Dana", email="dana+test@example.com", - created_on=datetime(2022, 2, 22, 2, 22, 22, tzinfo=timezone.utc), + created_on=datetime(2022, 2, 22, 2, 22, 22, tzinfo=UTC), ip_address="255.255.255.255", hobbies=["edge", "case", "testing"], admin=False, diff --git a/examples/http/service_consumer_provider/pyproject.toml b/examples/http/service_consumer_provider/pyproject.toml index 48fcbe198..388782a01 100644 --- a/examples/http/service_consumer_provider/pyproject.toml +++ b/examples/http/service_consumer_provider/pyproject.toml @@ -6,8 +6,8 @@ name = "example-service-consumer-provider" description = "Example of a service acting as both a Pact consumer and provider" version = "1.0.0" -requires-python = ">=3.10" -dependencies = ["fastapi~=0.0", "requests~=2.0", "typing-extensions~=4.0"] +requires-python = ">=3.11" +dependencies = ["fastapi~=0.0", "requests~=2.0"] [dependency-groups] test = ["pact-python", "pytest~=9.0", "uvicorn~=0.29"] diff --git a/examples/http/xml_example/README.md b/examples/http/xml_example/README.md index c117ed1c1..c688c7c05 100644 --- a/examples/http/xml_example/README.md +++ b/examples/http/xml_example/README.md @@ -94,7 +94,7 @@ For JSON-based examples using the same matchers, see ## Prerequisites -- Python 3.10 or higher +- Python 3.11 or higher - A dependency manager ([uv](https://docs.astral.sh/uv/) recommended, [pip](https://pip.pypa.io/en/stable/) also works) diff --git a/examples/http/xml_example/consumer.py b/examples/http/xml_example/consumer.py index d4230f34e..43fd01b4c 100644 --- a/examples/http/xml_example/consumer.py +++ b/examples/http/xml_example/consumer.py @@ -34,8 +34,7 @@ if TYPE_CHECKING: from types import TracebackType - - from typing_extensions import Self + from typing import Self logger = logging.getLogger(__name__) diff --git a/examples/http/xml_example/pyproject.toml b/examples/http/xml_example/pyproject.toml index 3e72798e8..de99278d7 100644 --- a/examples/http/xml_example/pyproject.toml +++ b/examples/http/xml_example/pyproject.toml @@ -6,8 +6,8 @@ name = "example-xml" description = "Example of XML contract testing with Pact Python" version = "1.0.0" -requires-python = ">=3.10" -dependencies = ["fastapi~=0.0", "requests~=2.0", "typing-extensions~=4.0"] +requires-python = ">=3.11" +dependencies = ["fastapi~=0.0", "requests~=2.0"] [dependency-groups] test = ["pact-python", "pytest~=9.0", "uvicorn~=0.29"] diff --git a/examples/v2/src/consumer.py b/examples/v2/src/consumer.py index 966a80955..aed2f68d1 100644 --- a/examples/v2/src/consumer.py +++ b/examples/v2/src/consumer.py @@ -26,7 +26,6 @@ from __future__ import annotations -import sys from dataclasses import dataclass from datetime import datetime from typing import Any @@ -105,9 +104,6 @@ def get_user(self, user_id: int) -> User: response = requests.get(uri, timeout=5) response.raise_for_status() data: dict[str, Any] = response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = data["created_on"][:-2] + ":" + data["created_on"][-2:] return User( id=data["id"], name=data["name"], @@ -135,9 +131,6 @@ def create_user( response = requests.post(uri, json={"name": name}, timeout=5) response.raise_for_status() data: dict[str, Any] = response.json() - # Python < 3.11 don't support ISO 8601 offsets without a colon - if sys.version_info < (3, 11) and data["created_on"][-4:].isdigit(): - data["created_on"] = data["created_on"][:-2] + ":" + data["created_on"][-2:] return User( id=data["id"], name=data["name"], diff --git a/pact-python-cli/pyproject.toml b/pact-python-cli/pyproject.toml index d570813ca..36a357fb1 100644 --- a/pact-python-cli/pyproject.toml +++ b/pact-python-cli/pyproject.toml @@ -21,15 +21,15 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Software Development :: Testing", ] -requires-python = ">=3.10" +requires-python = ">=3.11" [project.urls] "Bug Tracker" = "https://github.com/pact-foundation/pact-python/issues" @@ -120,7 +120,7 @@ build-backend = "hatchling.build" dependency-groups = ["test"] [[tool.hatch.envs.test.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.mypy] # Overwrite the exclusions from the root pyproject.toml. diff --git a/pact-python-ffi/pyproject.toml b/pact-python-ffi/pyproject.toml index 21fbe4b0a..41a63a935 100644 --- a/pact-python-ffi/pyproject.toml +++ b/pact-python-ffi/pyproject.toml @@ -21,15 +21,15 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Software Development :: Testing", ] -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = ["cffi~=2.0"] @@ -43,7 +43,7 @@ dependencies = ["cffi~=2.0"] [dependency-groups] dev = ["ruff==0.16.10", { include-group = "test" }, { include-group = "types" }] test = ["pytest~=9.0", "pytest-cov~=7.0"] -types = ["mypy==2.4.0", "typing-extensions~=4.0"] +types = ["mypy==2.4.0"] [build-system] requires = ["cffi", "hatchling", "packaging", "setuptools"] @@ -106,7 +106,7 @@ build-backend = "hatchling.build" dependency-groups = ["test"] [[tool.hatch.envs.test.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.mypy] # Overwrite the exclusions from the root pyproject.toml. diff --git a/pact-python-ffi/src/pact_ffi/__init__.py b/pact-python-ffi/src/pact_ffi/__init__.py index 41a949220..d002e4f35 100644 --- a/pact-python-ffi/src/pact_ffi/__init__.py +++ b/pact-python-ffi/src/pact_ffi/__init__.py @@ -105,9 +105,9 @@ from collections.abc import Collection from collections.abc import Generator as GeneratorType from pathlib import Path + from typing import Self import cffi - from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/pyproject.toml b/pyproject.toml index e9bab384b..6e83d9d88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,15 +24,15 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Software Development :: Testing", ] -requires-python = ">=3.10" +requires-python = ">=3.11" # Dependencies of Pact Python should be specified using the broadest range # compatible version unless: @@ -127,7 +127,6 @@ types = [ "types-grpcio~=1.0", "types-protobuf~=7.34", "types-requests~=2.0", - "typing-extensions~=4.0", # For Python 3.10 support ] # Dependencies for v2 example and test environments @@ -199,7 +198,7 @@ build-backend = "hatchling.build" dependency-groups = ["test"] [[tool.hatch.envs.test.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.hatch.envs.example] installer = "uv" @@ -207,7 +206,7 @@ build-backend = "hatchling.build" dependency-groups = ["example"] [[tool.hatch.envs.example.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.hatch.envs.v2-test] features = ["compat-v2"] @@ -220,7 +219,7 @@ build-backend = "hatchling.build" test = "pytest tests/v2 {args}" [[tool.hatch.envs.v2-test.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.hatch.envs.v2-example] features = ["compat-v2"] @@ -233,7 +232,7 @@ build-backend = "hatchling.build" example = "pytest examples/v2 {args}" [[tool.hatch.envs.v2-example.matrix]] - python = ["3.10", "3.11", "3.12", "3.13", "3.14"] + python = ["3.11", "3.12", "3.13", "3.14", "3.15"] [tool.mypy] exclude = """(?x)^( diff --git a/scripts/release.py b/scripts/release.py index 79be79200..c1c617f83 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -21,11 +21,10 @@ import re import subprocess import sys +import tomllib from dataclasses import dataclass from pathlib import Path -import tomllib - ROOT = Path(__file__).parent.parent logger = logging.getLogger(__name__) diff --git a/src/pact/_server.py b/src/pact/_server.py index f1f1d48de..8e8bde81f 100644 --- a/src/pact/_server.py +++ b/src/pact/_server.py @@ -37,8 +37,7 @@ if TYPE_CHECKING: from types import TracebackType - - from typing_extensions import Self + from typing import Self logger = logging.getLogger(__name__) diff --git a/src/pact/interaction/_base.py b/src/pact/interaction/_base.py index fc1dbc137..6dfc72793 100644 --- a/src/pact/interaction/_base.py +++ b/src/pact/interaction/_base.py @@ -20,8 +20,7 @@ if TYPE_CHECKING: from pathlib import Path - - from typing_extensions import Self + from typing import Self from pact.match import AbstractMatcher diff --git a/src/pact/interaction/_http_interaction.py b/src/pact/interaction/_http_interaction.py index b62273dce..f3c2e61b7 100644 --- a/src/pact/interaction/_http_interaction.py +++ b/src/pact/interaction/_http_interaction.py @@ -16,8 +16,7 @@ if TYPE_CHECKING: from collections.abc import Iterable - - from typing_extensions import Self + from typing import Self class HttpInteraction(Interaction): diff --git a/src/pact/interaction/_sync_message_interaction.py b/src/pact/interaction/_sync_message_interaction.py index a74c6da90..2b60aa408 100644 --- a/src/pact/interaction/_sync_message_interaction.py +++ b/src/pact/interaction/_sync_message_interaction.py @@ -10,7 +10,7 @@ from pact.interaction._base import Interaction if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self class SyncMessageInteraction(Interaction): diff --git a/src/pact/pact.py b/src/pact/pact.py index 80b2da01e..db0c08755 100644 --- a/src/pact/pact.py +++ b/src/pact/pact.py @@ -89,8 +89,7 @@ if TYPE_CHECKING: from collections.abc import Callable, Generator from types import TracebackType - - from typing_extensions import Self + from typing import Self from pact.interaction import Interaction diff --git a/src/pact/verifier.py b/src/pact/verifier.py index 3f3bbc672..0f1e12b66 100644 --- a/src/pact/verifier.py +++ b/src/pact/verifier.py @@ -105,8 +105,7 @@ if TYPE_CHECKING: from collections.abc import Iterable - - from typing_extensions import Self + from typing import Self logger = logging.getLogger(__name__) diff --git a/tests/compatibility_suite/util/interaction_definition.py b/tests/compatibility_suite/util/interaction_definition.py index 5b4e69b2c..65b932d0b 100644 --- a/tests/compatibility_suite/util/interaction_definition.py +++ b/tests/compatibility_suite/util/interaction_definition.py @@ -30,8 +30,7 @@ if typing.TYPE_CHECKING: from http.server import SimpleHTTPRequestHandler from pathlib import Path - - from typing_extensions import Self + from typing import Self from pact.interaction import Interaction from pact.pact import Pact diff --git a/tests/compatibility_suite/util/provider.py b/tests/compatibility_suite/util/provider.py index 9c42c4940..e065144b4 100644 --- a/tests/compatibility_suite/util/provider.py +++ b/tests/compatibility_suite/util/provider.py @@ -51,8 +51,7 @@ from collections.abc import Generator from pathlib import Path from types import TracebackType - - from typing_extensions import Self + from typing import Self from pact.types import Message from pact.verifier import Verifier From 77cd3464f2e9652c1a37ff87b1e3fbec8f4c3db7 Mon Sep 17 00:00:00 2001 From: JP-Ellis Date: Wed, 14 Oct 2026 21:37:44 +1100 Subject: [PATCH 2/2] chore(deps): update docs dependencies pymdown-extensions 12.2 made `md` a required argument of `Highlight.__init__`, which mkdocstrings 1.0.6 does not pass, so every docs build fails with a `TypeError`. mkdocstrings 1.0.7 passes it. The griffe-pydantic, griffe-warnings-deprecated and mkdocs-llmstxt patch releases come along because, like mkdocstrings 1.0.7, they needed the Python 3.11 floor before they would resolve. Assisted-by: Claude Code:claude-opus-5-5 --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6e83d9d88..7acf406d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,16 +85,16 @@ dev = [ docs = [ "griffe-generics==1.0.13", "griffe-inherited-method-crossrefs==0.0.1.4", - "griffe-pydantic==1.3.1", - "griffe-warnings-deprecated==1.1.1", + "griffe-pydantic==1.3.2", + "griffe-warnings-deprecated==1.1.2", "mkdocs==1.6.1", "mkdocs-gen-files==0.6.1", "mkdocs-github-admonitions-plugin==0.1.1", "mkdocs-literate-nav==0.6.3", - "mkdocs-llmstxt==0.5.0", + "mkdocs-llmstxt==0.5.1", "mkdocs-material[recommended,git,imaging]==9.7.7", "mkdocs-section-index==0.3.12", - "mkdocstrings[python]==1.0.6", + "mkdocstrings[python]==1.0.7", "pathspec==1.1.1", ] example = [