From 534a6364c1fff8abfd69d7f79593a3f969c9f749 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 10:29:23 -0400 Subject: [PATCH 1/9] ci: switch from pre-commit to prek prek is a drop-in, Rust-based reimplementation of pre-commit that reads the same .pre-commit-config.yaml. Swaps the pre-commit dependency and invocations (nox session / CI action) for prek. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 17 ++------------- pyproject.toml | 2 +- uv.lock | 57 +++++++++++++------------------------------------- 3 files changed, 18 insertions(+), 58 deletions(-) diff --git a/noxfile.py b/noxfile.py index ba9163e..faf583a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -27,21 +27,8 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: - """Run the pre-commit hooks.""" - # no-commit-to-branch guards a human's local `git commit`/`git push`, - # not a manual "run every hook over all files" invocation like this - # one -- which CI also runs on every push to `main`, where it would - # otherwise always fail. Skipped here (locally or in CI); the - # installed git hook still catches the real case. Add it to any SKIP - # a caller already set, rather than clobbering it. - skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) - s.run( - "pre-commit", - "run", - "--all-files", - *s.posargs, - env={"SKIP": skip}, - ) + """Run prek.""" + s.run("prek", "run", "--all-files", *s.posargs) @session(uv_groups=["lint"], reuse_venv=True) diff --git a/pyproject.toml b/pyproject.toml index 5792984..dd1d6be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ build = [ ] lint = [ "mypy>=1.19.0", - "pre-commit>=4.1.0", + "prek>=0.5.3", "pylint>=3.3.8", ] nox = [ diff --git a/uv.lock b/uv.lock index 5feeb9a..9660867 100644 --- a/uv.lock +++ b/uv.lock @@ -264,15 +264,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, ] -[[package]] -name = "cfgv" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, -] - [[package]] name = "charset-normalizer" version = "3.5.1" @@ -705,7 +696,7 @@ dev = [ { name = "mypy" }, { name = "nox" }, { name = "nox-uv" }, - { name = "pre-commit" }, + { name = "prek" }, { name = "pylint" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -714,7 +705,7 @@ dev = [ ] lint = [ { name = "mypy" }, - { name = "pre-commit" }, + { name = "prek" }, { name = "pylint" }, ] nox = [ @@ -748,7 +739,7 @@ dev = [ { name = "mypy", specifier = ">=1.19.0" }, { name = "nox", specifier = ">=2024.10.9" }, { name = "nox-uv", specifier = ">=0.6.3" }, - { name = "pre-commit", specifier = ">=4.1.0" }, + { name = "prek", specifier = ">=0.5.3" }, { name = "pylint", specifier = ">=3.3.8" }, { name = "pytest", specifier = ">=8.3.3" }, { name = "pytest-cov", specifier = ">=6.2.1" }, @@ -757,7 +748,7 @@ dev = [ ] lint = [ { name = "mypy", specifier = ">=1.19.0" }, - { name = "pre-commit", specifier = ">=4.1.0" }, + { name = "prek", specifier = ">=0.5.3" }, { name = "pylint", specifier = ">=3.3.8" }, ] nox = [ @@ -833,15 +824,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/aa/0b7365d30fed43e7a3449aba1fe20a0a7174d9cf13e282af4e69ac825441/humanize-4.16.0-py3-none-any.whl", hash = "sha256:353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d", size = 137209, upload-time = "2026-06-30T16:17:28.36Z" }, ] -[[package]] -name = "identify" -version = "2.6.19" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -1475,15 +1457,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c5/3c/3179b85b0e1c3659f0369940200cd6d0fa900e6cefcc7ea0bc6dd0e29ffb/nest_asyncio2-1.7.2-py3-none-any.whl", hash = "sha256:f5dfa702f3f81f6a03857e9a19e2ba578c0946a4ad417b4c50a24d7ba641fe01", size = 7843, upload-time = "2026-02-13T00:34:02.691Z" }, ] -[[package]] -name = "nodeenv" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, -] - [[package]] name = "nox" version = "2026.8.17" @@ -1784,19 +1757,19 @@ wheels = [ ] [[package]] -name = "pre-commit" -version = "4.6.2" +name = "prek" +version = "0.5.3" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/89/1f3e8e1fc3e97de0fa963495832f581f025f29471602a309e48808244292/pre_commit-4.6.2.tar.gz", hash = "sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441", size = 198670, upload-time = "2026-08-10T22:07:18.421Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/93/d1e5afc996b9fde04d71c37fd28bdd404e54daf1da6c76b883c5cdbdb411/prek-0.5.3.tar.gz", hash = "sha256:06d88bed9a5b2886cd3796957e4cecf05fa9b06724b625df31dbf0a48ff2330c", size = 551929, upload-time = "2026-09-13T08:38:37.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/e2/bbb7129c9e7999a6b8ee9cca3b66486c25c423ab5a75f34071798b74ce94/pre_commit-4.6.2-py2.py3-none-any.whl", hash = "sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e", size = 226202, upload-time = "2026-08-10T22:07:16.942Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/8731dc49cb5424d37424db3ea2f3ff6a0b20caa971db72796e822ce48b24/prek-0.5.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2d7240c5e6a996ef5bebe5d57d43049803e41c225b343681c020bf0c87dab2b2", size = 6003518, upload-time = "2026-09-13T08:38:20.584Z" }, + { url = "https://files.pythonhosted.org/packages/51/45/ca413aefb3ea2411bbf0adb6eebfac1e9bfc5ac7679dbe26336fc651c347/prek-0.5.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1dd6df8235ca361dbbeec89c305ccb41a138088a2fc548484b4cb89f66a80d20", size = 5538754, upload-time = "2026-09-13T08:38:23.004Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/874d58ba40d2fe7cfec2789ae8bf415f3751666ad5c59928821fe53c3c01/prek-0.5.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:61ff791bb850ba52cc0d86576498df7e1bd1e9ed20df63dae113a4a6aa495e19", size = 5841568, upload-time = "2026-09-13T08:38:25.008Z" }, + { url = "https://files.pythonhosted.org/packages/8f/40/167037b8eef75f6f036c60ff7e93f42defebb7c3733677e2f30e5a479257/prek-0.5.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a863379a2668c4ef079d820ffd5b14073145380dfbe0e70fef498dc73c568f86", size = 6226960, upload-time = "2026-09-13T08:38:26.788Z" }, + { url = "https://files.pythonhosted.org/packages/42/9a/bfcd3c1fe1fdefee8dc144a92c4b0b93b59f31c7df60ee1720c49223a90a/prek-0.5.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:086d1b77557f0a089568dc5b93c22ccd793351524f3eeb29d36781421a350e72", size = 5854832, upload-time = "2026-09-13T08:38:28.954Z" }, + { url = "https://files.pythonhosted.org/packages/15/41/1450b995b18bbf0d8ab65016860198052baad5c79c231151fa1dcca4a2de/prek-0.5.3-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:4f09b947255124e9591a7f7ef16eb5340a36ba94a275d788d7ea804fab35f0e2", size = 6343055, upload-time = "2026-09-13T08:38:30.96Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e8/8f45950b8a8ddcf22efc7183e9d6b1d7e30373229ff7d9e56f918a55922c/prek-0.5.3-py3-none-win_amd64.whl", hash = "sha256:5b74a9742c3e8f8688d5dad1439688f112e2e1746fcb91016015dae38df3eb42", size = 5744293, upload-time = "2026-09-13T08:38:33.104Z" }, + { url = "https://files.pythonhosted.org/packages/24/cd/ce637f4b6cc6c583516dc16b539408dab593672f7c6fe7723a37caf378ff/prek-0.5.3-py3-none-win_arm64.whl", hash = "sha256:20d92aef53a5e237f4659ecc6669c0fbc98a1b83f0e2476530400a72d42ca390", size = 5512958, upload-time = "2026-09-13T08:38:36.123Z" }, ] [[package]] From d7f87acf49d61ad7e604a58a61d3160a2a06eded Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 11:23:07 -0400 Subject: [PATCH 2/9] fix(ci): skip no-commit-to-branch in the full-suite CI run no-commit-to-branch would otherwise fail every push to main: CI checks out a real local branch literally named `main` for push events, so the hook would always fire. It's a client-side guard for a human running `git commit`/`git push` locally (or via installed git hooks) -- not something a full "run every hook" CI invocation should re-evaluate after the fact. Skips it there via SKIP=no-commit-to-branch; the hook itself is untouched and still fully active locally. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index faf583a..6ec8136 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,10 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: """Run prek.""" - s.run("prek", "run", "--all-files", *s.posargs) + # no-commit-to-branch always fails here: CI checks out the real + # `main` branch on every push, which is exactly what the hook exists to + # block for a human running `git commit`/`git push` locally. + s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": "no-commit-to-branch"}) @session(uv_groups=["lint"], reuse_venv=True) From 4030154b47aa5e75e0575a96828640fc2aa1fee0 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 11:40:36 -0400 Subject: [PATCH 3/9] fix(ci): don't clobber an existing SKIP when skipping no-commit-to-branch Appends no-commit-to-branch to any SKIP a developer already has set (e.g. via their shell) rather than overwriting it wholesale, matching the same fix applied in response to Copilot review feedback on GalacticDynamics/coordinax#885. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 6ec8136..06f2215 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,8 +30,10 @@ def precommit(s: nox.Session, /) -> None: """Run prek.""" # no-commit-to-branch always fails here: CI checks out the real # `main` branch on every push, which is exactly what the hook exists to - # block for a human running `git commit`/`git push` locally. - s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": "no-commit-to-branch"}) + # block for a human running `git commit`/`git push` locally. Add it to + # any SKIP a caller already set, rather than clobbering it. + skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) + s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": skip}) @session(uv_groups=["lint"], reuse_venv=True) From 38613f81a89e2c038c263d166713d7d4a912ce25 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 12:31:29 -0400 Subject: [PATCH 4/9] docs(nox): clarify the no-commit-to-branch skip isn't CI-specific Addresses Copilot review feedback on GalacticDynamics/galax#847: the comment said "CI checks out the real main branch," but the skip applies unconditionally, including local `nox -s lint` runs -- which is correct (a CI-only skip would leave the same false failure for any local dev running the full suite while on `main`). Fixes the wording to match the actual, intended behavior instead of narrowing it. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/noxfile.py b/noxfile.py index 06f2215..3da8a0d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,10 +28,12 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: """Run prek.""" - # no-commit-to-branch always fails here: CI checks out the real - # `main` branch on every push, which is exactly what the hook exists to - # block for a human running `git commit`/`git push` locally. Add it to - # any SKIP a caller already set, rather than clobbering it. + # no-commit-to-branch guards a human's local `git commit`/`git push`, + # not a manual "run every hook over all files" invocation like this + # one -- which CI also runs on every push to `main`, where it would + # otherwise always fail. Skipped here (locally or in CI); the + # installed git hook still catches the real case. Add it to any SKIP + # a caller already set, rather than clobbering it. skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": skip}) From 9f7fd4e713c364cce4962cbec9a8204286c66871 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 12:37:10 -0400 Subject: [PATCH 5/9] docs(nox): clarify the precommit session still runs via prek Addresses Copilot review feedback on GalacticDynamics/dataclassish#94: the docstring "Run prek." on a session still named `precommit` could read as though the session itself was renamed. Spells out that it runs the pre-commit hooks, now via prek. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 3da8a0d..12695e3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -27,7 +27,7 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: - """Run prek.""" + """Run the pre-commit hooks (via prek).""" # no-commit-to-branch guards a human's local `git commit`/`git push`, # not a manual "run every hook over all files" invocation like this # one -- which CI also runs on every push to `main`, where it would From 677cafcfa848786b06873b8fc9a6d61cda81716e Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 12:42:00 -0400 Subject: [PATCH 6/9] docs(nox): fix now-inaccurate git-push mention in the SKIP comment Addresses Copilot review feedback on GalacticDynamics/galax#847: this comment still said no-commit-to-branch guards `git push`, but the earlier stages: [pre-commit] fix means it no longer runs on push at all. Clarifies that explicitly instead of leaving stale wording. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index 12695e3..162ee81 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,12 +28,13 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: """Run the pre-commit hooks (via prek).""" - # no-commit-to-branch guards a human's local `git commit`/`git push`, - # not a manual "run every hook over all files" invocation like this - # one -- which CI also runs on every push to `main`, where it would - # otherwise always fail. Skipped here (locally or in CI); the - # installed git hook still catches the real case. Add it to any SKIP - # a caller already set, rather than clobbering it. + # no-commit-to-branch guards a human's local `git commit` (it's + # scoped to stages: [pre-commit] in .pre-commit-config.yaml, so it + # never runs on push) -- not a manual "run every hook over all files" + # invocation like this one, which CI also runs on every push to + # `main`, where it would otherwise always fail. Skipped here (locally + # or in CI); the installed git hook still catches the real case. Add + # it to any SKIP a caller already set, rather than clobbering it. skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": skip}) From 5a9a532113b5508f3b2e289ac0e71a2fa7b0487f Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 12:56:06 -0400 Subject: [PATCH 7/9] docs: disambiguate "never fires on push" from the workflow's own push trigger Addresses Copilot review feedback on GalacticDynamics/galax#847: "it never fires on push" reads as a claim about this workflow's own `on: push:` trigger (which is false -- that's why the SKIP exists at all), when it actually means the git pre-push hook stage. Spells that out explicitly. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/noxfile.py b/noxfile.py index 162ee81..2ad29fc 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,11 +30,12 @@ def precommit(s: nox.Session, /) -> None: """Run the pre-commit hooks (via prek).""" # no-commit-to-branch guards a human's local `git commit` (it's # scoped to stages: [pre-commit] in .pre-commit-config.yaml, so it - # never runs on push) -- not a manual "run every hook over all files" - # invocation like this one, which CI also runs on every push to - # `main`, where it would otherwise always fail. Skipped here (locally - # or in CI); the installed git hook still catches the real case. Add - # it to any SKIP a caller already set, rather than clobbering it. + # never fires as a pre-push git hook) -- not a manual "run every + # hook over all files" invocation like this one, which CI also runs + # on every push to `main`, where it would otherwise always fail. + # Skipped here (locally or in CI); the installed git hook still + # catches the real case. Add it to any SKIP a caller already set, + # rather than clobbering it. skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": skip}) From b5de5eb8b7752a2c570aaa8e1ae69ce3914de004 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 13:11:31 -0400 Subject: [PATCH 8/9] fix(deps): pin pyyaml floor, drop the now-dead nodeenv one Switching pre-commit -> prek removed pre-commit's own `pyyaml>=5.1` requirement, which had been implicitly keeping --resolution lowest's global pyyaml pick high enough to build. With it gone, the only remaining constraint is commitizen's (via cz-conventional-gitmoji) unbounded pyyaml, so --resolution lowest dropped to pyyaml 3.10 -- a 2013 sdist that fails to compile against modern CPython's C API, breaking "Check Oldest Dependencies". Restores the same pyyaml>=5.1 floor pre-commit used to provide. Also drops the nodeenv>=1.9.1 constraint added for the same reason on pre-commit's behalf: nodeenv was pre-commit's own dependency and has no other consumer, so it's no longer part of the graph at all now that prek (a standalone binary) replaced it. Co-Authored-By: Claude Sonnet 5 --- pyproject.toml | 14 ++++++++------ uv.lock | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dd1d6be..b7acc7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -193,12 +193,14 @@ src = ["src"] [tool.uv] constraint-dependencies = [ - # `pre-commit` allows any `nodeenv>=0.11.1`, and 0.11.1 is a 2012 sdist whose - # `setup.py` imports `pkg_resources` at build time -- long gone from modern - # setuptools, so `--resolution lowest` cannot build it and the "Check Oldest - # Dependencies" job dies before collecting a single test. Dev tooling: its - # floor says nothing about which dependencies this library supports. - "nodeenv>=1.9.1", + # `commitizen` (pulled in via `cz-conventional-gitmoji`) allows any bare + # `pyyaml`, and `--resolution lowest` picks 3.10 -- a 2013 sdist that fails + # to build against modern CPython's C API (`PyCode_New`'s signature + # changed, `PyFrameObject` is now an incomplete type). `pre-commit` used to + # mask this by requiring `pyyaml>=5.1`; switching it out for `prek` removed + # that floor. Dev tooling: its floor says nothing about which dependencies + # this library supports. + "pyyaml>=5.1", # `build` depends on a bare, unbounded `pyproject_hooks`, so `--resolution # lowest` picks 0.1.0 -- a 2021 release predating `BuildBackendHookCaller`, # which `build` 1.x calls unconditionally, so `nox -s build` dies with an diff --git a/uv.lock b/uv.lock index 9660867..21d970e 100644 --- a/uv.lock +++ b/uv.lock @@ -20,7 +20,6 @@ constraints = [ { name = "jax", specifier = ">0.4.34" }, { name = "matplotlib", specifier = ">=3.10" }, { name = "nest-asyncio", specifier = ">=0.1.0" }, - { name = "nodeenv", specifier = ">=1.9.1" }, { name = "opt-einsum", specifier = ">=3.3.0" }, { name = "pickleshare", specifier = ">=0.7" }, { name = "pillow", specifier = ">=10.3.0" }, @@ -29,6 +28,7 @@ constraints = [ { name = "pycparser", specifier = ">=2.10" }, { name = "pyparsing", specifier = ">3.0.0" }, { name = "pyproject-hooks", specifier = ">=1.0.0" }, + { name = "pyyaml", specifier = ">=5.1" }, { name = "scipy", specifier = ">=1.14" }, { name = "tomli", specifier = ">=1.0" }, { name = "wcwidth", specifier = ">=0.1.0" }, From fbb24b7e778d2ad11437564bdf3ea63febd9ee52 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 15 Sep 2026 13:53:36 -0400 Subject: [PATCH 9/9] docs: trim verbose inline comments down to the load-bearing WHY The archaeology (why pre-commit's nodeenv/pyyaml floors mattered, why --skip clobbers, the full CI-checkout explanation) belongs in commit history, not permanently inline. Keeps just enough to orient a future reader without re-litigating the whole investigation. Co-Authored-By: Claude Sonnet 5 --- noxfile.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/noxfile.py b/noxfile.py index 2ad29fc..046a753 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,14 +28,8 @@ def lint(s: nox.Session, /) -> None: @session(uv_groups=["lint"], reuse_venv=True) def precommit(s: nox.Session, /) -> None: """Run the pre-commit hooks (via prek).""" - # no-commit-to-branch guards a human's local `git commit` (it's - # scoped to stages: [pre-commit] in .pre-commit-config.yaml, so it - # never fires as a pre-push git hook) -- not a manual "run every - # hook over all files" invocation like this one, which CI also runs - # on every push to `main`, where it would otherwise always fail. - # Skipped here (locally or in CI); the installed git hook still - # catches the real case. Add it to any SKIP a caller already set, - # rather than clobbering it. + # Not a real commit -- no-commit-to-branch would always fail here. + # Merge into any SKIP already set, rather than clobber it. skip = ",".join(filter(None, [os.environ.get("SKIP"), "no-commit-to-branch"])) s.run("prek", "run", "--all-files", *s.posargs, env={"SKIP": skip})