Skip to content

Commit 393190b

Browse files
committed
feat!: add Python 3.13, drop 3.8
With the release of Python 3.13 and Python 3.8 no longer being maintained, we update the supported Python versions of Pact Python to match what is currently maintained. BREAKING CHANGE: Python 3.8 support dropped Signed-off-by: JP-Ellis <[email protected]>
1 parent d582e4b commit 393190b

36 files changed

+160
-123
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ concurrency:
1515
cancel-in-progress: true
1616

1717
env:
18-
STABLE_PYTHON_VERSION: "3.12"
18+
STABLE_PYTHON_VERSION: "3.13"
1919
HATCH_VERBOSE: "1"
2020
FORCE_COLOR: "1"
2121
CIBW_BUILD_FRONTEND: build

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
- master
1212

1313
env:
14-
STABLE_PYTHON_VERSION: "3.12"
14+
STABLE_PYTHON_VERSION: "3.13"
1515
FORCE_COLOR: "1"
1616
HATCH_VERBOSE: "1"
1717

.github/workflows/test.yml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ concurrency:
1313
cancel-in-progress: true
1414

1515
env:
16-
STABLE_PYTHON_VERSION: "3.12"
16+
STABLE_PYTHON_VERSION: "3.13"
1717
PYTEST_ADDOPTS: --color=yes
1818
HATCH_VERBOSE: "1"
1919
FORCE_COLOR: "1"
@@ -67,12 +67,12 @@ jobs:
6767
fail-fast: false
6868
matrix:
6969
os: [ubuntu-latest]
70-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
70+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
7171
experimental: [false]
7272
include:
7373
- # Run tests against the next Python version, but no need for the full list of OSes.
7474
os: ubuntu-latest
75-
python-version: "3.13.0-alpha.0 - 3.13"
75+
python-version: "3.14"
7676
experimental: true
7777

7878
steps:
@@ -133,16 +133,12 @@ jobs:
133133
fail-fast: false
134134
matrix:
135135
os: [windows-latest, macos-latest]
136-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
137-
# Python 3.8 and 3.9 aren't supported on macos-latest (ARM)
136+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
137+
# Python 3.9 aren't supported on macos-latest (ARM)
138138
exclude:
139-
- os: macos-latest
140-
python-version: "3.8"
141139
- os: macos-latest
142140
python-version: "3.9"
143141
include:
144-
- os: macos-13
145-
python-version: "3.8"
146142
- os: macos-13
147143
python-version: "3.9"
148144

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,5 @@ repos:
8181
entry: hatch run mypy
8282
language: system
8383
types: [python]
84-
exclude: ^(src/pact|tests|examples/tests)/(?!v3/).*\.py$
84+
exclude: ^(src/pact|tests|examples|examples/tests)/(?!v3/).*\.py$
8585
stages: [pre-push]

docs/scripts/other.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,13 @@ def is_binary(buffer: bytes) -> bool:
8484
if is_binary(buf):
8585
if source_path.stat().st_size < 16 * 2**20:
8686
# Copy the file only if it's less than 16MB.
87-
with Path(source_path).open("rb") as fi, mkdocs_gen_files.open(
88-
dest_path,
89-
"wb",
90-
) as fd:
87+
with (
88+
Path(source_path).open("rb") as fi,
89+
mkdocs_gen_files.open(
90+
dest_path,
91+
"wb",
92+
) as fd,
93+
):
9194
fd.write(fi.read())
9295
else:
9396
# File is too big, create a redirect.
@@ -109,9 +112,12 @@ def is_binary(buffer: bytes) -> bool:
109112
)
110113

111114
else:
112-
with Path(source_path).open("r", encoding="utf-8") as fi, mkdocs_gen_files.open(
113-
dest_path,
114-
"w",
115-
encoding="utf-8",
116-
) as fd:
115+
with (
116+
Path(source_path).open("r", encoding="utf-8") as fi,
117+
mkdocs_gen_files.open(
118+
dest_path,
119+
"w",
120+
encoding="utf-8",
121+
) as fd,
122+
):
117123
fd.write(fi.read())

examples/conftest.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
from __future__ import annotations
1414

1515
from pathlib import Path
16-
from typing import Any, Generator, Union
16+
from typing import TYPE_CHECKING, Any
1717

1818
import pytest
1919
from testcontainers.compose import DockerCompose # type: ignore[import-untyped]
2020
from yarl import URL
2121

22+
if TYPE_CHECKING:
23+
from collections.abc import Generator
24+
2225
EXAMPLE_DIR = Path(__file__).parent.resolve()
2326

2427

