fix: resolve ruff and mypy from the generator's own environment - #6
Merged
Conversation
Both tools are invoked at runtime, but they were looked up with shutil.which(), which searches PATH. That is the wrong place: uv tool install and pipx expose only the console scripts a distribution declares, so the copies installed alongside the generator are not on PATH and an unrelated system install wins instead. The bug reproduced in this very repo, where PATH resolved to ruff 0.15.10 / mypy 1.20.0 while the pinned environment held 0.15.15 / 2.1.0 -- generated code was being formatted and checked by versions nobody selected. Add tooling.py, which searches the environment the generator was installed into before anything ambient. Its first candidate is derived from this module's own location, because ruff arrived through the same install; sysconfig's default scheme cannot stand in for it, since under `pip install --user` that names the system bin while the generator and its ruff live under the user scheme, letting a too-old system ruff win. The remaining candidates are the active environment, the interpreter's own directory, the user scheme and the base prefix, with PATH last. ruff is resolved to a binary path rather than run as `python -m ruff`, which measured 4.4x slower per call (12.7ms -> 56.5ms) and is invoked twice per generated file. mypy runs as `python -m mypy`, a single call where the interpreter also determines what it resolves imports against. Promote mypy from the `check` extra to a core dependency, since --check is documented as a headline feature and the extra left it absent by default. The extra stays, now empty, so existing [check] installs keep resolving. --check gains --python-executable pointing at an activated virtualenv when that differs from the generator's own environment. Without it, moving mypy off PATH would have regressed standalone installs: the generator's environment has ruff and mypy but not unihttp or the serializer, so every generated import would fail to resolve. Environment roots are compared rather than interpreters, because venvs built from one base python share a bin/python symlink target and would otherwise look identical.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 29 30 +1
Lines 2557 2604 +47
=========================================
+ Hits 2557 2604 +47 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Both tools are invoked at runtime, but they were looked up with shutil.which(), which searches PATH. That is the wrong place: uv tool install and pipx expose only the console scripts a distribution declares, so the copies installed alongside the generator are not on PATH and an unrelated system install wins instead. The bug reproduced in this very repo, where PATH resolved to ruff 0.15.10 / mypy 1.20.0 while the pinned environment held 0.15.15 / 2.1.0 -- generated code was being formatted and checked by versions nobody selected.
Add tooling.py, which searches the environment the generator was installed into before anything ambient. Its first candidate is derived from this module's own location, because ruff arrived through the same install; sysconfig's default scheme cannot stand in for it, since under
pip install --userthat names the system bin while the generator and its ruff live under the user scheme, letting a too-old system ruff win. The remaining candidates are the active environment, the interpreter's own directory, the user scheme and the base prefix, with PATH last.ruff is resolved to a binary path rather than run as
python -m ruff, which measured 4.4x slower per call (12.7ms -> 56.5ms) and is invoked twice per generated file. mypy runs aspython -m mypy, a single call where the interpreter also determines what it resolves imports against.Promote mypy from the
checkextra to a core dependency, since --check is documented as a headline feature and the extra left it absent by default. The extra stays, now empty, so existing [check] installs keep resolving.--check gains --python-executable pointing at an activated virtualenv when that differs from the generator's own environment. Without it, moving mypy off PATH would have regressed standalone installs: the generator's environment has ruff and mypy but not unihttp or the serializer, so every generated import would fail to resolve. Environment roots are compared rather than interpreters, because venvs built from one base python share a bin/python symlink target and would otherwise look identical.