Read Python version specifier from hook repo pyproject.toml#1596
Read Python version specifier from hook repo pyproject.toml#1596j178 merged 4 commits intoj178:masterfrom
pyproject.toml#1596Conversation
When a Python hook has no `language_version` and its entry isn't a PEP 723 script, prek creates a venv with `Any` — uv picks whatever Python is available, which may be too old for the hook's dependencies. Read `requires-python` from the hook repo's `pyproject.toml` as a fallback before defaulting to `Any`. Precedence: PEP 723 > user config > pyproject.toml > default (Any) Closes j178#1594
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1596 +/- ##
==========================================
- Coverage 91.66% 91.62% -0.04%
==========================================
Files 93 96 +3
Lines 18312 18637 +325
==========================================
+ Hits 16785 17077 +292
- Misses 1527 1560 +33 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
📦 Cargo Bloat ComparisonBinary size change: +0.42% (23.7 MiB → 23.8 MiB) Expand for cargo-bloat outputHead Branch ResultsBase Branch Results |
|
Reading |
I think handling transitive dependencies is ideal, but a higher bar; ultimately, I think it's responsible for the hook maintainer to report a proper version bound. I proposed a subsequent enhancement in the PR description inspired by uv's approach for e.g., Unfortunately, I don't think |
pyproject.tomlpyproject.toml
|
|
Currently, when a Python hook has no
language_versionand its entry isn't a PEP 723 script, prek creates a venv withAny. uv will pick the Python version according to its default version resolution, which may be too old for the hook's dependencies in certain cases.Now, in this case, prek will read
requires-pythonfrom the hook repo'spyproject.tomlas a fallback before defaulting toAny.Precedence: PEP 723 > user config > pyproject.toml > default (Any)
Closes #1594
This only handles an explicit version specifier, but not possible narrower transitive dependencies. As a followup enhancement, we might want to retry
uv pip installfailures by extracting Python constraints from the resolver error. Whenuv pip installfails due to a Python version incompatibility, the error contains the constraint (e.g., does not satisfy Python>=3.10). prek could parse this, recreate the venv with--python ">=3.10", and retry. This handles transitive deps, (likezizmor-pre-committoday, where the wrapper has norequires-pythonbut its depzizmorrequires>=3.10). The retry should be cheap; uv's HTTP cache means the second resolution is near instant since all package metadata was already fetched in the failed attempt.Indeed, this is what uv itself already does in e.g.
uvxoruv tool install. When initial resolution fails withNoSolution, uv callsrefine_interpreter()which extracts a Python bound from the resolver error, then re-resolves with that inferred bound. This handles transitive constraints without any pre-resolution step. The uv code notes this is heuristic and can miss edge cases, but prek could adopt the same pattern foruv pip installfailures.All that being said, I figured that should probably be a follow-up PR, and is not immediately necessary.