Modernise typing syntax and enforce pyupgrade - #100
Merged
Conversation
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%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJb27fB7d7HbQqfXc7U66V
febus982
added a commit
that referenced
this pull request
Aug 4, 2026
* Require Python 3.11 or later 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 * Modernise typing syntax and enforce pyupgrade (#100) 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> --------- 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
With the floor at 3.11, PEP 604 unions (
X | Y) and PEP 585 builtin generics (list[T]) are available everywhere, and the abstract collection types belong incollections.abcrather thantyping.This also enables ruff's
UP(pyupgrade) ruleset permanently, so the style is enforced rather than cleaned up once. Without it the codebase drifts straight back, because new code follows the style of the code around it. The ruleset trackstarget-version, so it will flag the next batch automatically whenever the floor moves again.Net −43 lines. No behaviour change.
What ruff did
131 findings, applied with
ruff check --fix:UP007Union[X, Y]→X | YUP006List/Dict/Type/Tuple→list/dict/type/tupleUP035Mapping,Iterator,AsyncIteratormove fromtypingtocollections.abcMost of the size reduction is multi-line unions collapsing:
The three hand edits — worth a look during review
Everything else is mechanical; these are not.
PRIMARY_KEYin_repository/common.py. This is a module-level alias, not an annotation, so rewriting it produces atypes.UnionTypeat runtime rather than atyping.Union— a different object, observable by anything that introspects it. Ruff deliberately declines to auto-fix it. It is safe here because the alias lives in a private module and is not re-exported throughrepository.pyor the package root, so no downstream code can see the change. Flagging it explicitly because it is the one line in this PR with runtime semantics attached._unit_of_work/__init__.py. Matches the"__init__.py" = ["F401"]per-file ignore, so the imports the rewrite left unused were not stripped automatically and had to be removed by hand.Docstrings. Sphinx
:type:fields still named the old spellings (Union[CursorReference, None],List[MODEL]). mkdocstrings publishes these, so they would have contradicted the annotations directly above them in the rendered docs.Out of scope
class Repository[MODEL]:) is 3.12+, so out of reach at a 3.11 floor. TheTypeVar/Generic[MODEL]machinery incommon.pyis untouched, including the deliberately constrainedCURSOR_VALUEand its explanatory comment.@contextmanager+-> Iterator[T]as favouringGenerator[T]in_unit_of_work/__init__.py. I checked the annotations against the base commit: they are byte-identical, only the import source moved. Pre-existing, not introduced here, and mypy — the checker this project actually configures — does not flag it.Verification
Full tox matrix green: py311, py312, py313, py314, typing, lint, format — 7/7 OK, 210 tests, coverage still at 100% (
fail_under = 100).🤖 Generated with Claude Code
https://claude.ai/code/session_01BJb27fB7d7HbQqfXc7U66V