blob: 9730a1181c824796fe0fa4f4a04936be693f57b7 [file] [log] [blame] [view] [edit]
# rules_mypy
An aspect to instrument `py_*` targets with mypy type-checking.
Compared to [bazel-mypy-integration](https://github.com/bazel-contrib/bazel-mypy-integration), this ruleset aims to make a couple of improvements:
- Propagation of the mypy cache between dependencies within a repository to avoid exponential type-checking work
- Robust (and automated) support for including 3rd party types/stubs packages
> [!WARNING]
> rules_mypy's build actions produce mypy caches as outputs, and these may contain large file counts and that will only grow as a dependency chain grows. This may have an impact on the size and usage of build and/or remote caches.
## Usage
This aspect will run over any `py_binary`, `py_library` or `py_test`.
Setup is significantly easier with bzlmod, we recommend and predominantly support bzlmod, though these rules should work without issue in non-bzlmod setups, albeit with more work to configure.
### Bzlmod Setup
**Add rules_mypy to your MODULE.bazel:**
```starlark
bazel_dep(name = "rules_mypy", version = "0.0.0")
```
**Optionally, configure a types repository:**
Many Python packages have separately published types/stubs packages. While mypy (and these rules) will work without including these types, this ruleset provides some utilities for leveraging these types to improve mypy's type checking.
```starlark
types = use_extension("@rules_mypy//mypy:types.bzl", "types")
types.requirements(
name = "pip_types",
# `@pip` in the next line corresponds to the `hub_name` when using
# rules_python's `pip.parse(...)`.
pip_requirements = "@pip//:requirements.bzl",
# also legal to pass a `requirements.in` here
requirements_txt = "//:requirements.txt",
)
use_repo(types, "pip_types")
```
**Configure `mypy_aspect`.**
Define a new aspect in a `.bzl` file (such as `./tools/aspects.bzl`):
```starlark
load("@pip_types//:types.bzl", "types")
load("@rules_mypy//mypy:mypy.bzl", "mypy")
mypy_aspect = mypy(types = types)
```
Update your `.bazelrc` to include this new aspect:
```starlark
# register mypy_aspect with Bazel
build --aspects //tools:aspects.bzl%mypy_aspect
# optionally, default enable the mypy checks
build --output_groups=+mypy
```
## Customizing mypy
### Configuring mypy with mypy.ini
mypy's behavior may be customized using a [mypy config file](https://mypy.readthedocs.io/en/stable/config_file.html) file. To use a mypy config file, pass a label for a valid config file to the `mypy` aspect factory:
```starlark
mypy_aspect = mypy(
mypy_ini = "@@//:mypy.ini",
types = types,
)
```
> [!NOTE]
> The label passed to `mypy_ini` needs to be absolute (a prefix of `@@` means the root repo).
> [!NOTE]
> mypy.ini files should likely contain the following lines to suppress type-checking 3rd party modules.
>
> ```
> follow_imports = silent
> follow_imports_for_stubs = True
> ```
### Changing the version of mypy and/or including plugins
To customize the version of mypy, use rules_python's requirements resolution and construct a custom mypy CLI:
```starlark
# in a BUILD file
load("@pip//:requirements.bzl", "requirements") # '@pip' must match configured pip hub_name
load("@rules_mypy//mypy:mypy.bzl", "mypy", "mypy_cli")
mypy_cli(
name = "mypy_cli",
mypy_requirement = requirement("mypy"),
)
```
And in your `aspects.bzl` (or similar) file:
```starlark
load("@rules_mypy//mypy:mypy.bzl", "mypy")
mypy_aspect = mypy(
mypy_cli = ":mypy_cli",
types = types,
)
```
Further, to use mypy plugins referenced in any config file, use the `deps` attribute of `mypy_cli`:
```starlark
# in a BUILD file
load("@pip//:requirements.bzl", "requirement") # '@pip' must match configured pip hub_name
load("@rules_mypy//mypy:mypy.bzl", "mypy", "mypy_cli")
mypy_cli(
name = "mypy_cli",
mypy_requirement = requirement("mypy"),
deps = [
requirement("pydantic"),
],
)
```
## Skipping Targets
Skip running mypy on targets by tagging with `no-mypy`, or customize the tags that will suppress mypy by providing a list to the `suppression_tags` argument of the mypy aspect initializer:
```starlark
load("@rules_mypy//mypy:mypy.bzl", "mypy")
mypy_aspect = mypy(
suppression_tags = ["no-mypy", "no-checks"],
types = types,
)
```
## Running in opt-in mode
To add type checking to a codebase incrementally, configure a list of opt-in tags that will suppress running mypy by default unless a target is tagged explicitly with one of the opt-in tags.
```starlark
load("@rules_mypy//mypy:mypy.bzl", "mypy")
mypy_aspect = mypy(
opt_in_tags = ["typecheck"],
types = types,
)
```