Skip to content

python: stand up uv workspace and rules_python pip.parse chain#121

Merged
Syndic merged 5 commits into
mainfrom
python-tooling-bootstrap
Jun 15, 2026
Merged

python: stand up uv workspace and rules_python pip.parse chain#121
Syndic merged 5 commits into
mainfrom
python-tooling-bootstrap

Conversation

@Syndic

@Syndic Syndic commented Jun 14, 2026

Copy link
Copy Markdown
Owner

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 a py_test that 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: existing tools/ subdirs are Go modules, so a tools/* glob would error at lock time; members get added explicitly when real Python projects land. Carries a transient requests dep that backs the smoke target.
  • uv.lock + requirements_lock.txtuv lock source of truth and its uv export --format requirements-txt --no-hashes --no-emit-project projection. rules_python doesn't read uv.lock natively, so the export is what pip.parse consumes.
  • MODULE.bazelpip.parse(hub_name = "pypi", python_version = "3.14", requirements_lock = "//:requirements_lock.txt") + use_repo(pip, "pypi"). Includes a DEFERRED: block explaining why rules_python_gazelle_plugin is not wired (see below).
  • meta/scripts/smoke_py/ — transient py_test importing requests, 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 — new uv-lock-fresh local hook re-runs uv lock + the requirements export; modelled on bazel-mod-tidy.
  • .devcontainer/Dockerfile — pinned uv 0.9.30 install (Renovate-tracked, COPY --from=ghcr.io/astral-sh/uv:0.9.30) so the lock hook and uv sync work in-container. Mirrors the pattern in ~/.dotfiles/.devcontainer/Dockerfile. The fuller devcontainer rewrite (ruff/ty/pre-commit via uv 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_plugin gazelle binary depends transitively on smacker/go-tree-sitter via CGO. That package does not compile under our pinned Go 1.26.4 SDK (upstream undefined: Node errors on iter.go). Upstream tracks this in bazel-contrib/rules_python#3416; the pure-Go binding replacement is the unmerged #3786. Until that ships in a rules_python_gazelle_plugin release, Python BUILD.bazel files are hand-authored — cheap while the footprint is small. Trigger to revisit is documented in future-considerations.md.

What is NOT in this PR

  • No CI jobs yet — ruff (B2), ty (B3), and the polyglot check_modules.py (B5) land their own gates in subsequent PRs. The smoke target rides the existing bazel test //... matrix.
  • No README "checks" table changes — those land alongside each new CI gate.

Test plan

  • bazel test //... green in devcontainer (11/11, including //meta/scripts/smoke_py:smoke_test).
  • bazel run //:gazelle -- -mode=diff clean (Go gazelle unaffected).
  • pre-commit run --all-files clean in devcontainer.
  • uv-lock-fresh fires and passes when pyproject.toml is touched without lockfile updates (verified by pre-commit run uv-lock-fresh --files pyproject.toml).
  • Lockfile idempotent across host + in-container uv lock.
  • CI passes on the PR.

Syndic added 5 commits June 13, 2026 20:42
…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.
@Syndic Syndic enabled auto-merge (squash) June 15, 2026 00:05
@Syndic Syndic merged commit 19381d4 into main Jun 15, 2026
34 checks passed
@Syndic Syndic deleted the python-tooling-bootstrap branch June 15, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant