{"id":28146445,"url":"https://github.com/aspect-build/rules_py","last_synced_at":"2026-05-22T04:10:02.356Z","repository":{"id":37594991,"uuid":"466236063","full_name":"aspect-build/rules_py","owner":"aspect-build","description":"More compatible Bazel rules for running Python tools and building Python projects","archived":false,"fork":false,"pushed_at":"2025-07-24T20:35:35.000Z","size":13958,"stargazers_count":115,"open_issues_count":81,"forks_count":49,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-08-06T18:41:47.549Z","etag":null,"topics":["bazel","bazel-rules","python"],"latest_commit_sha":null,"homepage":"","language":"Starlark","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aspect-build.png","metadata":{"funding":{"github":null,"patreon":null,"open_collective":"aspect-build","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null},"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-03-04T18:49:25.000Z","updated_at":"2025-07-28T19:21:41.000Z","dependencies_parsed_at":"2024-04-13T23:02:50.647Z","dependency_job_id":"3aa07457-e136-4ab3-8504-5b5b751ed664","html_url":"https://github.com/aspect-build/rules_py","commit_stats":{"total_commits":239,"total_committers":22,"mean_commits":"10.863636363636363","dds":0.6192468619246863,"last_synced_commit":"581e662b421355da87c72e45558c08affe7f9762"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":"bazel-contrib/rules-template","purl":"pkg:github/aspect-build/rules_py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspect-build%2Frules_py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspect-build%2Frules_py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspect-build%2Frules_py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspect-build%2Frules_py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aspect-build","download_url":"https://codeload.github.com/aspect-build/rules_py/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspect-build%2Frules_py/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269834291,"owners_count":24482640,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bazel","bazel-rules","python"],"created_at":"2025-05-14T23:12:35.651Z","updated_at":"2026-05-22T04:10:02.335Z","avatar_url":"https://github.com/aspect-build.png","language":"Starlark","funding_links":["https://opencollective.com/aspect-build"],"categories":["Starlark"],"sub_categories":[],"readme":"# aspect_rules_py\n\n\u003e [!WARNING]\n\u003e **This is the 2.x ALPHA branch.** APIs and behavior may change without notice. For stable documentation, see the [1.x branch](https://github.com/aspect-build/rules_py/tree/1.x).\n\n`aspect_rules_py` is a high-performance alternative to [rules_python](https://github.com/bazelbuild/rules_python), the\nreference Python ruleset for Bazel.\n\nIt provides drop-in replacements for `py_binary`, `py_library`, and `py_test` that prioritize:\n\n- **Blazing-fast dependency resolution** via native `uv` integration\n- **Strict hermeticity** with isolated Python execution and Bash-based launchers\n- **Idiomatic Python layouts** using standard `site-packages` symlink trees\n- **Seamless IDE compatibility** via virtualenv-native structures\n- **Production-ready containers** with optimized OCI image layers\n\n`aspect_rules_py` optimizes for modern Python development workflows, large-scale monorepos, and Remote Build Execution (\nRBE) environments.\n\n## Advantages Over `rules_python`\n\n| Feature                   | rules_python                                                                                            | rules_py                                                                                                                 |\n|:--------------------------|:--------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------|\n| Dependency resolution     | `pip.parse` (repo rules, loading phase)                                                                 | Build-action wheel installs (`whl_install`)                                                                              |\n| uv integration            | `uv pip compile` → `requirements.txt` → `pip.parse`                                                     | Native `uv.lock` consumption                                                                                             |\n| Cross-platform lockfile   | `requirements.txt` (`uv.lock` via `uv pip compile`)                                                     | Native single `uv.lock` consumption                                                                                      |\n| sdist / PEP 517 builds    | Not supported ([#2410](https://github.com/bazel-contrib/rules_python/issues/2410), open since Nov 2024) | Build actions (`pep517_whl`, `pep517_native_whl`)                                                                        |\n| Interpreter provisioning  | Download via rules_python extension                                                                     | Own [python-build-standalone](https://github.com/astral-sh/python-build-standalone) extension — no rules_python required |\n| Site-packages layout      | Standard `site-packages` layout (flag-enabled)                                                          | Standard `site-packages` symlink tree                                                                                    |\n| Cross-compilation         | Limited                                                                                                 | Native platform transitions (e.g. arm64 image on amd64 host)                                                             |\n| Virtual dependencies      | No                                                                                                      | `virtual_deps` — swap implementations at binary level                                                                    |\n| PEP 735 dependency groups | No                                                                                                      | `--@pypi//dep_group=prod` flag                                                                                                |\n\n\u003e [!NOTE]\n\u003e **rules_python's uv support**: `rules_python`'s uv integration runs `uv pip compile` as a build action to\n\u003e generate a `requirements.txt`—it is a faster `pip-compile` replacement. The result still feeds into `pip.parse()` →\n\u003e `whl_library` repository rules at loading phase. There is no `uv.lock` consumption; the rules_python maintainer has\n\u003e [suggested](https://github.com/bazel-contrib/rules_python/discussions/3391) this work belongs in a dedicated project.\n\n### Native `uv.lock` Dependency Resolution\n\nInstead of relying on legacy `pip` machinery, we provide native integration with [uv](https://github.com/astral-sh/uv),\na Rust-native Python package resolver.\n\n- **Build-action installs**: Wheel extraction runs as Bazel execution-phase actions—not repo rules—so they are\n  sandboxed and compatible with RBE. Crucially, wheels are no longer resolved against the host machine\n  architecture: a single build can fetch and extract wheels for any exec or target platform, enabling true\n  cross-platform builds (e.g. building Linux `aarch64` wheels on a macOS `x86_64` host)\n- **Native `uv.lock` parsing**: Consumes `uv.lock` directly; no `requirements.txt` generation step\n- **Universal lockfiles**: A single `uv.lock` works across all platforms\n- **sdist / PEP 517 builds**: Build source distributions as Bazel actions (rules_python has no equivalent;\n  [#2410](https://github.com/bazel-contrib/rules_python/issues/2410) open since November 2024)\n- **PEP 735 dependency groups**: Define `prod`, `dev`, `test` dependency groups and switch between them with a flag\n- **Editable requirements**: Override locked packages with local `py_library` targets via `uv.override_package()`\n- **Lazy downloads**: Wheel installation happens during the build phase, not repository loading—fully compatible with private\n  mirrors and RBE\n\n### Own Python Interpreter Provisioning\n\n`aspect_rules_py` ships its own [python-build-standalone](https://github.com/astral-sh/python-build-standalone)\ninterpreter extension—rules_python is not required as a toolchain provider.\n\n\u003e [!NOTE]\n\u003e The `//py` and `//uv` extension paths provide stable APIs for interpreter provisioning\n\u003e and dependency resolution. They graduated from `//py/unstable` and `//uv/unstable` in rules_py v2.0.0.\n\n```python\ninterpreters = use_extension(\"@aspect_rules_py//py:extensions.bzl\", \"python_interpreters\")\ninterpreters.toolchain(python_version = \"3.12\", is_default = True)\nuse_repo(interpreters, \"python_interpreters\")\nregister_toolchains(\"@python_interpreters//:all\")\n```\n\nThis enables cross-compilation from any host to any target without host-installed Python, and is the foundation for\ncorrect toolchain selection in RBE environments.\n\n### Idiomatic `site-packages` Layout\n\nWe do not manipulate `sys.path` or `$PYTHONPATH`. Instead, we generate a standard `site-packages` directory structure\nusing symlink trees:\n\n- Prevents module name collisions (e.g., standard library `collections` vs. a transitive dependency named `collections`)\n- Matches standard Python expectations—tools just work\n- Native IDE compatibility: VSCode, PyCharm, and language servers resolve jump-to-definition correctly into the Bazel\n  sandbox\n\n### Strict Sandbox Isolation\n\n- **Isolated mode**: Python executes with `-I` flag, preventing implicit loading of user site-packages or host\n  environment variables\n- **Hermetic launchers**: Our launcher uses the Bazel Bash toolchain, not the host Python, this ensures 100% hermetic\n  execution across local machines and RBE nodes\n- **No host Python leakage**: Breaks the implicit dependency on system Python during the boot sequence\n\n### Cross-Platform \u0026 Cross-Build Native\n\n- **Effortless cross-compilation**: Build Linux container images from macOS (or vice versa) using standard Bazel\n  platform transitions\n- **Multi-architecture OCI images**: Native support for building `amd64` and `arm64` container images\n- **Platform-agnostic queries**: All hub labels are always available—no more \"target incompatible\" errors when querying\n  on a different OS\n\n### Virtual Dependencies for Monorepos\n\n`virtual_deps` allow external Python dependencies to be specified by package name rather than by label:\n\n- Individual projects within a monorepo can upgrade dependencies independently\n- Test against multiple versions of the same dependency\n- Swap implementations at the binary level (e.g., use `cowsnake` instead of `cowsay`)\n\n### Production Container Support\n\nBuilt-in rules for creating optimized container images:\n\n- [`py_image_layer`](py/defs.bzl): Creates layered tar files compatible with `rules_oci`\n- Cross-platform builds with automatic platform transitions\n- Optimized layer caching—dependencies and application code are separated\n\n### Native Pytest Integration\n\n- First-class pytest support with `py_pytest_main`\n- Automatic test discovery with proper import handling\n- Compatible with `pytest-mock`, `pytest-xdist`, and other plugins\n\n## Installation and Configuration\n\n```bzl\nbazel_dep(name = \"aspect_rules_py\", version = \"1.11.2\")\n```\n\n### Quick Start\n\nLoad rules from `aspect_rules_py` in your `BUILD` files:\n\n```starlark\nload(\"@aspect_rules_py//py:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\n\npy_library(\n    name = \"lib\",\n    srcs = [\"lib.py\"],\n    deps = [\"@pypi//requests\"],\n)\n\npy_binary(\n    name = \"app\",\n    srcs = [\"main.py\"],\n    main = \"main.py\",\n    deps = [\":lib\"],\n)\n\npy_test(\n    name = \"test\",\n    srcs = [\"test.py\"],\n    deps = [\":lib\"],\n)\n```\n\n## Dependency Resolution with `uv`\n\n`aspect_rules_py//uv` is our alternative to `rules_python`'s `pip.parse`:\n\n```bzl\nuv = use_extension(\"@aspect_rules_py//uv:extensions.bzl\", \"uv\")\n\n# 1. Declare a hub (a shared dependency namespace)\nuv.declare_hub(\n    hub_name = \"pypi\",\n)\n\n# 2. Register projects (lockfiles) into the hub\nuv.project(\n    hub_name = \"pypi\",\n    lock = \"//:uv.lock\",\n    pyproject = \"//:pyproject.toml\",\n    # Build tools injected for sdist packages that need them (e.g. maturin, setuptools)\n    default_build_dependencies = [\"build\", \"setuptools\"],\n)\n\n# 3a. (Optional) Replace a package with a local Bazel target\nuv.override_package(\n    name = \"some_package\",\n    lock = \"//:uv.lock\",\n    target = \"//third_party/some_package\",\n)\n\n# 3b. (Optional) Patch an installed wheel's file tree after unpacking\nuv.override_package(\n    name = \"some_other_package\",\n    lock = \"//:uv.lock\",\n    post_install_patches = [\"//third_party/patches:fix_some_other_package.patch\"],\n    post_install_patch_strip = 1,\n)\n\nuse_repo(uv, \"pypi\")\n```\n\nRequirements are declared in standard `pyproject.toml`:\n\n```toml\n[project]\nname = \"myapp\"\nversion = \"1.0.0\"\nrequires-python = \"\u003e= 3.11\"\ndependencies = [\n    \"requests\u003e=2.28\",\n    \"pydantic\u003e=2.0\",\n]\n\n[dependency-groups]\ndev = [\"pytest\", \"black\", \"mypy\"]\n```\n\nGenerate the lockfile with uv:\n\n```bash\nuv lock\n```\n\nSwitch between dependency groups:\n\n```bash\n# Default: use all dependencies\nbazel run //:app\n\n# Use only production dependencies\nbazel run //:app --@pypi//dep_group=prod\n```\n\n## Virtual Dependencies\n\nDeclare virtual dependencies in libraries:\n\n```starlark\npy_library(\n    name = \"greet_lib\",\n    srcs = [\"greet.py\"],\n    virtual_deps = [\"cowsay\"],  # Not a label—just a package name\n)\n```\n\nResolve them in binaries:\n\n```starlark\npy_binary(\n    name = \"app\",\n    srcs = [\"main.py\"],\n    deps = [\":greet_lib\"],\n    resolutions = {\n        \"cowsay\": \"@pypi//cowsay\",\n    },\n)\n\n# Or use a different implementation!\npy_binary(\n    name = \"app_snake\",\n    srcs = [\"main.py\"],\n    deps = [\":greet_lib\"],\n    resolutions = {\n        \"cowsay\": \"//cowsnake\",  # Swapped implementation\n    },\n)\n```\n\n## Container Images\n\nBuild optimized OCI images with layer caching:\n\n```starlark\nload(\"@aspect_rules_py//py:defs.bzl\", \"py_binary\", \"py_image_layer\")\nload(\"@rules_oci//oci:defs.bzl\", \"oci_image\", \"oci_load\")\n\npy_binary(\n    name = \"app_bin\",\n    srcs = [\"main.py\"],\n    deps = [\"//:lib\"],\n)\n\npy_image_layer(\n    name = \"app_layers\",\n    binary = \":app_bin\",\n)\n\noci_image(\n    name = \"image\",\n    base = \"@ubuntu\",\n    tars = [\":app_layers\"],\n    entrypoint = [\"/app/app_bin\"],\n)\n\noci_load(\n    name = \"image_load\",\n    image = \":image\",\n    repo_tags = [\"myapp:latest\"],\n)\n```\n\nCross-compile for Linux from macOS:\n\n```bash\nbazel build //:image --platforms=//platforms:linux_amd64\n```\n\n## IDE Integration\n\n`aspect_rules_py` generates standard virtualenv structures that IDEs understand:\n\n```bash\n# Creates a .venv symlink for the target\nbazel run //:my_app.venv\n```\n\nThen point your IDE to the generated virtualenv:\n\n- **VSCode**: Set `python.defaultInterpreterPath` to the `.venv` path\n- **PyCharm**: Add the `.venv` as a Python interpreter\n- **Neovim/LSP**: Configure `python-lsp-server` or `pyright` to use the virtualenv\n\n### Debugger Support (VSCode/PyCharm)\n\nAttach debuggers using `debugpy`:\n\n```starlark\n# In debug mode, wraps the binary with debugpy listener\npy_binary(\n    name = \"app_debug\",\n    srcs = [\"main.py\"],\n    deps = [\"//:lib\", \"@pypi//debugpy\"],\n    env = {\"DEBUGPY_WAIT\": \"1\"},  # Wait for IDE attachment\n)\n```\n\nVSCode `launch.json`:\n\n```json\n{\n  \"name\": \"Attach to Bazel py_binary\",\n  \"type\": \"debugpy\",\n  \"request\": \"attach\",\n  \"connect\": {\n    \"host\": \"127.0.0.1\",\n    \"port\": 5678\n  }\n}\n```\n\n## Gazelle Integration\n\nGenerate `BUILD` files automatically with the Gazelle extension:\n\n```bzl\n# MODULE.bazel\nbazel_dep(name = \"gazelle\", version = \"0.42.0\")\nbazel_dep(name = \"aspect_rules_py\", version = \"1.11.2\")\n\n# In your BUILD file\n# gazelle:map_kind py_library py_library @aspect_rules_py//py:defs.bzl\n# gazelle:map_kind py_binary py_binary @aspect_rules_py//py:defs.bzl\n# gazelle:map_kind py_test py_test @aspect_rules_py//py:defs.bzl\n```\n\n```bash\n# Generate BUILD files\nbazel run //:gazelle\n```\n\n\u003e [!NOTE]\n\u003e  When using `pytest_main=True`, you can create a [small wrapper macro](https://github.com/aspect-build/aspect-workflows-template/blob/main/%7B%7B%20.ProjectSnake%20%7D%7D/tools/pytest/defs.bzl) for `py_test` that presets `pytest_main=True` and automatically adds `pytest` as dep.\n\u003e Then you need to update ` gazelle:map_kind py_test py_test` to point to your wrapper rule.\n\n## Migration from `rules_python`\n\n`aspect_rules_py` is designed for incremental adoption:\n\n1. **Swap the rules**: Load `py_binary`, `py_library`, `py_test` from `@aspect_rules_py//py:defs.bzl` instead of\n   `@rules_python//python:defs.bzl`\n2. **Migrate dependencies**: Replace `pip.parse` with `uv.hub` and generate a `uv.lock`\n3. **Optionally migrate toolchains**: Replace `rules_python` interpreter provisioning with\n   the `aspect_rules_py` interpreter extension for fully independent hermetic interpreters\n\nFor detailed migration guidance, see [docs/migrating.md](docs/migrating.md).\n\n## Documentation\n\n- [Dependency resolution with `uv`](docs/uv.md)\n- [Virtual dependencies](docs/virtual_deps.md)\n- [Interpreter configuration](docs/interpreter.md)\n- [Migration guide](docs/migrating.md)\n- [Contributing](CONTRIBUTING.md)\n\n## Users\n\n- [OpenAI](https://github.com/openai/codex)\n- [Physical Intelligence](https://www.physicalintelligence.company/)\n- [RAI Institute](https://rai-inst.com/)\n- [NVIDIA OSMO](https://github.com/NVIDIA/OSMO)\n- [ZML](https://github.com/zml/zml)\n- [Eclipse SCORE](https://github.com/eclipse-score/score)\n- [Intrinsic](https://github.com/intrinsic-opensource/ros-central-registry)\n- [Enfabrica](https://github.com/enfabrica/enkit)\n- [ReSim AI](https://github.com/resim-ai/open-core)\n- [StackAV](https://github.com/stackav-oss/clockwork)\n- [Netherlands Cancer Institute](https://github.com/NKI-AI/direct)\n- [pyrovelocity](https://github.com/pyrovelocity/pyrovelocity)\n\n## Architecture\n\n| Layer          | Implementation         | Description                                                                          |\n|:---------------|:-----------------------|:-------------------------------------------------------------------------------------|\n| **Toolchains** | `@aspect_rules_py//py` | Own python-build-standalone interpreter provisioning; `@rules_python` optional       |\n| **Resolution** | `@aspect_rules_py//uv` | Fast, lockfile-backed dependency resolution with `uv`                                |\n| **Execution**  | `@aspect_rules_py//py` | Drop-in replacements for `py_binary`, `py_library`, `py_test` with sandbox isolation |\n| **Generation** | `aspect-gazelle`       | Pre-compiled Gazelle extension—no CGO toolchain required                             |\n\n## License\n\nApache 2.0 - see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspect-build%2Frules_py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faspect-build%2Frules_py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspect-build%2Frules_py/lists"}