grievejia/rules_pyrefly

Bazel rules for running Pyrefly type checking over Python targets. Provides an aspect-based integration and Bzlmod toolchain extension for hermetic Pyrefly binary management.

★ 0Forks 0GitHub ↗Compare

README

rules_pyrefly

Bazel rules for running Pyrefly type checking over py_* targets.

Status

rules_pyrefly is an experimental integration for pyrefly bazel-check. It can run Pyrefly through a registered Bzlmod toolchain or through a direct pyrefly_cli executable override.

Pyrefly 1.2.0 is the checked-in supported version and the first stable Pyrefly release that includes the experimental bazel-check command.

For more detail, see the design, integration notes, and user notes.

Usage

Add the ruleset and register a Pyrefly toolchain in MODULE.bazel:

bazel_dep(name = "rules_pyrefly", version = "<version>")

pyrefly = use_extension("@rules_pyrefly//pyrefly:extensions.bzl", "pyrefly")
pyrefly.toolchain(version = "1.2.0")
use_repo(pyrefly, "pyrefly_toolchains")
register_toolchains("@pyrefly_toolchains//:all")

The selected Pyrefly version must either be present in the ruleset's checked-in release metadata or be supplied with explicit release asset checksums. Use base_url with checksums for forks, mirrors, or prereleases outside the default Pyrefly GitHub release location.

Define an aspect in a project-local .bzl file:

load("@rules_pyrefly//pyrefly:pyrefly.bzl", "pyrefly")

pyrefly_aspect = pyrefly()

Register it in .bazelrc:

build:pyrefly --aspects=//tools:aspects.bzl%pyrefly_aspect
build:pyrefly --output_groups=pyrefly

Then run:

bazel build --config=pyrefly //...

Use --output_groups=+pyrefly only when you also want Bazel to build the targets' normal outputs. For type-check-only runs, --output_groups=pyrefly keeps unrelated build failures out of the Pyrefly pass.

Configuration

The default aspect infers the target Python version from the active rules_python toolchain and the target platform from Bazel platform constraints. Pass python_version or system_platform only when that inference is not enough for a workspace.

The pyrefly() aspect factory accepts these options:

Option Default Purpose
pyrefly_cli Registered toolchain Executable override supplied as a Label.
python_version Inferred Target Python version, such as "3.12".
system_platform Inferred Target sys.platform value, such as "linux", "darwin", or "win32".
preset Unset Pyrefly preset passed to bazel-check.
min_severity "error" Minimum reported severity: "ignore", "info", "warn", or "error".
error_severities {} Per-error-kind severity overrides.
suppression_tags ["no-pyrefly"] Target tags that disable checking.
opt_in_tags [] When nonempty, target tags that enable checking.
color "always" Diagnostic color mode: "auto", "always", or "never".

error_severities maps Pyrefly error-kind names to the same four severity values accepted by min_severity. Suppression tags take precedence over opt-in tags. For example:

pyrefly_aspect = pyrefly(
    color = "auto",
    error_severities = {"bad-assignment": "error"},
    min_severity = "warn",
    suppression_tags = ["no-pyrefly", "no-checks"],
)

Use pyrefly_cli when testing a local or otherwise preinstalled Pyrefly binary:

pyrefly_aspect = pyrefly(
    pyrefly_cli = Label("@local_pyrefly//:pyrefly"),
)

The aspect follows the rules_python python_import_all_repositories setting when it builds repository-root search-path facts for Pyrefly.

Generated pyrefly bazel-check input JSON files are available through the debug-only pyrefly_input output group.

Examples

The demo workspace consumes this repository through local_path_override:

cd examples/demo
bazel build --config=pyrefly //...

The demo includes //:type_error, a manual target with an intentional type error:

bazel build --config=pyrefly //:type_error

To test a local Pyrefly binary instead of the release toolchain, create a Bazel repository that exposes the binary as a public executable target named //:pyrefly, then inject it as @local_pyrefly:

bazel build \
  --inject_repository=local_pyrefly=/path/to/local_pyrefly \
  --aspects=//:aspects_local.bzl%pyrefly_aspect \
  --output_groups=pyrefly \
  //...

Development

Common checks:

bazel build //...
bazel run @buildifier_prebuilt//:buildifier -- -mode=check -r .
python tests/verify_demo.py

Toolchain resolution coverage downloads release assets and needs BCR and GitHub release access:

python tests/verify_toolchain_resolution.py
python tests/verify_toolchain_resolution.py --output-group=pyrefly

The first command checks generated input JSON without executing the downloaded Pyrefly binary. The second command exercises the real bazel-check action path.

License

rules_pyrefly is Apache 2.0 licensed. See LICENSE and CODE_OF_CONDUCT.md.

Contributors

grievejia

Issues