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

# Python

> Build, run, and test Python applications with hermetic toolchains and PyPI dependencies.

`rbs` builds Python projects with a fully hermetic toolchain: it downloads a standalone
CPython interpreter, resolves your PyPI dependencies into a shared cache, and produces
runnable targets that bundle everything they need. No system Python, no virtualenvs,
no `pip install` steps.

## Set up the toolchain

Declare the Python toolchain once in your workspace's `WORKSPACE.rbs`. It downloads a
standalone CPython build for your platform and registers it automatically:

```python theme={null}
load("@rbs//python/toolchain.rbs", "python_toolchain")

python_toolchain(name = "python3", version = "3.12")
```

Supported versions are `3.11`, `3.12`, and `3.13` (patch versions such as `3.12.7` are
accepted and normalized). Keep the toolchain name `python3` — the Python rules look it
up by that name. When you cross-compile — for example building a Linux container image
from macOS — a host interpreter is also downloaded so build-time tools run natively.

## Declare pip dependencies

External packages come from PyPI. Declare each one in `WORKSPACE.rbs` with
`py_repository`:

```python theme={null}
load("@rbs//python/dependencies.rbs", "py_repository")

py_repository(
    name = "django_repo",
    package = "django",
    version = "5.0.1",
)

py_repository(
    name = "requests_repo",
    package = "requests",
    version = "2.32.4",
)
```

To declare several packages at once, use `py_repositories` with `package:version`
specs (each package gets its own target named `<name>_<package>`):

```python theme={null}
load("@rbs//python/dependencies.rbs", "py_repositories")

py_repositories(
    name = "deps",
    packages = [
        "django:5.0.1",
        "requests:2.32.4",
    ],
)
```

When a target that uses these packages is built, `rbs` resolves the full transitive
dependency tree from PyPI, selects the correct wheel for your platform and interpreter
version, downloads packages in parallel, and records the resolution in the workspace
lockfile (`rbs.lock`) so subsequent builds resolve instantly.

Reference dependencies from `BUILD.rbs` targets in either form:

* `:django_repo` — the repository target you declared. Name repository targets with a
  `_repo` suffix; that suffix is what marks a label as an external package reference.
* `@external://django:5.0.1` — a direct package reference, which works regardless of
  how the repository target is named.

## Rules

Load the rules at the top of your `BUILD.rbs`:

```python theme={null}
load("@rbs//python/rules.rbs", "py_binary", "py_library", "py_test")
```

### py\_binary

Builds a runnable Python application. The output is a self-contained launcher that
bundles your sources, all resolved dependencies, and the hermetic interpreter — run it
with `rbs run` or execute it directly.

```python theme={null}
py_binary(
    name = "dev_server",
    srcs = ["manage.py"],
    main = "manage.py",
    args = ["runserver", "8001"],
    deps = [
        ":blog_app",       # local py_library
        ":django_repo",    # external package
    ],
    env = {
        "DJANGO_SETTINGS_MODULE": "django_blog.settings",
    },
)
```

| Attribute        | Type        | Description                                                                          |
| ---------------- | ----------- | ------------------------------------------------------------------------------------ |
| `srcs`           | label list  | Python source files. Directory structure is preserved, so package imports work.      |
| `deps`           | label list  | Dependencies — local targets or external packages.                                   |
| `main`           | string      | Entry-point file, as a package-relative path (defaults to the first file in `srcs`). |
| `data`           | label list  | Data files bundled alongside the sources.                                            |
| `args`           | string list | Default arguments passed on every run.                                               |
| `env`            | string dict | Environment variables set at runtime.                                                |
| `python_version` | string      | Python version override.                                                             |

```bash theme={null}
rbs build //:dev_server
rbs run :dev_server
```

### py\_library

Groups source files into a reusable library that `py_binary` and `py_test` targets can
depend on. External dependencies of a library propagate transitively to whatever
depends on it.

```python theme={null}
py_library(
    name = "blog_app",
    srcs = [
        "blog/__init__.py",
        "blog/models.py",
        "blog/views.py",
    ],
    deps = [
        ":django_repo",
        ":pillow_repo",
    ],
)
```

| Attribute | Type        | Description                                          |
| --------- | ----------- | ---------------------------------------------------- |
| `srcs`    | label list  | Python source files (directory structure preserved). |
| `deps`    | label list  | Dependencies — local targets or external packages.   |
| `data`    | label list  | Data files.                                          |
| `imports` | string list | Import path modifications.                           |