@@ -34,7 +37,7 @@ def broker(request: pytest.FixtureRequest) -> Generator[URL, Any, None]:
3437
Otherwise, the Pact broker is started in a container. The URL of the
3538
containerised broker is then returned.
3639
"""
37-
broker_url: Union[str, None] = request.config.getoption("--broker-url")
40+
broker_url: str | None = request.config.getoption("--broker-url")
3841

3942
# If we have been given a broker URL, there's nothing more to do here and we
4043
# can return early.

examples/src/fastapi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
import logging
3131
from datetime import datetime, timezone
32-
from typing import Annotated, Any, Dict, Optional
32+
from typing import Annotated, Any, Optional
3333

3434
from pydantic import BaseModel, PlainSerializer
3535

@@ -90,7 +90,7 @@ def __repr__(self) -> str:
9090
be mocked out to avoid the need for a real database. An example of this can be
9191
found in the [test suite][examples.tests.test_01_provider_fastapi].
9292
"""
93-
FAKE_DB: Dict[int, User] = {}
93+
FAKE_DB: dict[int, User] = {}
9494

9595

9696
@app.get("/users/{uid}")

examples/src/flask.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import logging
2323
from dataclasses import dataclass
2424
from datetime import datetime, timezone
25-
from typing import Any, Dict, Tuple
25+
from typing import Any
2626

2727
from flask import Flask, Response, abort, jsonify, request
2828

@@ -89,11 +89,11 @@ def dict(self) -> dict[str, Any]:
8989
be mocked out to avoid the need for a real database. An example of this can be
9090
found in the [test suite][examples.tests.test_01_provider_flask].
9191
"""
92-
FAKE_DB: Dict[int, User] = {}
92+
FAKE_DB: dict[int, User] = {}
9393

9494

9595
@app.route("/users/<int:uid>")
96-
def get_user_by_id(uid: int) -> Response | Tuple[Response, int]:
96+
def get_user_by_id(uid: int) -> Response | tuple[Response, int]:
9797
"""
9898
Fetch a user by their ID.
9999
@@ -114,7 +114,7 @@ def create_user() -> Response:
114114
if request.json is None:
115115
abort(400, description="Invalid JSON data")
116116

117-
user: Dict[str, Any] = request.json
117+
user: dict[str, Any] = request.json
118118
uid = len(FAKE_DB)
119119
FAKE_DB[uid] = User(
120120
id=uid,
@@ -129,7 +129,7 @@ def create_user() -> Response:
129129

130130

131131
@app.route("/users/<int:uid>", methods=["DELETE"])
132-
def delete_user(uid: int) -> Tuple[str | Response, int]:
132+
def delete_user(uid: int) -> tuple[str | Response, int]:
133133
if uid not in FAKE_DB:
134134
return jsonify({"detail": "User not found"}), 404
135135
del FAKE_DB[uid]

examples/src/message.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from __future__ import annotations
1111

1212
from pathlib import Path
13-
from typing import Any, Dict, Union
13+
from typing import Any
1414

1515

1616
class Filesystem:
@@ -58,7 +58,7 @@ def __init__(self) -> None:
5858
"""
5959
self.fs = Filesystem()
6060

61-
def process(self, event: Dict[str, Any]) -> Union[str, None]:
61+
def process(self, event: dict[str, Any]) -> str | None:
6262
"""
6363
Process an event from the queue.
6464
@@ -84,7 +84,7 @@ def process(self, event: Dict[str, Any]) -> Union[str, None]:
8484
raise ValueError(msg)
8585

8686
@staticmethod
87-
def validate_event(event: Union[Dict[str, Any], Any]) -> None: # noqa: ANN401
87+
def validate_event(event: dict[str, Any] | Any) -> None: # noqa: ANN401
8888
"""
8989
Validates the event received from the queue.
9090

examples/tests/test_00_consumer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import logging
1919
from http import HTTPStatus
20-
from typing import TYPE_CHECKING, Any, Dict, Generator
20+
from typing import TYPE_CHECKING, Any
2121

2222
import pytest
2323
import requests
@@ -27,6 +27,7 @@
2727
from pact import Consumer, Format, Like, Provider
2828

2929
if TYPE_CHECKING:
30+
from collections.abc import Generator
3031
from pathlib import Path
3132

3233
from pact.pact import Pact
@@ -104,7 +105,7 @@ def test_get_existing_user(pact: Pact, user_consumer: UserConsumer) -> None:
104105
# what it needs from the provider (as opposed to the full schema). Should
105106
# the provider later decide to add or remove fields, Pact's consumer-driven
106107
# approach will ensure that interaction is still valid.
107-
expected: Dict[str, Any] = {
108+
expected: dict[str, Any] = {
108109
"id": Format().integer,
109110
"name": "Verna Hampton",
110111
"created_on": Format().iso_8601_datetime(),
@@ -154,7 +155,7 @@ def test_create_user(pact: Pact, user_consumer: UserConsumer) -> None:
154155
status code is 200 and the response body matches the expected user data.
155156
"""
156157
body = {"name": "Verna Hampton"}
157-
expected_response: Dict[str, Any] = {
158+
expected_response: dict[str, Any] = {
158159
"id": 124,
159160
"name": "Verna Hampton",
160161
"created_on": Format().iso_8601_datetime(),

0 commit comments

Comments
 (0)