Switch to uv for all of our Python scripts (#7242)

This removes the need to install any specific version of Python or
figure out how to configure it by instead asking users to install `uv`
and letting it manage Python. Among other advantages, `uv` is designed
to be fast enough to embed directly into our scripts.

We were already using this in `bench_runner.py` so that the script could
import non standard library dependencies. Moving to it for the rest of
our Python unifies the approach and will also enable dependencies
whenever needed.

I've left `github_tools` alone as it has special handling with its own
Bazel setup.

I've updated the contributing tools to explain the approach here.
This commit is contained in:
Chandler Carruth
2026-05-28 18:03:52 +00:00
committed by GitHub
parent 6aee1280e9
commit b79ce84f33
32 changed files with 193 additions and 75 deletions
@@ -11,13 +11,12 @@ inputs:
runs:
using: composite
steps:
# Setup Python and related tools.
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
# Setup Python and related tools with uv.
- name: Set up uv and Python
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
# Match the min version listed in docs/project/contribution_tools.md
# or the oldest version available on the OS.
python-version:
${{ inputs.matrix_runner == 'macos-14' && '3.11' || '3.10' }}
enable-cache: true
version: '0.11.15'
- uses: ./.github/actions/build-setup-macos
if: startsWith(inputs.matrix_runner, 'macos')
@@ -38,9 +37,10 @@ runs:
bazelisk --version
echo '*** run_bazel.py'
./scripts/run_bazel.py --version
echo '*** python'
which python
python --version
echo '*** uv'
which uv
uv --version
uv python list --only-installed
echo '*** clang'
which clang
clang --version
+8 -6
View File
@@ -27,23 +27,25 @@ jobs:
egress-policy: block
allowed-endpoints: >
api.github.com:443 github.com:443 pypi.org:443
files.pythonhosted.org:443
files.pythonhosted.org:443 raw.githubusercontent.com:443
# Note: pull_request_target checks out the base branch by default.
# This is safe as it avoids running untrusted code from the PR branch.
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install dependencies
run: |
python3 -m pip install gql==2.0.0 requests
- name: Set up uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: true
version: '0.11.15'
- name: Check Dependent PR
run: |
if [ "$EVENT_ACTION" = "closed" ]; then
python3 github_tools/check_dependent_pr.py --scan
./github_tools/check_dependent_pr.py --scan
else
python3 github_tools/check_dependent_pr.py --pr-number "${PR_NUMBER}"
./github_tools/check_dependent_pr.py --pr-number "${PR_NUMBER}"
fi
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -5
View File
@@ -48,6 +48,7 @@ jobs:
oauth2.googleapis.com:443
objects.githubusercontent.com:443
pypi.org:443
raw.githubusercontent.com:443
registry.npmjs.org:443
release-assets.githubusercontent.com:443
releases.bazel.build:443
@@ -75,13 +76,9 @@ jobs:
if: steps.filter.outputs.has_cpp == 'true'
run: ./scripts/create_compdb.py
- name: Install clangd-tidy
if: steps.filter.outputs.has_cpp == 'true'
run: pip install clangd-tidy==1.1.0.post2
- name: Run clangd-tidy
if: steps.filter.outputs.has_cpp == 'true'
env:
FILTER_FILES: ${{ steps.filter.outputs.has_cpp_files }}
run: |
clangd-tidy -p . -j 10 $FILTER_FILES
uvx --with clangd-tidy==1.1.0.post2 clangd-tidy -p . -j 10 $FILTER_FILES
+7
View File
@@ -28,6 +28,13 @@ jobs:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: true
version: '0.11.15'
- name: Prebuild actions
run: ./website/prebuild.py
- name: Setup Ruby
+7
View File
@@ -35,6 +35,13 @@ jobs:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: true
version: '0.11.15'
- name: Prebuild actions
run: ./website/prebuild.py
- name: Setup Pages
+1
View File
@@ -59,6 +59,7 @@ jobs:
oauth2.googleapis.com:443
objects.githubusercontent.com:443
pypi.org:443
raw.githubusercontent.com:443
registry.npmjs.org:443
release-assets.githubusercontent.com:443
releases.bazel.build:443
+1 -1
View File
@@ -40,6 +40,7 @@ jobs:
oauth2.googleapis.com:443
objects.githubusercontent.com:443
pypi.org:443
raw.githubusercontent.com:443
registry.npmjs.org:443
release-assets.githubusercontent.com:443
releases.bazel.build:443
@@ -48,7 +49,6 @@ jobs:
www.googleapis.com:443
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
# Ensure LLVM is set up consistently.
- uses: ./.github/actions/build-setup-common
+1
View File
@@ -70,6 +70,7 @@ jobs:
oauth2.googleapis.com:443
objects.githubusercontent.com:443
pypi.org:443
raw.githubusercontent.com:443
registry.npmjs.org:443
release-assets.githubusercontent.com:443
releases.bazel.build:443
+2 -1
View File
@@ -14,7 +14,8 @@
/examples/**/bazel-*
/examples/**/MODULE.bazel.lock
# Directories created by python.
# Files and directories created by python.
uv.lock
**/__pycache__/
# Ignore the user's VSCode settings and debug setup.
+10 -1
View File
@@ -1,4 +1,13 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
# NOTE: The `uv` shebang and inline metadata above are only used for direct
# execution of this script outside of Bazel. When executed by Bazel (e.g., as a
# tool in a rule or as a test), Bazel uses its own hermetic Python toolchain
# and ignores this metadata.
"""Generate a file from a template, substituting the provided key/value pairs.
+14 -17
View File
@@ -59,11 +59,10 @@ sudo apt install \
libc++abi-dev \
lld \
lldb \
python3 \
pipx
pre-commit
# Install pre-commit.
pipx install pre-commit
# Install `uv` for Python scripts.
curl -LsSf https://astral.sh/uv/install.sh | sh
# Set up git.
# If you don't already have a fork:
@@ -123,7 +122,7 @@ brew install \
bazelisk \
gh \
llvm \
python@3.10 \
uv \
pre-commit
# IMPORTANT: Make sure `llvm` is added to the PATH! It's separate from `brew`.
@@ -161,13 +160,16 @@ These tools are essential for work on Carbon.
- [Homebrew](https://brew.sh/) (for macOS)
- To upgrade versions of `brew` packages, it will be necessary to
periodically run `brew upgrade`.
- [Python](https://python.org)
- Carbon requires Python 3.10 or newer.
- To upgrade versions of pip-installed packages, it will be necessary
to periodically run `pipx list --outdated`, then
`pipx install -U <package>` to upgrade desired packages.
- When upgrading, version dependencies may mean packages _should_ be
outdated, and not be upgraded.
- [Python](https://python.org) using [`uv`](https://docs.astral.sh/uv/)
- Carbon uses `uv` to run Python scripts directly, ensuring automatic
dependency management.
- Standalone scripts (for example, in `scripts/`) have dependencies
embedded in the file using PEP 723 inline metadata.
- To run a script directly, ensure `uv` is installed and the script
should be runnable directly (for example,
`./scripts/create_compdb.py`).
- Installation:
https://docs.astral.sh/uv/getting-started/installation/
- Main tools
- [Bazel](https://www.bazel.build/)
- [Bazelisk](https://docs.bazel.build/versions/master/install-bazelisk.html):
@@ -244,11 +246,6 @@ considering if they fit your workflow.
- **NOTE**: This assumes you have `python` 3 installed on your system.
- [`uv`](https://docs.astral.sh/uv/): A fast Python package manager.
- Notably, `uv` supports automatic management of even complex Python
dependencies for scripts: https://docs.astral.sh/uv/guides/scripts/
- Installation: https://docs.astral.sh/uv/getting-started/installation/
#### Jujutsu (`jj`)
[Jujutsu](https://github.com/jj-vcs/jj) is a Git-compatible version control
+14 -14
View File
@@ -1,14 +1,24 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "gql==2.0.0",
# "requests",
# "types-requests",
# ]
# ///
"""Check if a PR depends on other open PRs based on shared commits.
Usage examples:
# Check a specific PR in dry-run mode:
GITHUB_ACCESS_TOKEN=$(gh auth token) \
python3 github_tools/check_dependent_pr.py --pr-number <PR_NUMBER> --dry-run
./github_tools/check_dependent_pr.py --pr-number <PR_NUMBER> --dry-run
# Scan all dependent PRs in dry-run mode:
GITHUB_ACCESS_TOKEN=$(gh auth token) \
python3 github_tools/check_dependent_pr.py --scan --dry-run
./github_tools/check_dependent_pr.py --scan --dry-run
"""
__copyright__ = """
@@ -19,26 +29,16 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
import argparse
import datetime
import importlib.util
import json
import re
import os
import sys
import requests
from typing import Any, Optional
# Do some extra work to support direct runs.
try:
from github_tools import github_helpers
except ImportError:
github_helpers_spec = importlib.util.spec_from_file_location(
"github_helpers",
os.path.join(os.path.dirname(__file__), "github_helpers.py"),
)
assert github_helpers_spec is not None
github_helpers = importlib.util.module_from_spec(github_helpers_spec)
github_helpers_spec.loader.exec_module(github_helpers) # type: ignore
import github_helpers # type: ignore
# Queries
_QUERY_OPEN_PRS = """
+5 -1
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Prepares a new proposal file and PR."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Run `bazel mod deps` for pre-commit. Lets pre-commit handle modifications."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Prints sha information for tracked tool releases."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Verify that the bazel build graph is in a valid state, for pre-commit."""
+5 -1
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Checks for missing or incorrect header guards."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Requires files be named for their SHA1.
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Create a compilation database for Clang tools like `clangd`.
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Automatically fixes bazel C++ dependencies.
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Detects and prevents dependencies on LLVM's googletest.
+2 -2
View File
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""No-op test that should always pass.
This is designed to have the fewest avoidable dependencies to make no-op build
and test runs in CI as inexpensive as possible.
Note that this is not designed for direct execution and is only for Bazel's use.
"""
__copyright__ = """
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Queries latest module versions from MODULE.bazel."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Runs bazel on arguments.
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Runs bazelisk with arbitrary arguments."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Runs buildifier on passed-in BUILD files, mainly for pre-commit."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Runs buildozer on arguments.
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Script to compute statistics about source code."""
+6 -1
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Computes the potentially differing rules from some git commit.
+11 -1
View File
@@ -1,4 +1,14 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
# NOTE: The `uv` shebang and inline metadata above are only used for direct
# execution of this script outside of Bazel. When executed by Bazel (e.g., as a
# workspace_status_command), Bazel uses its own hermetic Python toolchain
# and ignores this metadata.
"""Bazel `--workspace_status_command` script.
+5 -1
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Autoupdates testdata in toolchain."""
+5 -1
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
"""Updates files in preparation for a jekyll build.