diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index cbbbc27..df58e07 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -13,6 +13,7 @@ runs: uses: actions/setup-python@v5 with: python-version: ${{ inputs.python }} + allow-prereleases: true cache: 'pip' cache-dependency-path: | requirements/*.txt' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7e0098..3c9cb59 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,22 +9,26 @@ on: jobs: lint: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: ["3.12", "3.13"] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup with: - python: "3.12" + python: ${{ matrix.python }} - name: Run linters # Temporarily disabled so I get CI up and running and I can fix the new # lint errors separately. run: | - nox -ts --python=3.12 --tags lint + nox -ts --python=${{ matrix.python }} --tags lint check: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.9", "pypy3.10"] + python: ["3.9", "3.10", "3.11", "3.12", "pypy3.9", "pypy3.10", "3.13"] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dda3c6..ed2f89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Move to [`hatchling`](https://hatch.pypa.io/latest/) as a build backend. - Drop Python 3.7 support. +- Drop Python 3.8 support. +- Declare support for Python 3.13. ## [0.6.1] - 2023-06-13 diff --git a/noxfile.py b/noxfile.py index 9e156e3..f7a2650 100644 --- a/noxfile.py +++ b/noxfile.py @@ -10,11 +10,11 @@ ALL_PYTHON_VERSIONS = ( - "3.8", "3.9", "3.10", "3.11", "3.12", + "3.13", "pypy3.9", "pypy3.10", ) @@ -64,7 +64,7 @@ def check_imports(session: nox.Session) -> None: session.run("isort", *(session.posargs or DEFAULT_LINT_TARGETS), "--check") -@nox.session(python=DEFAULT_PYTHON_VERSION, tags=["lint", "check"]) +@nox.session(python=ALL_PYTHON_VERSIONS, tags=["lint", "check"]) def lint(session: nox.Session) -> None: """ Run the ruff linter diff --git a/pyproject.toml b/pyproject.toml index 3e745a5..9469659 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,11 +19,11 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Typing :: Typed", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", @@ -31,7 +31,7 @@ classifiers = [ dynamic = ["version"] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "typing_extensions>=4", @@ -78,7 +78,7 @@ extra_standard_library = "typing_extensions" [tool.ruff] line-length = 80 -target-version = "py38" +target-version = "py39" [tool.ruff.format] preview = true diff --git a/src/statsd/client.py b/src/statsd/client.py index 26d8a7b..3bf269d 100644 --- a/src/statsd/client.py +++ b/src/statsd/client.py @@ -10,13 +10,17 @@ import socket import threading import time -from typing import Any, Callable, Iterator, Mapping, TypeVar +from typing import TYPE_CHECKING, Any, Callable, TypeVar from typing_extensions import ParamSpec from statsd.exceptions import InvalidMetricType, InvalidSampleRate from statsd.formats import DefaultSerializer, Serializer +if TYPE_CHECKING: + from collections.abc import Iterator, Mapping + + P = ParamSpec("P") T = TypeVar("T") diff --git a/src/statsd/formats.py b/src/statsd/formats.py index dca6e1e..f46a8a3 100644 --- a/src/statsd/formats.py +++ b/src/statsd/formats.py @@ -1,6 +1,6 @@ import abc import re -from typing import Mapping +from collections.abc import Mapping from statsd.exceptions import InvalidTags diff --git a/tests/test_formats.py b/tests/test_formats.py index 71a1caf..4c6bf22 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -1,4 +1,4 @@ -from typing import Mapping, Tuple +from collections.abc import Mapping import pytest @@ -10,7 +10,7 @@ ) -SerializeInput = Tuple[str, str, str, float, Mapping[str, str]] +SerializeInput = tuple[str, str, str, float, Mapping[str, str]] CASE_SIMPLE: SerializeInput = ("my_metric", "c", "1", 1, {}) diff --git a/tests/test_udp_client.py b/tests/test_udp_client.py index baa3536..e652131 100644 --- a/tests/test_udp_client.py +++ b/tests/test_udp_client.py @@ -2,7 +2,8 @@ import logging import socket import time -from typing import Any, Generator +from collections.abc import Generator +from typing import Any from unittest import mock import pytest @@ -85,8 +86,11 @@ def test_broken_pipe(receiver_socket: socket.socket, caplog: Any) -> None: host, port = receiver_socket.getsockname() client = StatsdClient(host=host, port=port, max_buffer_size=0) - with mock.patch("socket.socket.send", side_effect=[2]), caplog.at_level( - logging.WARNING, + with ( + mock.patch("socket.socket.send", side_effect=[2]), + caplog.at_level( + logging.WARNING, + ), ): # Should not raise. client.increment("foo", 1) @@ -102,10 +106,13 @@ def test_socket_errors_are_logged_not_raised( host, port = receiver_socket.getsockname() client = StatsdClient(host=host, port=port, max_buffer_size=0) - with mock.patch( - "socket.socket.send", - side_effect=[OSError("Broken socket")], - ), caplog.at_level(logging.WARNING): + with ( + mock.patch( + "socket.socket.send", + side_effect=[OSError("Broken socket")], + ), + caplog.at_level(logging.WARNING), + ): # Should not raise. client.increment("foo", 1) @@ -120,10 +127,13 @@ def test_unexpected_exceptions_are_logged_not_raised( host, port = receiver_socket.getsockname() client = StatsdClient(host=host, port=port, max_buffer_size=0) - with mock.patch( - "socket.socket.send", - side_effect=[ValueError("Random error")], - ), caplog.at_level(logging.ERROR): + with ( + mock.patch( + "socket.socket.send", + side_effect=[ValueError("Random error")], + ), + caplog.at_level(logging.ERROR), + ): # Should not raise. client.increment("foo", 1)