### py\_test

Defines a test target. pytest and coverage support are included automatically — you
only declare the code under test:

```python theme={null}
py_test(
    name = "calculator_test",
    srcs = ["tests/test_calculator.py"],
    deps = [":calculator"],
)

py_test(
    name = "calculator_coverage_test",
    srcs = ["tests/test_calculator.py"],
    deps = [":calculator"],
    min_line_coverage = 85,
    min_branch_coverage = 75,
    size = "small",
    timeout = 60,
)
```

| Attribute               | Type        | Description                                                       |
| ----------------------- | ----------- | ----------------------------------------------------------------- |
| `srcs`                  | label list  | Test source files.                                                |
| `deps`                  | label list  | Dependencies — usually the library or binary under test.          |
| `main`                  | string      | Main test file (defaults to the first file in `srcs`).            |
| `data`                  | label list  | Test data files.                                                  |
| `args`                  | string list | Extra arguments passed to the test runner.                        |
| `env`                   | string dict | Environment variables for the test run.                           |
| `test_runner`           | string      | `"pytest"` (default) or `"unittest"`.                             |
| `size`                  | string      | Test size: `small`, `medium` (default), or `large`.               |
| `timeout`               | int         | Timeout in seconds (default 300).                                 |
| `python_version`        | string      | Python version override.                                          |
| `min_line_coverage`     | int         | Minimum line coverage %. The test fails below this threshold.     |
| `min_branch_coverage`   | int         | Minimum branch coverage %. The test fails below this threshold.   |
| `min_function_coverage` | int         | Minimum function coverage %. The test fails below this threshold. |

## Testing

Enable the shared test dependencies once in `WORKSPACE.rbs` — this pre-installs
pytest, coverage, pytest-cov, and pytest-xdist:

```python theme={null}
load("@rbs//python/toolchain.rbs", "python_toolchain", "python_test_deps")

python_toolchain(name = "python3", version = "3.11")
python_test_deps()
```

Then run tests with the standard commands:

```bash theme={null}
rbs test :calculator_test        # one target
rbs test //...                   # every test in the workspace
rbs test :calculator_test -w     # watch mode: re-run on file changes
rbs coverage :calculator_test    # run with coverage reporting
```

Coverage thresholds declared on the target (`min_line_coverage`, etc.) are enforced
when you run `rbs coverage` — a target below its threshold fails.

## Linting

`py_lint` runs Black and isort using the workspace's own hermetic interpreter — no
system installs. Register the linters in `WORKSPACE.rbs`, then declare a lint target:

```python theme={null}
# WORKSPACE.rbs
load("@rbs//python/lint.rbs", "register_python_linters")
register_python_linters()
```

```python theme={null}
# BUILD.rbs
load("@rbs//python/lint.rbs", "py_lint")

py_lint(
    name = "lint",
    srcs = glob(["**/*.py"], exclude = [".rbs/**", ".venv/**", "__pycache__/**"]),
)

py_lint(
    name = "lint_fix",
    srcs = glob(["**/*.py"], exclude = [".rbs/**", ".venv/**", "__pycache__/**"]),
    fix = True,
)
```

| Attribute | Type       | Description                                      |
| --------- | ---------- | ------------------------------------------------ |
| `srcs`    | label list | Python files to lint.                            |
| `black`   | bool       | Run Black (default `true`).                      |
| `isort`   | bool       | Run isort (default `true`).                      |
| `fix`     | bool       | Auto-fix instead of reporting (default `false`). |

```bash theme={null}
rbs build //:lint       # check — fails the build on lint errors
rbs build //:lint_fix   # apply fixes
```

## Protocol Buffers

Generate Python protobuf (and optionally gRPC) code from a `proto_library` with
`python_proto_library`:

```python theme={null}
load("@rbs//python/proto.rbs", "python_proto_library")

python_proto_library(
    name = "service_py_proto",
    proto = [":service_proto"],
    enable_grpc = True,
)
```

The protobuf runtime (and gRPC libraries when `enable_grpc = True`) are added
automatically. See [Other languages](/languages/other-languages) for the shared proto
toolchain and `proto_library` setup.

## Editor support

<Note>
  Python language support in the ReasonOS editor (completions, diagnostics, go-to-definition)
  is provisioned automatically when Python is detected in your workspace — there is
  nothing to configure in your build files. See the editor language support documentation
  for details.
</Note>
