| --- | |
| trigger: glob | |
| description: Python coding, typing, CLI, and pytest conventions | |
| globs: "*.py,*.sh" | |
| --- | |
| # Python Conventions | |
| ## pytest | |
| * Register helper fixtures using `pytest_plugins = ["<module_path>"]`. | |
| * Name fixture functions with `fixture_` prefix and pass public name via | |
| `@pytest.fixture(name="foo")`. | |
| ## CLI & Arguments | |
| * Use direct attribute access (e.g. `args.foo`) on `argparse.Namespace` with | |
| well-defined shapes. Avoid defensive `getattr()`. | |
| * Assume caller-guaranteed preconditions hold (e.g., input directories exist | |
| and contain files). Do not add silent no-op guards (`if path.is_dir():` or | |
| `if not path.is_file(): continue`) that mask broken invocations. | |
| * Factor non-trivial scripts (such as multi-line Python or shell logic) into | |
| standalone files (e.g. `.py` or `.sh` files) instead of inlining them via | |
| heredocs (`<< 'EOF'`) or `-c` flags (`python3 -c`, `bash -c`). | |
| * **Directly Executable Scripts**: Scripts executed directly (outside a | |
| `py_binary` rule) must have `chmod +x` (mode `100755`) and a | |
| `#!/usr/bin/env python3` shebang. | |
| ## Interface & ABC Docstrings | |
| * In abstract base class / interface methods, always document `Args:`, | |
| `Returns:`, and all custom exceptions raised under `Raises:`. | |
| ## Exception Handling | |
| * Inherit custom exceptions from `Exception`, not `RuntimeError`. | |
| ## Subprocess | |
| * Demarcate captured stdout/stderr in exceptions with 20 `=` characters: | |
| `==================== STDOUT BEGIN ====================` / | |
| `==================== STDOUT END ====================` (same for STDERR). | |
| ## TypedDict | |
| * **External Objects**: When defining a `TypedDict` for an external object, | |
| link to its definition in the docstring. | |
| ## Type Checking & Annotations | |
| * **Version differences**: Guard version-specific arguments or APIs with runtime | |
| checks (e.g. `if sys.version_info >= (3, 13):`) instead of | |
| `# pyrefly: ignore[...]` comments. | |
| * **`importlib.metadata` `PackagePath`**: `f.locate()` is typed as `PathLike`. | |
| Wrap with `pathlib.Path(f.locate())` to call `.exists()`, `.is_file()`, etc. | |
| * **In-file disables vs target skipping**: Prefer | |
| `# pyrefly: ignore[<error-code>]` (e.g. `[missing-import]`) over | |
| `tags = ["no-pyrefly"]`. | |
| * **No blanket ignores**: NEVER use bare `# type: ignore` or literal | |
| `# type: ignore[...]`. Use error-specific ignores instead. | |
| * **Ignore comments**: When adding `# pyrefly: ignore[...]` or type | |
| suppressions, add an explanatory comment indicating why it is suppressed. | |
| * **Type assertions**: When adding assertions for type narrowing, add an | |
| end-of-line comment: `assert foo is not None # type assert`. | |
| * **Consent for `Any`**: Require user consent before changing type annotations | |
| to `Any`. | |
| * **Union syntax (`X | None`)**: Use `X | None` instead of `typing.Optional[X]`. | |
| Add `from __future__ import annotations` if necessary. | |
| * **Collections generics**: Use `collections.abc` (e.g., `Sequence`, `Iterable`, | |
| `Iterator`, `Callable`, `Mapping`) and builtin generics (`list`, `dict`, | |
| `tuple`, `set`) instead of `typing.XXX` collection types. | |
| ## Runfiles | |
| * **Fail-fast creation**: Prefer `runfiles.CreateOrRaise()` over | |
| `runfiles.Create()` + `assert`. | |
| * **Path navigation**: Prefer `rf.root() / "<repo>/<path>"` over `Rlocation()`; | |
| use `.glob()` on `runfiles.Path` instead of multi-root fallback loops. | |
| ## Delegating Functions | |
| * Module-level functions delegating to class methods should have a docstring | |
| referring to the class method (e.g. `"""Refer to \`Class.method\`."""`). | |
| ## Context Managers | |
| * Implement temporary state changes (e.g., `Git.checkout()`, `os.chdir()`) as | |
| context managers that restore prior state on exit. |