# Python development, build, lint, and format tasks ["python:dev"] description = "Install Python package in development mode (builds CLI binary)" depends = ["python:proto"] run = "uv sync --group dev && uv pip install ." ["python:build"] description = "Build Python wheel with CLI binary (native)" depends = ["python:proto"] run = "uv run maturin build --release" ["python:build:multiarch"] description = "Build Python wheels for Linux amd64/arm64 with buildx" depends = ["python:proto"] run = """ #!/usr/bin/env bash set -euo pipefail PLATFORMS=${DOCKER_PLATFORMS:-linux/amd64,linux/arm64} VERSION=${NAVIGATOR_VERSION:-} if [ -z "$VERSION" ]; then VERSION=$(uv run python build/scripts/release.py get-version --python) fi VERSION_ARGS=(--build-arg "NAVIGATOR_VERSION=${VERSION}") if ! docker buildx inspect multiarch >/dev/null 2>&1; then echo "Creating multi-platform buildx builder..." docker buildx create --name multiarch --use --bootstrap else docker buildx use multiarch fi mkdir -p target/wheels for platform in ${PLATFORMS//,/ }; do echo "Building wheel for ${platform}..." docker buildx build \ --builder multiarch \ --platform "${platform}" \ -f deploy/docker/Dockerfile.python-wheels \ --target wheels \ "${VERSION_ARGS[@]}" \ --output type=local,dest=target/wheels \ . done ls -la target/wheels/*.whl """ ["python:build:macos"] description = "Build Python wheel for macOS arm64" depends = ["python:proto"] run = """ #!/usr/bin/env bash set -euo pipefail if [ "$(uname -s)" != "Darwin" ]; then echo "Skipping macOS wheel build on non-macOS host" exit 0 fi # Avoid failures when RUSTC_WRAPPER is set to an unresolved `sccache` binary. if command -v sccache >/dev/null 2>&1; then export RUSTC_WRAPPER="$(command -v sccache)" else unset RUSTC_WRAPPER || true fi mkdir -p target/wheels rustup target add aarch64-apple-darwin uv run maturin build --release --target aarch64-apple-darwin --out target/wheels ls -la target/wheels/*.whl """ ["python:build:all"] description = "Build Python wheels for Linux and macOS" depends = ["python:build", "python:build:multiarch", "python:build:macos"] ["python:lint"] description = "Lint Python code with ruff" depends = ["python:proto"] env = { UV_NO_SYNC = "1" } run = "uv run ruff check {{vars.python_paths}}" ["python:fmt"] description = "Format Python code with ruff" run = "uv run ruff format {{vars.python_paths}}" ["python:typecheck"] description = "Type check Python code with ty" depends = ["python:proto"] run = "uv run ty check {{vars.python_paths}}" ["python:proto"] description = "Generate Python protobuf stubs from .proto files" env = { UV_NO_SYNC = "1" } run = """ #!/usr/bin/env bash set -euo pipefail uv run python -m grpc_tools.protoc \ -Iproto \ --python_out=python/navigator/_proto \ --pyi_out=python/navigator/_proto \ --grpc_python_out=python/navigator/_proto \ proto/inference.proto \ proto/navigator.proto \ proto/datamodel.proto \ proto/sandbox.proto # Fix absolute imports in generated stubs to use package-relative imports uv run python - <<'PY' from pathlib import Path replacements = { "python/navigator/_proto/inference_pb2_grpc.py": [ ("import inference_pb2", "from . import inference_pb2"), ], "python/navigator/_proto/navigator_pb2_grpc.py": [ ("import navigator_pb2", "from . import navigator_pb2"), ("import sandbox_pb2", "from . import sandbox_pb2"), ], "python/navigator/_proto/navigator_pb2.py": [ ("import datamodel_pb2", "from . import datamodel_pb2"), ("import sandbox_pb2", "from . import sandbox_pb2"), ], "python/navigator/_proto/datamodel_pb2.py": [ ("import sandbox_pb2", "from . import sandbox_pb2"), ], "python/navigator/_proto/datamodel_pb2_grpc.py": [ ("import datamodel_pb2", "from . import datamodel_pb2"), ], "python/navigator/_proto/sandbox_pb2_grpc.py": [ ("import sandbox_pb2", "from . import sandbox_pb2"), ], } for path, rules in replacements.items(): file_path = Path(path) text = file_path.read_text() for before, after in rules: text = text.replace(before, after) file_path.write_text(text) PY """