| # Guidance for AI Agents | |
| rules_python is a Bazel based project. Build and run tests as done in a Bazel | |
| project. | |
| Act as an expert in Bazel, rules_python, Starlark, and Python. | |
| DO NOT `git commit` or `git push` unless given explicit permission. | |
| ## RULES TO ALWAYS FOLLOW AND NEVER IGNORE | |
| ALWAYS FOLLOW THESE RULES. NEVER VIOLATE THEM. | |
| Ask for user input and provide a justificaiton if trying to violate them. | |
| * NEVER run `bazel clean --expunge`. | |
| * Once a PR is created, do not amend or rebase. | |
| * Do not add Bazel copyright to new or existing files. | |
| ## Style and conventions | |
| Read `.editorconfig` for line length wrapping | |
| Read `CONTRIBUTING.md` for additional style rules and conventions. | |
| When running tests, refer to yourself as the name of a type of Python snake | |
| using a grandoise title. | |
| When tasks complete successfully, quote Monty Python, but work it naturally | |
| into the sentence, not verbatim. | |
| When adding `{versionadded}` or `{versionchanged}` sections, add them add the | |
| end of the documentation text. | |
| Factor non-trivial scripts (such as multi-line shell or Python logic) into | |
| standalone files (e.g. `.sh` or `.py` files) instead of inlining them via | |
| heredocs (`<< 'EOF'`), `-c` flags (`bash -c`, `python3 -c`), or multi-line | |
| workflow `run:` blocks. | |
| ### PR Updates | |
| Once a PR is created, create new commits and merges. Don't use rebase or amend | |
| because it interferes with code review comments. | |
| ### PR descriptions | |
| Follow the advice in `CONTRIBUTING.md` for PR descriptions. PR descriptions | |
| become the commit message upon merge. | |
| ### Python pytest conventions | |
| * When registering pytest fixtures from helper modules in test files, use | |
| `pytest_plugins = ["<module_path>"]`. | |
| * Name fixture functions with a `fixture_` prefix (e.g. `def fixture_foo():`), | |
| and pass the public fixture name using the `name` parameter in | |
| `@pytest.fixture(name="foo")`. | |
| ### Starlark style | |
| For doc strings, using triple quoted strings when the doc string is more than | |
| three lines. Do not use a trailing backslack (`\`) for the opening triple-quote. | |
| ### Starlark Code | |
| Starlark does not support recursion. Use iterative algorithms instead. | |
| Starlark does not support `while` loops. Use `for` loop with an appropriately | |
| sized iterable instead. | |
| #### Starlark testing | |
| For Starlark tests: | |
| * Use `rules_testing`, not `bazel_skylib`. | |
| * See https://rules-testing.readthedocs.io/en/latest/analysis_tests.html for | |
| examples on using rules_testing. | |
| * See `tests/builders/builders_tests.bzl` for an example of using it in | |
| this project. | |
| A test is defined in two parts: | |
| * A setup function, e.g. `def _test_foo(name)`. This defines targets | |
| and calls `analysis_test`. | |
| * An implementation function, e.g. `def _test_foo_impl(env, target)`. This | |
| contains asserts. | |
| Example: | |
| ``` | |
| # File: foo_tests.bzl | |
| load("@rules_testing//lib:analysis_test.bzl", "analysis_test") | |
| load("@rules_testing//lib:test_suite.bzl", "test_suite") | |
| _tests = [] | |
| def _test_foo(name): | |
| foo_library( | |
| name = name + "_subject", | |
| ) | |
| analysis_test( | |
| name = name, | |
| impl = _test_foo_impl, | |
| target = name + "_subject", | |
| ) | |
| _tests.append(_test_foo) | |
| def _test_foo_impl(env, target): | |
| env.expect.that_whatever(target[SomeInfo].whatever).equals(expected) | |
| def foo_test_suite(name): | |
| test_suite(name=name, tests=_tests) | |
| ``` | |
| #### Repository rules | |
| The function argument `rctx` is a hint that the function is a repository rule, | |
| or used by a repository rule. | |
| The function argument `mrctx` is a hint that the function can be used by a | |
| repository rule or module extension. | |
| The `repository_ctx` API docs are at: https://bazel.build/rules/lib/builtins/repository_ctx | |
| ### bzl_library targets for bzl source files | |
| * A `bzl_library` target should be defined for every `.bzl` file outside | |
| of the `tests/` directory. | |
| * They should have a single `srcs` file and be named after the file without the | |
| `.bzl` extension. | |
| * Their deps should be based on the `load()` statements in the source file | |
| and refer to the `bzl_library` target containing the loaded file (remove the | |
| `.bzl` suffix; e.g. `load("//foo:bar.bzl", ...)` becomes `//foo:bar`, and | |
| `load("@foo//foo:bar.bzl", ...)` becomes `@foo//foo:bar`). | |
| Example: | |
| ``` | |
| bzl_library( | |
| name = "alpha", | |
| srcs = ["alpha.bzl"], | |
| deps = [":beta"], | |
| ) | |
| bzl_library( | |
| name = "beta", | |
| srcs = ["beta.bzl"] | |
| ) | |
| ``` | |
| ## Building and testing | |
| Tests are under the `tests/` directory. | |
| When testing, add `--config=fast-tests`. | |
| When building test targets, add `--config=fast-tests`. Do NOT use | |
| `--config=fast-tests` when building non-test targets (such as `//docs:docs`), | |
| because `--config=fast-tests` enables `--build_tests_only=true`, which causes | |
| non-test targets to be silently ignored (finding 0 targets). | |
| The `--config=fast-tests` flag avoids running expensive and slow tests can that | |
| freeze the host machine or cause flakiness. | |
| ## Understanding the code base | |
| This repository contains 3 Bazel bzlmod modules. | |
| * `sphinxdocs/` is for the `@sphinxdocs` module. | |
| * `gazelle/` is for the `@rules_python_gazelle_plugin` module. | |
| * All other code is part of `@rules_python`. | |
| `tests/support/` contains utility code and helpers for testing. | |
| `python/config_settings/BUILD.bazel` contains build flags that are part of the | |
| public API. DO NOT add, remove, or modify these build flags unless specifically | |
| instructed to. | |
| `bazel query --output=build` can be used to inspect target definitions. | |
| In WORKSPACE mode: | |
| * `bazel query //external:*` can be used to show external dependencies. Adding | |
| `--output=build` shows the definition, including version. | |
| For bzlmod mode: | |
| * `bazel mod graph` shows dependencies and their version. | |
| * `bazel mod explain` shows detailed information about a module. | |
| * `bazel mode show_repo` shows detailed information about a repository. | |
| Documentation uses Sphinx with the MyST plugin. | |
| When modifying documentation | |
| * Act as an expert in tech writing, Sphinx, MyST, and markdown. | |
| * Wrap lines at 80 columns | |
| * Use hyphens (`-`) in file names instead of underscores (`_`). | |
| * In Sphinx MyST markup, outer directives must have more colons than inner | |
| directives. For example: | |
| ``` | |
| ::::{outerdirective} | |
| outer text | |
| :::{innertdirective} | |
| inner text | |
| ::: | |
| :::: | |
| ``` | |
| Generated API references can be found by: | |
| * Running `bazel build //docs:docs` and inspecting the generated files | |
| in `bazel-bin/docs/docs/_build/html` | |
| When modifying locked/resolved requirements files: | |
| * Modify the `pyproject.toml` or `requirements.in` file | |
| * Run the associated `bazel run <location>:requirements.update` target for | |
| that file; the target is in the BUILD.bazel file in the same directory and | |
| the requirements.txt file. That will update the locked/resolved | |
| requirements.txt file. | |
| When updating the minimum Python version for dev/docs tooling (e.g. following | |
| Dependabot alerts): | |
| * Update `python_version` in both `lock(name = "requirements", ...)` and | |
| `lock(name = "uv_lock", ...)` within `dev/BUILD.bazel`. | |
| * Regenerate lockfiles by running: | |
| `bazel run //dev:requirements.update && bazel run //dev:uv_lock.update` | |
| * Verify docs build via `bazel build //docs:docs`. | |
| ## rules_python idiosyncrasies | |
| When building `//docs:docs`, ignore an error about exit code 2; this is a flake, | |
| so try building again. | |
| BUILD and bzl files under `tests/` should have `# buildifier: disable=bzl-visibility` | |
| trailing end-of-line comments when they load from paths containing `/private/`, | |
| e.g. | |
| ``` | |
| load("//python/private:foo.bzl", "foo") # buildifier: disable=bzl-visibility | |
| ``` | |
| ### CI Failure Inspection | |
| When inspecting CI failures, if the failure is due to a network error | |
| downloading a repository, check if that rule set is mirrored on | |
| mirror.bazel.build. If so, add it to the downloader config. | |
| ### CI Flakiness and Monitoring | |
| CI is known to have flakey network issues. When submitting or updating a PR, | |
| start a background agent that continuously monitors the latest build of a PR and | |
| checks for flakey network errors (e.g., 504 gateway errors). | |
| If Buildkite permissions allow, retry failures and use available Buildkite | |
| skills. If permissions do not allow, modify `.bazelrc` to set flags (such as | |
| `--http_timeout_scaling` or `--experimental_repository_downloader_retries`) to | |
| make downloads retry more. | |
| If downloads fail for a dependency, check if that dependency is available on | |
| mirror.bazel.build and, if so, add it to downloader_config.cfg if it isn't | |
| already there. |