# 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 # 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 else echo "Building macOS wheel via Docker cross-toolchain" mise run python:build:macos:docker fi ls -la target/wheels/*.whl """ ["python:build:macos:docker"] description = "Build Python wheel for macOS arm64 from Docker" depends = ["python:proto"] run = """ #!/usr/bin/env bash set -euo pipefail VERSION=${NAVIGATOR_VERSION:-} OSXCROSS_IMAGE_REF=${OSXCROSS_IMAGE:-crazymax/osxcross:latest} if [ -z "$VERSION" ]; then VERSION=$(uv run python build/scripts/release.py get-version --python) fi mkdir -p target/wheels docker build \ -f deploy/docker/Dockerfile.python-wheels-macos \ --target wheels \ --build-arg "OSXCROSS_IMAGE=${OSXCROSS_IMAGE_REF}" \ --build-arg "NAVIGATOR_VERSION=${VERSION}" \ --output type=local,dest=target/wheels \ . ls -la target/wheels/*macosx*arm64.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:format"] 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 import re line_rewrites = { "python/navigator/_proto/inference_pb2_grpc.py": [ (r"^import inference_pb2 as inference__pb2$", "from . import inference_pb2 as inference__pb2"), ], "python/navigator/_proto/navigator_pb2_grpc.py": [ (r"^import navigator_pb2 as navigator__pb2$", "from . import navigator_pb2 as navigator__pb2"), (r"^import sandbox_pb2 as sandbox__pb2$", "from . import sandbox_pb2 as sandbox__pb2"), ], "python/navigator/_proto/navigator_pb2.py": [ (r"^import datamodel_pb2 as datamodel__pb2$", "from . import datamodel_pb2 as datamodel__pb2"), (r"^import sandbox_pb2 as sandbox__pb2$", "from . import sandbox_pb2 as sandbox__pb2"), ], "python/navigator/_proto/datamodel_pb2.py": [ (r"^import sandbox_pb2 as sandbox__pb2$", "from . import sandbox_pb2 as sandbox__pb2"), ], "python/navigator/_proto/datamodel_pb2_grpc.py": [ (r"^import datamodel_pb2 as datamodel__pb2$", "from . import datamodel_pb2 as datamodel__pb2"), ], "python/navigator/_proto/sandbox_pb2_grpc.py": [ (r"^import sandbox_pb2 as sandbox__pb2$", "from . import sandbox_pb2 as sandbox__pb2"), ], } for path, rules in line_rewrites.items(): file_path = Path(path) text = file_path.read_text() text = text.replace("from . from . import", "from . import") for pattern, replacement in rules: text = re.sub(pattern, replacement, text, flags=re.MULTILINE) file_path.write_text(text) PY """