chore!: modernise tooling, raise the python floor to 3.12 #minor - #9
Merged
Merged
Conversation
Drops support for python 3.8-3.11 (breaking change for consumers): updates `requires-python`, the `Programming Language :: Python` classifiers (now 3.12/3.13 only), and the `pydependence` pre-commit hook's `language_version`. Also drops the now-unneeded `typing-extensions` runtime dependency, since `typing.Annotated` has been in the stdlib since 3.9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same config as mtg-dataset/disent/norfair-rs: line-length 120, `select = ["E4","E7","E9","F","I","UP","TID251","B009","B010"]`, single-line isort imports, and a banned-api list for `typing.Any`/`typing.cast`. Ran `ruff check --fix` and `ruff format`, then fixed the remainder by hand: 5 `str, Enum` classes to `StrEnum` (UP042), and a `TYPE_CHECKING`-guarded `tomlkit` import so its quoted type annotations resolve (F821) now that the runtime import stays lazy. The AST-fixture package `tests/test-packages/` is excluded (`extend-exclude` + `force-exclude`): its files are fake modules with imports to non-existent packages, parsed only via `ast.parse` by the import-parser tests, never executed -- linting/formatting them would "fix" away the exact bytes the tests assert on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same base as mtg-dataset: pre-commit-hooks v6.0.0, ruff-pre-commit v0.16.6. No `uv-lock` hook / committed `uv.lock`, and the `ty` hook runs `ty` directly via `uvx` instead of the standard `ty-pre-commit` hook (which shells out to `uv check`): pyproject.toml's bootstrapped `example-legacy` extra intentionally pins `pydantic<2`, conflicting with the `pydantic>=2.0.0` in the base `[project.dependencies]`. Any `uv` command that resolves the full project (`uv lock`, `uv sync`, `uv check`) fails on this by design -- it's a demo of resolving conflicting `env=`-scoped versions, never meant to be installed as a real extra. Kept the project's own `pydependence` self-hook, which regenerates `pyproject.toml`'s dependency lists from `pydependence/`'s own imports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds lint.yaml, test.yaml, release.yaml, each a thin caller of the shared reusable workflows in nmichlo/.github, matching mtg-dataset. The old python-lint.yml/python-publish.yml/python-test.yml/version-bump.yml split (removed in the floor-bump commit) never worked for publishing: version-bump.yml tagged on merge and python-publish.yml triggered on tag push, but a tag pushed with GITHUB_TOKEN doesn't trigger another workflow. release.yaml owns both triggers in one run, so it can actually publish. release.yaml declares `permissions: contents: write` on the job, since a reusable workflow can't hold more permission than its caller. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds `[tool.ty]` (python 3.12, excluding the AST-fixture package for the same reason as ruff) and gets `uvx ty@0.0.79 check` to "All checks passed!". Real bug found: `from . import x` (a bare relative import, where `ast.ImportFrom.module` is `None`) crashed `visit_ImportFrom` with `TypeError: sequence item 1: expected str instance, NoneType found` (pydependence/_core/module_imports_ast.py). `from .submodule import x` was fine since `.module` is set there. Fixed and added a regression test. The rest are `bool | None` / `str | None` config fields that are only narrowed to non-`None` by a separate `set_defaults`/`apply_defaults` call made before use -- added `assert ... is not None` at each use site to document and check the invariant, since ty can't see across that call. Also: converted 3 pydantic `@model_validator(mode="after")` classmethods to plain instance methods (pydantic's stub only types the instance-method form), fixed a `dict` key-type mismatch from a `None`-as-default-env convenience, typed `_AstImportsCollector`'s node params as `ast.stmt | ast.expr` (not `ast.AST`, which has no `lineno`/`col_offset`), and dropped the dead python-3.8 `ast.unparse` polyfill. Co-Authored-By: Claude Opus 5 <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.
Part of standardising CI and tooling across my repos against
nmichlo/.github.The release chain could never publish
version-bump.ymltagged on merge;python-publish.ymltriggered on tag push. A tagpushed with
GITHUB_TOKENdoes not trigger another workflow, so the publish never fired.Both are replaced by a caller of the shared
release.yaml, which owns both triggers in onerun.
lint.yamlandtest.yamlcall the shared workflows too.Python floor 3.8 -> 3.12
Breaking for downstream consumers. Classifiers and CI matrices updated to match. The
code already used
from enum import StrEnum, which needs 3.11 -- so the declared floor waswrong, not just old.
A real bug in relative-import parsing
_core/module_imports_ast.pycrashed on a bare relative import:ast.ImportFrom.moduleisNoneforfrom . import x(it is only set forfrom .sub import x), but the code appended it unconditionally before".".join(...).Found by ty's
invalid-argument-typecheck onlist.append. Fixed, withtests/test_module_data.py::test_get_module_imports_bare_relativeas a regression test.Given this repo's whole job is parsing imports, that is a good one to have caught.
The self-hook was running 3.12 code under Python 3.9
This repo dogfoods itself. The published hook runs
python3 -m pydependence, and-mputsCWD first on
sys.path-- so it was already executing the working copy inside a pinnedrelease's environment. That environment predates the 3.12 floor, so:
Rather than wait for a new tag, the self-hook is now a
localhook running the projectvenv directly. That is what was effectively happening, minus the version skew, and it
removes the chicken-and-egg of needing a release to lint the code that produces it.
.pre-commit-hooks.yamlalso gainedlanguage_version: "3.12"so consumers of thepublished hook get a correct interpreter.
Two deviations from the shared config, both forced
No
uv-lockhook and no committeduv.lock, andtyruns as a local hook rather thanvia
ty-pre-commit.pyproject.toml's bootstrappedexample-legacyextra intentionallypins
pydantic<2, conflicting withpydantic>=2.0.0in the base dependencies -- it is ademo of this tool's
env=-scoped version resolution, documented in the README, and nevermeant to be installed. Any command that resolves the whole project (
uv lock,uv sync,uv check-- whichty-pre-commitshells out to) fails on it by design.uv'sconflictsmarking only covers extra-vs-extra, not extra-vs-base. Running
tydirectly sidestepsproject resolution;
uv pip install -e ".[test]"still works, so the shared lint workflowis unaffected.
Also flagged, not fixed
pyproject.tomlhas a[tool.pytest]section where pytest expects[tool.pytest.ini_options]-- as written it is silently ignored. Pre-existing andunrelated, so left alone.
Testing
pre-commit run --all-files-> all 10 hooks passpytest-> 20 passed (19 original + 1 new regression test)ruff check,ruff format --check,ty check-> clean