python: stand up uv workspace and rules_python pip.parse chain#121
Merged
Conversation
…tree Recovered from on-disk worktree after a CCD session-archive incident destroyed the main repo's .git/. Base: b3b6f6f.
Reconstructed BUILD.bazel + smoke_test.py from the gallant-tesla-a3f986 session transcript's Write tool_use payloads.
Salvage went via transcript and missed buildifier-style alphabetization of attrs in go_sdk.download, git_override, and python.toolchain. Semantically identical; clears the bazel-mod-tidy pre-commit hook.
pip.parse defaulted to host-only wheel resolution, so cross-targeted matrix jobs (linux_arm64, darwin_arm64) failed with "No matching wheel for current configuration\xe2\x80\x99s Python version and platform" on every @pypi// target. linux_x86_64 happened to pass because the GitHub runner is also x86_64. Enumerating target_platforms = ["linux_x86_64", "linux_aarch64", "osx_aarch64"] matches the build-and-test-per-target matrix in .github/workflows/ci.yml, so wheels are resolved for every cross-build.
Reverts the target_platforms enumeration in pip.parse (7ceeb98). That fix was the wrong direction \xe2\x80\x94 it turned on cross-build wheel resolution for @pypi, which contradicts the policy in .claude/CLAUDE.md "Python Purity Is Not Enforced" ("Python targets are built and tested on a host that matches the target platform"). The actual mismatch was in CI: the linux_arm64 matrix row ran on ubuntu-latest (x86_64) and reached arm64 only via BuildBuddy execution. Fine for pure-Go cross-build, but it makes Bazel analysis happen on an x86_64 host while targeting arm64 \xe2\x80\x94 cross-build by another name, and the trigger for pip.parse host-only wheel resolution to miss every non-host config. Switching to ubuntu-24.04-arm keeps host = target across every matrix row. @pypi resolves wheels for the host platform on each runner natively, no target_platforms needed. README CI description updated to match the new layout.
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.
Summary
Lands the dependency-plumbing half of the Python toolchain (plan §B1). After this PR, third-party Python deps flow
pyproject.toml → uv.lock → requirements_lock.txt → pip.parse → @pypi//...and apy_testthat imports a third-party package builds cleanly under Bazel.Ruff (B2), ty (B3), gazelle_python (B4), polyglot
check_modules.py(B5), and the broader devcontainer rewrite (B6) follow in later PRs per the agreed split.What's in here
pyproject.toml— uv workspace root. Not a deployable package (tool.uv.package = false).members = []for now: existingtools/subdirs are Go modules, so atools/*glob would error at lock time; members get added explicitly when real Python projects land. Carries a transientrequestsdep that backs the smoke target.uv.lock+requirements_lock.txt—uv locksource of truth and itsuv export --format requirements-txt --no-hashes --no-emit-projectprojection.rules_pythondoesn't readuv.locknatively, so the export is whatpip.parseconsumes.MODULE.bazel—pip.parse(hub_name = "pypi", python_version = "3.14", requirements_lock = "//:requirements_lock.txt")+use_repo(pip, "pypi"). Includes aDEFERRED:block explaining whyrules_python_gazelle_pluginis not wired (see below).meta/scripts/smoke_py/— transientpy_testimportingrequests, proving the end-to-end chain. Deleted in a follow-up once gazelle_python lands and the chain is re-verified with hand-authored BUILD files giving way to generated ones..pre-commit-config.yaml— newuv-lock-freshlocal hook re-runsuv lock+ the requirements export; modelled onbazel-mod-tidy..devcontainer/Dockerfile— pinneduv 0.9.30install (Renovate-tracked,COPY --from=ghcr.io/astral-sh/uv:0.9.30) so the lock hook anduv syncwork in-container. Mirrors the pattern in~/.dotfiles/.devcontainer/Dockerfile. The fuller devcontainer rewrite (ruff/ty/pre-commit viauv tool install, post-create changes, editor extensions) lands in B6.docs/future-considerations.md— new "Python BUILD Generation (gazelle_python)" entry explaining the deferral and naming the trigger that brings it back in.Why gazelle_python is deferred
The
rules_python_gazelle_plugingazelle binary depends transitively onsmacker/go-tree-sittervia CGO. That package does not compile under our pinned Go 1.26.4 SDK (upstreamundefined: Nodeerrors oniter.go). Upstream tracks this in bazel-contrib/rules_python#3416; the pure-Go binding replacement is the unmerged #3786. Until that ships in arules_python_gazelle_pluginrelease, PythonBUILD.bazelfiles are hand-authored — cheap while the footprint is small. Trigger to revisit is documented infuture-considerations.md.What is NOT in this PR
check_modules.py(B5) land their own gates in subsequent PRs. The smoke target rides the existingbazel test //...matrix.Test plan
bazel test //...green in devcontainer (11/11, including//meta/scripts/smoke_py:smoke_test).bazel run //:gazelle -- -mode=diffclean (Go gazelle unaffected).pre-commit run --all-filesclean in devcontainer.uv-lock-freshfires and passes whenpyproject.tomlis touched without lockfile updates (verified bypre-commit run uv-lock-fresh --files pyproject.toml).uv lock.