Require Python 3.11 or later - #99
Merged
Merged
Conversation
Python 3.9 reached end of life in October 2025 and 3.10 is security-only until October 2026. Dropping both raises the floor to a version that will be fully supported for the remainder of this package's current cycle. Update requires-python, trove classifiers, the mypy and ruff target versions, the tox env list, the CI matrix and the README badge. Regenerating the lock drops two compatibility shims that are part of the standard library from 3.11 onwards, exceptiongroup and backports-asyncio-runner, both pulled in transitively by pytest and pytest-asyncio. Supporting 3.9 had also forced dual resolutions for much of the dev toolchain; those collapse to single entries. No library code changes. The modules still use typing.Union and typing.Dict rather than PEP 604 and PEP 585 syntax; modernising them is left as separate work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJb27fB7d7HbQqfXc7U66V
Now that the floor is 3.11, PEP 604 unions and PEP 585 builtin generics are available everywhere, and the abstract collection types belong in collections.abc rather than typing. Enable ruff's UP (pyupgrade) ruleset so this is enforced rather than a one-off cleanup. Without it the codebase drifts back, because new code follows the style of the code around it. The ruleset also tracks target-version, so it will flag the next batch automatically whenever the floor moves again. Almost all of the diff is `ruff check --fix`: Union[X, Y] becomes X | Y, List/Dict/Type/Tuple become their builtin equivalents, and Mapping, Iterator and AsyncIterator move to collections.abc. Multi-line unions collapse to a single line, which is where most of the net reduction comes from. Three changes were made by hand: - PRIMARY_KEY is a module-level alias rather than an annotation, so rewriting it produces a types.UnionType at runtime instead of a typing.Union. Ruff will not apply that fix automatically. It is safe here because the alias lives in a private module and is not re-exported, so nothing downstream can observe the change. - _unit_of_work/__init__.py matches the "__init__.py" = ["F401"] per-file ignore, so the imports left unused by the rewrite had to be removed manually. - Sphinx :type: fields in docstrings still named the old spellings, which mkdocstrings publishes. They now match the annotations they document. No behaviour change. Full tox matrix green: py311 through py314, typing, lint and format, with coverage still at 100%. Claude-Session: https://claude.ai/code/session_01BJb27fB7d7HbQqfXc7U66V Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Raises the minimum supported Python from 3.9 to 3.11.
Python 3.9 reached end of life in October 2025, and 3.10 is security-only until October 2026. Dropping both puts the floor on a version that stays fully supported for the remainder of this package's current cycle.
Changes
pyproject.tomlrequires-python = ">=3.11,<3.15", trove classifiers,mypy python_version,ruff target-version = "py311"tox.inipy39andpy310fromenv_list.github/workflows/python-tests.yml["3.11", "3.12", "3.13", "3.14"]— 6 jobs down to 4README.mduv.lockOn the lock file diff
It is large (~1400 deletions) for two reasons, both mechanical:
uvto keep dual resolutions for much of the dev toolchain — a modern version plus an older 3.9-compatible one (mypy1.19.1/1.20.0,tox4.30.3/4.52.0, and ~20 others). Those collapse to single entries.exceptiongroup(ExceptionGroup/except*) andbackports-asyncio-runner(asyncio.Runner). Both were transitive dev dependencies of pytest and pytest-asyncio; no source file imported them.The lock was regenerated on top of #97, and
pymdown-extensionsis still pinned at 11.0.1 — that security fix is preserved.Not included
No library code changes. The modules still use
typing.Union/typing.Dictrather than PEP 604 and PEP 585 syntax. Ruff'sUPruleset is not enabled, so nothing flags it; modernising is left as separate work.Verification
Full tox matrix green on this branch — py311, py312, py313, py314, typing, lint, format: 7/7 OK, 210 tests passing.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BJb27fB7d7HbQqfXc7U66V