From 1a37ddf5d7fab824d05f304521500fee72a7bd4e Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 03:48:44 +0300 Subject: [PATCH 1/7] Replace pre-commit with prek in the dev group prek 0.5.3 is the newest release outside the 7-day exclude-newer cooldown. It is a single binary, so pre-commit's Python dependencies (cfgv, identify, nodeenv, pyyaml, virtualenv and theirs) leave uv.lock. Co-Authored-By: Claude Opus 5.5 --- pyproject.toml | 18 +++--- uv.lock | 172 ++++--------------------------------------------- 2 files changed, 22 insertions(+), 168 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d177b569..b9226d60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,10 +78,10 @@ Repository = "https://github.com/permitio/permit-python" # resolve the same versions. Dependabot raises them. A pin also gives the CVE # scan a version to evaluate: a spec with no bound has none, so a package listed # that way is absent from every audit. These pins are the only place the ruff, -# mypy and typos versions are set: their pre-commit hooks are `repo: local` and -# run the copies `uv run --locked` installs from uv.lock, so CI lints and -# type-checks with exactly these versions, and a Dependabot bump of a pin moves -# the hook with it. +# mypy and typos versions are set: their hooks in .pre-commit-config.yaml are +# `repo: local` and run the copies `uv run --locked` installs from uv.lock, so +# CI lints and type-checks with exactly these versions, and a Dependabot bump of +# a pin moves the hook with it. # aioresponses is left out on purpose. No test imports it, and its latest # release (0.7.9) is incompatible with the aiohttp 3.14.3 floor: every mocked # request raises "ClientResponse.__init__() missing 1 required keyword-only @@ -94,7 +94,7 @@ dev = [ # Imported directly by the offline tests, which evaluate the version markers # in [project].dependencies the way an installer does. "packaging==26.3", - "pre-commit==4.6.2", + "prek==0.5.3", # 9.x rather than 8.x: the old 8.3.0 floor is affected by CVE-2025-71176 # (insecure temporary directory handling), fixed in 9.0.3. Caught by this # repo's own audit gate. @@ -110,8 +110,8 @@ dev = [ "typos==1.50.2", # The uv version CI runs: every setup-uv step reads it from uv.lock # (version-file), except the publish build job, which pins its own version - # and checksum. Dependabot bumps it like any other pin. The uv-lock pre-commit - # hook runs the uv on PATH, which under `uv run` (as in CI) is this one. Run + # and checksum. Dependabot bumps it like any other pin. The uv-lock hook runs + # the uv on PATH, which under `uv run` (as in CI) is this one. Run # this version locally so uv.lock comes out the same. "uv==0.12.17", # Imported directly by the offline tests (Request/Response are used to assert @@ -183,8 +183,8 @@ line-length = 100 # The migration skill's sample apps are the scanner's test input, written the # way a permit 2.x project is; their exact text is what the tests assert on. extend-exclude = ["permit/api/models.py", "skills/tests/fixtures"] -# pre-commit passes file names explicitly, which bypasses exclusions unless -# this is set -- without it the hook would lint and reformat models.py. +# prek passes file names explicitly, which bypasses exclusions unless this +# is set -- without it the hook would lint and reformat models.py. force-exclude = true [tool.ruff.per-file-target-version] diff --git a/uv.lock b/uv.lock index 5c853a53..56da2b31 100644 --- a/uv.lock +++ b/uv.lock @@ -275,15 +275,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, ] -[[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 = "colorama" version = "0.4.6" @@ -293,15 +284,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "distlib" -version = "0.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z" }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -336,15 +318,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] -[[package]] -name = "filelock" -version = "3.32.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/59/e19834834cb01a32febfbb0f8a23a9088088f5d45991824ff2bc3b5e8acb/filelock-3.32.7.tar.gz", hash = "sha256:37b8a3d9811b0f9aef7e5ec5c71bb320de52df51e6ca9bcd6f5ad81187660da7", size = 225154, upload-time = "2026-09-16T00:24:20.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/df/31098c5aeb4d966b553641472bd55fcf5fdfac953549894b8a765ba44e91/filelock-3.32.7-py3-none-any.whl", hash = "sha256:65ff0d0190ea42038b32bda4b77834fb05be2cad4c5b9b01aa4dfb3614536e52", size = 100157, upload-time = "2026-09-16T00:24:19.543Z" }, -] - [[package]] name = "frozenlist" version = "1.8.0" @@ -466,15 +439,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] -[[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 = "idna" version = "3.19" @@ -978,15 +942,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] -[[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 = "packaging" version = "26.3" @@ -1021,7 +976,7 @@ dependencies = [ dev = [ { name = "mypy" }, { name = "packaging" }, - { name = "pre-commit" }, + { name = "prek" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-httpserver" }, @@ -1052,7 +1007,7 @@ requires-dist = [ dev = [ { name = "mypy", specifier = "==2.3.1" }, { name = "packaging", specifier = "==26.3" }, - { name = "pre-commit", specifier = "==4.6.2" }, + { name = "prek", specifier = "==0.5.3" }, { name = "pytest", specifier = "==9.1.1" }, { name = "pytest-asyncio", specifier = "==1.4.0" }, { name = "pytest-httpserver", specifier = "==1.1.5" }, @@ -1065,15 +1020,6 @@ dev = [ pydantic-v1 = [{ name = "pydantic", specifier = "<2" }] pydantic-v2 = [{ name = "pydantic", specifier = ">=2" }] -[[package]] -name = "platformdirs" -version = "4.11.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/18/f3bb8ef0d3b930692343da8aa4d3cbcd6749477c053959395ac81965a6e9/platformdirs-4.11.8.tar.gz", hash = "sha256:f23abafea7dd4276d1f29104b83598d7dcc567cafd07c9c951e66665645437fc", size = 37182, upload-time = "2026-09-08T22:20:42.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", hash = "sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", size = 24027, upload-time = "2026-09-08T22:20:41.537Z" }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -1084,19 +1030,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]] @@ -1487,82 +1433,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/df/0bdf90b84c6a586a9fd2b509523a3ab26b1cc1b1dba2fb62a32e4411ea9e/pytest_httpserver-1.1.5-py3-none-any.whl", hash = "sha256:ee83feb587ab652c0c6729598db2820e9048233bac8df756818b7845a1621d0a", size = 23330, upload-time = "2026-02-14T13:27:22.119Z" }, ] -[[package]] -name = "python-discovery" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/96/0f93e27c9f60a650838f2118159aa115fd5732c0716247917b7ba7ede665/python_discovery-1.6.0.tar.gz", hash = "sha256:6393b4eae1be8b2182670635e7baff89ac21cb9f8e86fd1ff40c7b1144febb4c", size = 82849, upload-time = "2026-08-28T17:30:02.366Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/5e/21abf578182fb15006a57faf3711a1e659e29d600d19b6e557eae908c81d/python_discovery-1.6.0-py3-none-any.whl", hash = "sha256:d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a", size = 38451, upload-time = "2026-08-28T17:30:01.236Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - [[package]] name = "ruff" version = "0.16.8" @@ -1706,22 +1576,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/48/ded1dc765921d023e6551355711dcd9b076cd065d8f4425d08a17e4aea00/uv-0.12.17-py3-none-win_arm64.whl", hash = "sha256:c900ee8de1d2294b08e81271b7635947c7ab6df74023b78caddf9a41038003f2", size = 19283768, upload-time = "2026-09-18T18:57:37.701Z" }, ] -[[package]] -name = "virtualenv" -version = "21.7.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "python-discovery" }, - { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'group-6-permit-pydantic-v1' and extra == 'group-6-permit-pydantic-v2')" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/5acd348310e0803c658c8cf7c4d928e2d22fc4f79c29098b651cd3edfdba/virtualenv-21.7.10.tar.gz", hash = "sha256:a7bf10f37ecc36f1942d6e469d6b59f5fe308f60ac711f66f51ef3bd8cb2c9aa", size = 5350247, upload-time = "2026-09-15T23:36:05.739Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/7c/b75957b57ef372d84628b1099c4a530b3c361878cc6c54a8dfc5071d371a/virtualenv-21.7.10-py3-none-any.whl", hash = "sha256:d7ac9669ba19e675ffadbb97fe8e887ef92fb1743fe9a0032be62b947657327a", size = 5324900, upload-time = "2026-09-15T23:36:03.751Z" }, -] - [[package]] name = "werkzeug" version = "3.1.8" From 3677aa31d532492d7ba9e98f99a606a537939289 Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 03:57:47 +0300 Subject: [PATCH 2/7] Adapt the hook config to prek prek ignores minimum_pre_commit_version, so the floor moves to minimum_prek_version, which prek enforces. `language: system` is prek's name for what pre-commit 4.4 calls `unsupported`; prek reads both. prek replaces the pre-commit-hooks hooks with built-in Rust versions unless a hook sets its language. On a corpus of edge cases those versions diverge from the pinned v6.0.0 hooks (unknown YAML tags, TOML 1.1 syntax, an empty JSON file, a BOM or NaN in JSON, a lone `=======` during a merge, vertical tabs and CR-only line endings), so each hook sets `language: python` and keeps the pinned implementation, which fails and fixes the same files pre-commit did. Co-Authored-By: Claude Opus 5.5 --- .pre-commit-config.yaml | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b852b036..d609c515 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,45 @@ -# `language: unsupported` (the pre-commit 4.4 name for `system`) runs a command -# from the environment pre-commit was started in. -minimum_pre_commit_version: "4.4.0" +# Run by prek (see CONTRIBUTING.md). `language: system` runs a command from the +# environment prek was started in. +# +# An older prek, such as an old global install, stops with an error here rather +# than running the hooks differently. +minimum_prek_version: "0.5.3" repos: # Pinned to a commit rather than a tag, which can be moved; the `# frozen:` # comment names the release, and Dependabot updates both. + # + # `language: python` makes prek run that release's hooks. Without it, prek + # runs its own Rust versions of them, which differ on some inputs: check-yaml + # accepts unknown tags, check-toml accepts TOML 1.1, check-json accepts an + # empty file, and others. - repo: https://github.com/pre-commit/pre-commit-hooks rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: trailing-whitespace + language: python - id: end-of-file-fixer + language: python - id: check-added-large-files + language: python - id: check-case-conflict + language: python - id: check-executables-have-shebangs + language: python - id: check-shebang-scripts-are-executable + language: python - id: check-json + language: python - id: check-toml + language: python - id: check-yaml + language: python - id: check-xml + language: python - id: check-merge-conflict + language: python - id: mixed-line-ending + language: python args: [--fix=lf] # ruff, mypy and typos run from the project environment, so the versions in @@ -32,14 +52,14 @@ repos: - id: ruff-check name: ruff check entry: uv run --locked ruff check --fix - language: unsupported + language: system # pyproject.toml too: ruff validates its [project] table (RUF200). files: (\.pyi?|(^|/)pyproject\.toml)$ require_serial: true - id: ruff-format name: ruff format entry: uv run --locked ruff format - language: unsupported + language: system types_or: [python, pyi] require_serial: true - id: mypy @@ -47,7 +67,7 @@ repos: # No file names: mypy checks the `files` set in pyproject.toml as a whole, # which is what makes cross-module errors visible. entry: uv run --locked mypy - language: unsupported + language: system # pyproject.toml and uv.lock too: they hold mypy's config and the # dependency versions it checks against. files: (\.pyi?|^pyproject\.toml|^uv\.lock)$ @@ -56,7 +76,7 @@ repos: - id: typos name: typos entry: uv run --locked typos --force-exclude - language: unsupported + language: system types: [text] require_serial: true @@ -70,6 +90,6 @@ repos: - id: uv-lock name: uv lock --check entry: uv lock --check - language: unsupported + language: system files: ^(uv\.lock|pyproject\.toml|uv\.toml)$ pass_filenames: false From 17d34cc95e05cb82259279179d620c0a65ea0dbd Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 04:00:00 +0300 Subject: [PATCH 3/7] Run prek in the pre-commit CI job The job keeps its id, the required status check. prek comes from the locked dev group (`uv run --locked --only-dev`), so CI runs the version in uv.lock rather than the one j178/prek-action would download. The hook cache moves to prek's home, set explicitly so prek and the cache step agree, and its key adds uv.lock, which pins prek and the uv that builds the hook environments. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pre-commit.yml | 37 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 903ec334..eb137c01 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,12 +11,15 @@ permissions: jobs: # The job id is the required status check "pre-commit" on main; keep it. # - # These steps do what pre-commit/action v3.0.1 does, written out: its last - # release pins actions/cache@v4, which targets the deprecated Node 20 - # runtime, and it has had no release since. pre-commit itself comes from - # uv.lock rather than a pip install. + # prek runs the hooks in .pre-commit-config.yaml. It comes from uv.lock rather + # than j178/prek-action, so CI runs the version developers run, Dependabot's + # uv updates move it, and the dependency audit scans it. pre-commit: runs-on: ubuntu-24.04 + env: + # Where prek keeps cloned hook repositories and hook environments. Set + # here so prek and the cache step below use the same path. + PREK_HOME: ~/.cache/prek steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -30,26 +33,26 @@ jobs: python-version: "3.11" enable-cache: true - # Hook environments, keyed on the config that defines them and on the - # Python they were built with, since a hook venv does not survive an - # interpreter change. This is the cache pre-commit/action used to provide. - - name: Cache pre-commit hook environments + # Hook environments, keyed on the config that defines them, on uv.lock, + # which pins prek and the uv that builds them, and on the Python they were + # built with, since a hook venv does not survive an interpreter change. + - name: Cache prek hook environments uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: - path: ~/.cache/pre-commit + path: ${{ env.PREK_HOME }} key: >- - pre-commit-${{ runner.os }}-py${{ steps.setup-uv.outputs.python-version }}-${{ - hashFiles('.pre-commit-config.yaml') }} + prek-${{ runner.os }}-py${{ steps.setup-uv.outputs.python-version }}-${{ + hashFiles('.pre-commit-config.yaml', 'uv.lock') }} - # pre-commit itself comes from the locked dev group. The ruff, mypy and - # typos hooks are `repo: local` and run through `uv run --locked`, which - # syncs .venv to the default groups first: the project, its dependencies + # prek itself needs only the locked dev group. The ruff, mypy and typos + # hooks are `repo: local` and run through `uv run --locked`, which syncs + # .venv to the default groups first: the project, its dependencies # (pydantic 2) and the dev tools, so mypy checks against the SDK's real # dependencies. - - name: Run pre-commit + - name: Run prek run: >- - uv run --locked - pre-commit run --all-files --show-diff-on-failure --color=always + uv run --locked --only-dev + prek run --all-files --show-diff-on-failure --color=always # The SDK imports pydantic differently per major, so its types are checked # against pydantic 1 as well (the hook above ran against pydantic 2). From bb486c7c6086c6909591038b43502dc85d36ecca Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 04:01:07 +0300 Subject: [PATCH 4/7] Group prek with the lint tools in Dependabot prek is 0.x, so a minor release may change how the hooks run; like a new ruff rule, that must not hold up the runtime floor bumps. The pre-commit ecosystem entry stays: Dependabot only reads and rewrites .pre-commit-config.yaml, which prek reads unchanged. Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 557bdd52..cc92b327 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -36,10 +36,12 @@ updates: semver-major-days: 14 groups: # Listed first, since a dependency joins the first group it matches. A new - # lint rule or type-check error in one of these must not hold up the - # runtime floor bumps grouped below, so they get a PR of their own. + # lint rule or type-check error in one of these, or a prek release that + # changes how the hooks run (prek is 0.x, where a minor release may break), + # must not hold up the runtime floor bumps grouped below, so they get a PR + # of their own. lint-tools: - patterns: ["ruff", "mypy", "typos"] + patterns: ["ruff", "mypy", "typos", "prek"] minor-and-patch: update-types: ["minor", "patch"] ignore: @@ -77,12 +79,13 @@ updates: labels: - "dependencies" - # pre-commit hook revisions in .pre-commit-config.yaml. Dependabot follows the - # `# frozen: vX` comment on SHA-pinned revs and rewrites the SHA and the - # comment together. `repo: local` hooks (ruff, mypy, typos, uv-lock) are - # skipped: ruff, mypy and typos come from uv.lock, which the "uv" entry above - # maintains, and uv-lock runs the uv on PATH. Only `default-days` cooldown is - # supported for this ecosystem. + # Hook revisions in .pre-commit-config.yaml. The ecosystem is named after + # pre-commit, but Dependabot only reads and rewrites that file, so it works + # the same with prek. Dependabot follows the `# frozen: vX` comment on + # SHA-pinned revs and rewrites the SHA and the comment together. `repo: local` + # hooks (ruff, mypy, typos, uv-lock) are skipped: ruff, mypy and typos come + # from uv.lock, which the "uv" entry above maintains, and uv-lock runs the uv + # on PATH. Only `default-days` cooldown is supported for this ecosystem. - package-ecosystem: "pre-commit" directory: "/" schedule: From 24144256ff802d6e1fab93e61412c33654fff759 Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 04:02:04 +0300 Subject: [PATCH 5/7] Document prek in CONTRIBUTING.md Setup installs the Git hook with `uv run prek install`, and a clone whose hook pre-commit installed is told to pass --force: otherwise prek keeps that hook as pre-commit.legacy and runs it too, and it fails once pre-commit is gone from .venv. `uv run prek run --all-files` runs every hook as CI does. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1b5a5f3..80d164f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,9 +11,14 @@ the 7-day `exclude-newer` cooldown, which produces a different `uv.lock`. ```sh uv sync # .venv with the SDK and the dev tools, exactly as locked in uv.lock -uv run pre-commit install # lint, format, type-check and uv.lock checks on every commit +uv run prek install # lint, format, type-check and uv.lock checks on every commit ``` +The hooks are defined in `.pre-commit-config.yaml` and run by [prek](https://github.com/j178/prek), +which comes from the `dev` group. If `.git/hooks/pre-commit` was installed by pre-commit, run +`uv run prek install --force` instead: without `--force`, prek keeps that hook as +`pre-commit.legacy` and runs it as well, and it fails because pre-commit is not in `.venv`. + `uv sync` installs the SDK from this checkout in editable mode, so the tests and scripts import the working tree's `permit`. `.python-version` selects Python 3.11, the version the end-to-end CI job runs on. The SDK itself supports Python 3.10 and later. @@ -21,8 +26,14 @@ end-to-end CI job runs on. The SDK itself supports Python 3.10 and later. The ruff, mypy and typos hooks run through `uv run --locked`, which syncs `.venv` to `uv.lock` before running the tool, so the versions in `uv.lock` are the only ones in play; the hooks fail if `uv.lock` is out of date with `pyproject.toml`. That sync uses the default groups, so a commit -also switches a `.venv` synced with `--group pydantic-v1` back to pydantic 2.x. The same checks -by hand: +also switches a `.venv` synced with `--group pydantic-v1` back to pydantic 2.x. To run every +hook on every file, as CI does: + +```sh +uv run prek run --all-files +``` + +The same checks one tool at a time: ```sh uv run ruff check # lint (the rule set is `select = ["ALL"]` minus justified ignores) @@ -47,8 +58,8 @@ uv run --group pydantic-v1 mypy - Dev tools are exact pins in the `dev` dependency group, which `uv sync` installs by default. - After changing either, run `uv lock` and commit `uv.lock` with the change. The `uv-lock` - pre-commit hook fails while the two disagree, and CI installs with `uv sync --locked`, - which refuses a stale lock. + hook fails while the two disagree, and CI installs with `uv sync --locked`, which refuses + a stale lock. - `uv lock` leaves out releases less than 7 days old (`exclude-newer` in `[tool.uv]`), but the dependency audit does not, so it can fail on an advisory whose fix `uv lock` still filters out. To take that fix now, add `exclude-newer-package = { = false }` @@ -101,8 +112,8 @@ versions the runtime requirements allow and at the newest. `tests/test_typing_surface.py` runs mypy on `tests/type_check/consumer.py` the way a user's project sees an installed permit, and fails while `permit/_sync_types.pyi` is out of date -(see [Regenerating the sync stubs](#regenerating-the-sync-stubs)). The `mypy` pre-commit -hook type-checks the SDK itself, strictly and with the pydantic plugin (see [Setup](#setup)). +(see [Regenerating the sync stubs](#regenerating-the-sync-stubs)). The `mypy` hook +type-checks the SDK itself, strictly and with the pydantic plugin (see [Setup](#setup)). ### The migration skill's tests @@ -220,7 +231,7 @@ has to be restored by hand. in `pyproject.toml` and keeps the generator's formatting, so the diff shows only API changes. 5. Run the schema drift check, the offline tests under both pydantic majors (see above) and - `uv run pre-commit run --all-files`. + `uv run prek run --all-files`. ### Schema drift check From bb6cc1194935ca52185fb8a46f70e447d87f838f Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 04:52:32 +0300 Subject: [PATCH 6/7] Pin the Python that prek builds hook environments with prek builds the pre-commit-hooks environment with whichever Python uv picks first, ignoring .python-version, so check-json and check-toml could parse with a newer json or tomllib than the project's 3.11. default_language_version pins it to 3.11, the interpreter pre-commit used when run from .venv. Co-Authored-By: Claude Opus 5.5 --- .pre-commit-config.yaml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d609c515..69e1fc6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,14 +5,21 @@ # than running the hooks differently. minimum_prek_version: "0.5.3" +# The Python that `language: python` hook environments are built with: the one +# in .python-version. Without it prek builds them with whichever Python uv picks +# first, which can be newer than 3.11 or free-threaded, and check-json and +# check-toml then parse with that version's json and tomllib. +default_language_version: + python: "3.11" + repos: # Pinned to a commit rather than a tag, which can be moved; the `# frozen:` # comment names the release, and Dependabot updates both. # - # `language: python` makes prek run that release's hooks. Without it, prek - # runs its own Rust versions of them, which differ on some inputs: check-yaml - # accepts unknown tags, check-toml accepts TOML 1.1, check-json accepts an - # empty file, and others. + # `language: python` makes prek run that release's hooks, on the Python set in + # default_language_version above. Without it, prek runs its own Rust versions + # of them, which differ on some inputs: check-yaml accepts unknown tags, + # check-toml accepts TOML 1.1, check-json accepts an empty file, and others. - repo: https://github.com/pre-commit/pre-commit-hooks rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: From e9bc2f211c8f6e4dec2aa25d77c05c7b9e859609 Mon Sep 17 00:00:00 2001 From: Zeev Manilovich Date: Sat, 3 Oct 2026 04:53:32 +0300 Subject: [PATCH 7/7] Restore the prek cache across uv.lock changes The cache key hashed uv.lock with no restore-keys, so every uv.lock change, including each Dependabot uv bump, started from an empty cache. The key now hashes the config and uv.lock separately, and restore-keys falls back to the newest cache for the same config; prek reuses the environments that still match and the new key is saved. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pre-commit.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index eb137c01..0836811b 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -33,16 +33,22 @@ jobs: python-version: "3.11" enable-cache: true - # Hook environments, keyed on the config that defines them, on uv.lock, - # which pins prek and the uv that builds them, and on the Python they were - # built with, since a hook venv does not survive an interpreter change. + # Hook environments, keyed on the Python they were built with, since a + # hook venv does not survive an interpreter change, on the config that + # defines them, and on uv.lock, which pins prek and the uv that builds + # them. When only uv.lock changed, the newest cache for the same config + # is restored: prek reuses the environments that still match and the + # result is saved under the new key. - name: Cache prek hook environments uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ env.PREK_HOME }} key: >- prek-${{ runner.os }}-py${{ steps.setup-uv.outputs.python-version }}-${{ - hashFiles('.pre-commit-config.yaml', 'uv.lock') }} + hashFiles('.pre-commit-config.yaml') }}-${{ hashFiles('uv.lock') }} + restore-keys: >- + prek-${{ runner.os }}-py${{ steps.setup-uv.outputs.python-version }}-${{ + hashFiles('.pre-commit-config.yaml') }}- # prek itself needs only the locked dev group. The ruff, mypy and typos # hooks are `repo: local` and run through `uv run --locked`, which syncs