> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# @aspect_rules_py//py:defs.bzl

> Bazel rules_py public API: re-implemented py_binary, py_library, py_test, and py_pytest_main rules with hermetic Python toolchain version selection.

<Callout icon="book">
  Documentation for [@aspect\_rules\_py@v1.8.4](https://registry.bazel.build/modules/aspect_rules_py/1.8.4) -- <Icon icon="github" iconType="brands" /> [View source](https://github.com/aspect-build/rules_py/blob/v1.8.4/py/defs.bzl)
</Callout>

Re-implementations of [py\_binary](https://bazel.build/reference/be/python#py_binary)
and [py\_test](https://bazel.build/reference/be/python#py_test)

## Choosing the Python version

The `python_version` attribute must refer to a python toolchain version
which has been registered in the WORKSPACE or MODULE.bazel file.

When using WORKSPACE, this may look like this:

```python theme={null}
load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains")

python_register_toolchains(
    name = "python_toolchain_3_8",
    python_version = "3.8.12",
    # setting set_python_version_constraint makes it so that only matches py_* rule
    # which has this exact version set in the `python_version` attribute.
    set_python_version_constraint = True,
)

# It's important to register the default toolchain last it will match any py_* target.
python_register_toolchains(
    name = "python_toolchain",
    python_version = "3.9",
)
```

Configuring for MODULE.bazel may look like this:

```python theme={null}
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.8.12", is_default = False)
python.toolchain(python_version = "3.9", is_default = True)
```

## Rule: `py_pex_binary`

Build a pex executable from a py\_binary

**Kind:** Executable rule

### Attributes

<ParamField body="name" type="name" required>
  A unique name for this target.
</ParamField>

<ParamField body="binary" type="label" required>
  A py\_binary target
</ParamField>

<ParamField body="inject_env" type="dictionary: String → String" default={`{}`}>
  Environment variables to set when running the pex binary.
</ParamField>

<ParamField body="inherit_path" type="string" default={`""`}>
  Whether to inherit the `sys.path` (aka PYTHONPATH) of the environment that the binary runs in.

  Use `false` to not inherit `sys.path`; use `fallback` to inherit `sys.path` after packaged
  dependencies; and use `prefer` to inherit `sys.path` before packaged dependencies.
</ParamField>

<ParamField body="python_shebang" type="string" default={`#!/usr/bin/env python3`} />

<ParamField body="python_interpreter_constraints" type="list of strings" default={`["CPython=={major}.{minor}.*"]`}>
  Python interpreter versions this PEX binary is compatible with. A list of semver strings.
  The placeholder strings `{major}`, `{minor}`, `{patch}` can be used for gathering version
  information from the hermetic python toolchain.
</ParamField>

## Rule: `py_binary_rule`

Run a Python program under Bazel. Most users should use the [py\_binary macro](#py_binary) instead of loading this directly.

**Kind:** Executable rule

### Attributes

<ParamField body="name" type="name" required>
  A unique name for this target.
</ParamField>

<ParamField body="env" type="dictionary: String → String" default={`{}`}>
  Environment variables to set when running the binary.
</ParamField>

<ParamField body="main" type="label" default={`None`}>
  Script to execute with the Python interpreter.

  Must be a label pointing to a `.py` source file.
  If such a label is provided, it will be honored.

  If no label is provided AND there is only one `srcs` file, that `srcs` file will be used.

  If there are more than one `srcs`, a file matching `{name}.py` is searched for.
  This is for historical compatibility with the Bazel native `py_binary` and `rules_python`.
  Relying on this behavior is STRONGLY discouraged, may produce warnings and may
  be deprecated in the future.
</ParamField>

<ParamField body="venv" type="string" default={`""`}>
  The name of the Python virtual environment within which deps should be resolved.

  Part of the aspect\_rules\_py//uv system, has no effect in rules\_python's pip.
</ParamField>

<ParamField body="python_version" type="string" default={`""`}>
  Whether to build this target and its transitive deps for a specific python version.
</ParamField>

<ParamField body="package_collisions" type="string" default={`error`}>
  The action that should be taken when a symlink collision is encountered when creating the venv.
  A collision can occur when multiple packages providing the same file are installed into the venv. The possible values are:

  * "error": When conflicting symlinks are found, an error is reported and venv creation halts.
  * "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues.
  * "ignore": When conflicting symlinks are found, no message is reported and venv creation continues.
</ParamField>

<ParamField body="interpreter_options" type="list of strings" default={`[]`}>
  Additional options to pass to the Python interpreter in addition to -B and -I passed by rules\_py
</ParamField>

<ParamField body="srcs" type="list of labels" default={`[]`}>
  Python source files.
</ParamField>

<ParamField body="deps" type="list of labels" default={`[]`}>
  Targets that produce Python code, commonly `py_library` rules.
</ParamField>

<ParamField body="data" type="list of labels" default={`[]`}>
  Runtime dependencies of the program.

  The transitive closure of the `data` dependencies will be available in the `.runfiles`
  folder for this binary/test. The program may optionally use the Runfiles lookup library to
  locate the data files, see [https://pypi.org/project/bazel-runfiles/](https://pypi.org/project/bazel-runfiles/).
</ParamField>

<ParamField body="imports" type="list of strings" default={`[]`}>
  List of import directories to be added to the PYTHONPATH.
</ParamField>

<ParamField body="resolutions" type="dictionary: Label → String" default={`{}`}>
  Satisfy a virtual\_dep with a mapping from external package name to the label of an installed package that provides it.
  See virtual\_deps.
</ParamField>

## Rule: `py_test_rule`

Run a Python program under Bazel. Most users should use the [py\_test macro](#py_test) instead of loading this directly.

**Kind:** Test rule

### Attributes

<ParamField body="name" type="name" required>
  A unique name for this target.
</ParamField>

<ParamField body="env" type="dictionary: String → String" default={`{}`}>
  Environment variables to set when running the binary.
</ParamField>

<ParamField body="main" type="label" default={`None`}>
  Script to execute with the Python interpreter.

  Must be a label pointing to a `.py` source file.
  If such a label is provided, it will be honored.

  If no label is provided AND there is only one `srcs` file, that `srcs` file will be used.

  If there are more than one `srcs`, a file matching `{name}.py` is searched for.
  This is for historical compatibility with the Bazel native `py_binary` and `rules_python`.
  Relying on this behavior is STRONGLY discouraged, may produce warnings and may
  be deprecated in the future.
</ParamField>

<ParamField body="venv" type="string" default={`""`}>
  The name of the Python virtual environment within which deps should be resolved.

  Part of the aspect\_rules\_py//uv system, has no effect in rules\_python's pip.
</ParamField>

<ParamField body="python_version" type="string" default={`""`}>
  Whether to build this target and its transitive deps for a specific python version.
</ParamField>

<ParamField body="package_collisions" type="string" default={`error`}>
  The action that should be taken when a symlink collision is encountered when creating the venv.
  A collision can occur when multiple packages providing the same file are installed into the venv. The possible values are:

  * "error": When conflicting symlinks are found, an error is reported and venv creation halts.
  * "warning": When conflicting symlinks are found, an warning is reported, however venv creation continues.
  * "ignore": When conflicting symlinks are found, no message is reported and venv creation continues.
</ParamField>

<ParamField body="interpreter_options" type="list of strings" default={`[]`}>
  Additional options to pass to the Python interpreter in addition to -B and -I passed by rules\_py
</ParamField>

<ParamField body="srcs" type="list of labels" default={`[]`}>
  Python source files.
</ParamField>

<ParamField body="deps" type="list of labels" default={`[]`}>
  Targets that produce Python code, commonly `py_library` rules.
</ParamField>

<ParamField body="data" type="list of labels" default={`[]`}>
  Runtime dependencies of the program.

  The transitive closure of the `data` dependencies will be available in the `.runfiles`
  folder for this binary/test. The program may optionally use the Runfiles lookup library to
  locate the data files, see [https://pypi.org/project/bazel-runfiles/](https://pypi.org/project/bazel-runfiles/).
</ParamField>

<ParamField body="imports" type="list of strings" default={`[]`}>
  List of import directories to be added to the PYTHONPATH.
</ParamField>

<ParamField body="resolutions" type="dictionary: Label → String" default={`{}`}>
  Satisfy a virtual\_dep with a mapping from external package name to the label of an installed package that provides it.
  See virtual\_deps.
</ParamField>

<ParamField body="env_inherit" type="list of strings" default={`[]`}>
  Specifies additional environment variables to inherit from the external environment when the test is executed by bazel test.
</ParamField>

## Rule: `py_library`

### Attributes

<ParamField body="name" type="name" required>
  A unique name for this target.
</ParamField>

<ParamField body="virtual_deps" type="list of strings" default={`[]`} />

<ParamField body="srcs" type="list of labels" default={`[]`}>
  Python source files.
</ParamField>

<ParamField body="deps" type="list of labels" default={`[]`}>
  Targets that produce Python code, commonly `py_library` rules.
</ParamField>

<ParamField body="data" type="list of labels" default={`[]`}>
  Runtime dependencies of the program.

  The transitive closure of the `data` dependencies will be available in the `.runfiles`
  folder for this binary/test. The program may optionally use the Runfiles lookup library to
  locate the data files, see [https://pypi.org/project/bazel-runfiles/](https://pypi.org/project/bazel-runfiles/).
</ParamField>

<ParamField body="imports" type="list of strings" default={`[]`}>
  List of import directories to be added to the PYTHONPATH.
</ParamField>

<ParamField body="resolutions" type="dictionary: Label → String" default={`{}`}>
  Satisfy a virtual\_dep with a mapping from external package name to the label of an installed package that provides it.
  See virtual\_deps.
</ParamField>

## Rule: `py_unpacked_wheel`

### Attributes

<ParamField body="name" type="name" required>
  A unique name for this target.
</ParamField>

<ParamField body="src" type="label" required>
  The Wheel file, as defined by [https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format](https://packaging.python.org/en/latest/specifications/binary-distribution-format/#binary-distribution-format)
</ParamField>

## Function: `py_pytest_main`

py\_pytest\_main wraps the template rendering target and the final py\_library.

### Parameters

<ParamField body="name" type="name" required>
  The name of the runable target that updates the test entry file.
</ParamField>

<ParamField body="py_library" type="name" default={`<rule py_library>`}>
  Use this attribute to override the default py\_library rule.
</ParamField>

<ParamField body="deps" type="name" default={`[]`}>
  A list containing the pytest library target, e.g., @pypi\_pytest//:pkg.
</ParamField>

<ParamField body="data" type="name" default={`[]`}>
  A list of data dependencies to pass to the py\_library target.
</ParamField>

<ParamField body="testonly" type="name" default={`True`}>
  A boolean indicating if the py\_library target is testonly.
</ParamField>

<ParamField body="kwargs" type="string_list">
  The extra arguments passed to the template rendering target.
</ParamField>

## Function: `py_venv`

Build a Python virtual environment and produce a script to link it into the build directory.

### Parameters

<ParamField body="venv_name" type="name" default={`None`} />

<ParamField body="srcs" type="name" default={`[]`} />

<ParamField body="kwargs" type="string_list" />

## Function: `py_venv_link`

Build a Python virtual environment and produce a script to link it into the build directory.

### Parameters

<ParamField body="venv_name" type="name" default={`None`} />

<ParamField body="srcs" type="name" default={`[]`} />

<ParamField body="kwargs" type="string_list" />

## Function: `py_image_layer`

Produce a separate tar output for each layer of a python app

> Requires `awk` to be installed on the host machine/rbe runner.

For better performance, it is recommended to split the output of a py\_binary into multiple layers.
This can be done by grouping files into layers based on their path by using the `layer_groups` attribute.

The matching order for layer groups is as follows:

1. `layer_groups` are checked first.
2. If no match is found for `layer_groups`, the `default layer groups` are checked.
3. Any remaining files are placed into the default layer.

The default layer groups are:

```
{
    "packages": "\.runfiles/.*/site-packages",, # contains third-party deps
    "interpreter": "\.runfiles/python.*-.*/", # contains the python interpreter
}
```

### Parameters

<ParamField body="name" type="name" required>
  base name for targets
</ParamField>

<ParamField body="binary" type="name" required>
  a py\_binary target
</ParamField>

<ParamField body="root" type="name" default={`/`}>
  Path to where the layers should be rooted. If not specified, the layers will be rooted at the workspace root.
</ParamField>

<ParamField body="layer_groups" type="name" default={`{}`}>
  Additional layer groups to create. They are used to group files into layers based on their path. In the form of: `{"<name>": "regex_to_match_against_file_paths"}`
</ParamField>

<ParamField body="compress" type="name" default={`gzip`}>
  Compression algorithm to use. Default is gzip. See: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar\_rule-compress](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar_rule-compress)
</ParamField>

<ParamField body="tar_args" type="name" default={`[]`}>
  Additional arguments to pass to the tar rule. Default is `[]`. See: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar\_rule-args](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar_rule-args)
</ParamField>

<ParamField body="compute_unused_inputs" type="name" default={`1`}>
  Whether to compute unused inputs. Default is 1. See: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar\_rule-compute\_unused\_inputs](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#tar_rule-compute_unused_inputs)
</ParamField>

<ParamField body="platform" type="name" default={`None`}>
  The platform to use for the transition. Default is None. See: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/transitions.md#platform\_transition\_binary-target\_platform](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/transitions.md#platform_transition_binary-target_platform)
</ParamField>

<ParamField body="owner" type="name" default={`None`}>
  An owner uid for the uncompressed files. See mtree\_mutate: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#mutating-the-tar-contents](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#mutating-the-tar-contents)
</ParamField>

<ParamField body="group" type="name" default={`None`}>
  A group uid for the uncompressed files. See mtree\_mutate: [https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#mutating-the-tar-contents](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/tar.md#mutating-the-tar-contents)
</ParamField>

<ParamField body="kwargs" type="string_list">
  attribute that apply to all targets expanded by the macro
</ParamField>

**Returns:** A list of labels for each layer.

## Function: `resolutions.empty`

## Function: `resolutions.from_requirements`

### Parameters

<ParamField body="base" type="name" required />

<ParamField body="requirement_fn" type="name" default={`<function lambda from //py/private:virtual.bzl>`} />

## Function: `py_binary`

Wrapper macro for [`py_binary_rule`](#py_binary_rule).

Creates a [py\_venv](#function-py_venv) target to constrain the interpreter and packages used at runtime.
Users can `bazel run [name].venv` to create this virtualenv, then use it in the editor or other tools.

### Parameters

<ParamField body="name" type="name" required>
  Name of the rule.
</ParamField>

<ParamField body="srcs" type="name" default={`[]`}>
  Python source files.
</ParamField>

<ParamField body="main" type="name" default={`None`}>
  Entry point.
  Like rules\_python, this is treated as a suffix of a file that should appear among the srcs.
  If absent, then `[name].py` is tried. As a final fallback, if the srcs has a single file,
  that is used as the main.
</ParamField>

<ParamField body="kwargs" type="string_list">
  additional named parameters to `py_binary_rule`.
</ParamField>

## Function: `py_test`

Identical to [py\_binary](#function-py_binary), but produces a target that can be used with `bazel test`.

### Parameters

<ParamField body="name" type="name" required>
  Name of the rule.
</ParamField>

<ParamField body="srcs" type="name" default={`[]`}>
  Python source files.
</ParamField>

<ParamField body="main" type="name" default={`None`}>
  Entry point.
  Like rules\_python, this is treated as a suffix of a file that should appear among the srcs.
  If absent, then `[name].py` is tried. As a final fallback, if the srcs has a single file,
  that is used as the main.
</ParamField>

<ParamField body="pytest_main" type="name" default={`False`}>
  If set, generate a [py\_pytest\_main](#py_pytest_main) script and use it as the main.
  The deps should include the pytest package (as well as the coverage package if desired).
</ParamField>

<ParamField body="kwargs" type="string_list">
  additional named parameters to `py_binary_rule`.
</ParamField>
