From 5deffeb55c65f11536498674723fdd6eb6146b06 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sat, 3 Oct 2026 23:25:01 +0300 Subject: [PATCH] feat!: drop Python 3.10 and use typing.assert_never Closes #33 --- .github/workflows/_checks.yml | 6 ++---- docs/adr/0002-keep-py310-vendored-assert-never.md | 12 ------------ docs/agents/domain.md | 2 +- eof_fixer/fixer.py | 9 ++------- pyproject.toml | 3 +-- 5 files changed, 6 insertions(+), 26 deletions(-) delete mode 100644 docs/adr/0002-keep-py310-vendored-assert-never.md diff --git a/.github/workflows/_checks.yml b/.github/workflows/_checks.yml index b6bd0d5..1b37473 100644 --- a/.github/workflows/_checks.yml +++ b/.github/workflows/_checks.yml @@ -12,8 +12,8 @@ jobs: with: enable-cache: true cache-dependency-glob: "**/pyproject.toml" - - run: uv python install 3.10 - - run: uv python pin 3.10 + - run: uv python install 3.11 + - run: uv python pin 3.11 - run: just install lint-ci - run: just adr-check @@ -23,7 +23,6 @@ jobs: fail-fast: false matrix: python-version: - - "3.10" - "3.11" - "3.12" - "3.13" @@ -54,7 +53,6 @@ jobs: fail-fast: false matrix: python-version: - - "3.10" - "3.11" - "3.12" - "3.13" diff --git a/docs/adr/0002-keep-py310-vendored-assert-never.md b/docs/adr/0002-keep-py310-vendored-assert-never.md deleted file mode 100644 index 773babb..0000000 --- a/docs/adr/0002-keep-py310-vendored-assert-never.md +++ /dev/null @@ -1,12 +0,0 @@ -# Keep Python 3.10, with a vendored `_assert_never` - -`requires-python` floors at `>=3.10` and CI runs the matrix from 3.10 to 3.14t, so the exhaustive -`match` over `_EofAction` in `fixer.py` cannot use `typing.assert_never`, which is 3.11+, and -`typing_extensions` would become a new direct dependency. The original plan leaned on the return -type and was dropped: `fix_file` is typed `-> bool`, so a lost `match` arm falls through to an -implicit `None` that ty already rejects, but the error surfaces on the return type not the `match`, -and it vanishes for any future consumer of `_EofAction` that legitimately returns `None`. The -two-line vendored `_assert_never(value: NoReturn) -> NoReturn` pins the error at the `case _:` arm -on every supported version for no dependency, and dropping 3.10 to save it would be an -outward-facing breaking change. Python 3.10 reaches end of life on 2026-10-31, which is when the -floor moves to 3.11 and the helper becomes `from typing import assert_never`. diff --git a/docs/agents/domain.md b/docs/agents/domain.md index 45a5ff4..eb07f35 100644 --- a/docs/agents/domain.md +++ b/docs/agents/domain.md @@ -18,7 +18,7 @@ Single-context repo: ├── CONTEXT.md ├── docs/adr/ │ ├── 0001-build-on-pathspec-for-nested-gitignore.md -│ └── 0002-keep-py310-vendored-assert-never.md +│ └── 0003-fix-directory-returns-a-path-list-and-writes-nothing.md └── eof_fixer/ ``` diff --git a/eof_fixer/fixer.py b/eof_fixer/fixer.py index ef97917..79cbb2e 100644 --- a/eof_fixer/fixer.py +++ b/eof_fixer/fixer.py @@ -2,7 +2,7 @@ import os import pathlib from collections.abc import Iterator, Sequence -from typing import IO, NoReturn +from typing import IO, assert_never from eof_fixer.discovery import iter_text_files @@ -11,11 +11,6 @@ _BINARY_SAMPLE_SIZE = 1024 -def _assert_never(value: NoReturn) -> NoReturn: - """Static-exhaustiveness guard: ty errors if any union member can reach here.""" - raise AssertionError(value) # pragma: no cover - ty proves every call unreachable (ADR-0002) - - @dataclasses.dataclass(frozen=True) class Noop: """No change: the file is empty or already ends with exactly one terminator.""" @@ -100,7 +95,7 @@ def fix_file(file_obj: IO[bytes], *, check: bool) -> bool: file_obj.truncate() return True case _: # pragma: no cover - exhaustiveness guard; ty verifies every _EofAction is matched - _assert_never(action) + assert_never(action) def fix_directory( diff --git a/pyproject.toml b/pyproject.toml index 2d7eda0..d4ef8db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "eof-fixer" description = "CLI tool that ensures text files end with exactly one newline" license = "MIT" readme = "README.md" -requires-python = ">=3.10,<4" +requires-python = ">=3.11,<4" dependencies = [ "pathspec>=0.12", ] @@ -22,7 +22,6 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Typing :: Typed", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13",