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. - Do not repeat default values in CLI flag
help strings when the parser displays defaults automatically. - Include a
Usage: example and input file format description in CLI module docstrings. - State what a module or function does in docstrings; omit execution adverbs like
"Asynchronously". - Split simple comma- or newline-delimited CLI inputs with
splitlines() and .split(",") instead of csv.
API Minimalism & Module State
- Pass dependencies explicitly; avoid implicit optionals that auto-load state or magical global state. Do not return mutable internal state.
- Assume the supported execution environment. Do not add
ImportError fallbacks or sys.path probing unless direct invocation outside Bazel is required by an external caller constraint and justified in a comment or PR description. - Prefix private module-level constants and tables with
_. - Use direct attribute syntax (
obj.attr) instead of getattr/setattr for static attribute names.
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. - Let errors bubble up from utility functions instead of printing and returning failure sentinels or catching and re-wrapping into caller-specific exception classes.
- Use
e.add_note(...) to attach context to an exception and plain raise when re-raising. - Do not place redundant
return statements after sys.exit(...).
Unit Testing
- Use real temporary files (
tmp_path or tempfile) instead of mocking file IO or pathlib.Path. - Use
unittest.mock.patch (and mock.create_autospec(..., instance=True) over bare Mock()/MagicMock()) instead of manual state save/restore, and avoid over-mocking the unit under test. - Call
main(args) directly in tests instead of mutating sys.argv, and test sys.exit with pytest.raises(SystemExit) / assertRaises(SystemExit) instead of mocking sys.exit. - Do not prefix test method names with redundant target names (e.g., use
test_success_directory instead of test_main_success_directory). - Factor repeated assertion blocks into
_assert_* helpers, and assert against literal expected strings in formatting/transformation tests rather than duplicating production logic.
Subprocess
- Demarcate captured stdout/stderr in exceptions with 20
= characters: ==================== STDOUT BEGIN ==================== / ==================== STDOUT END ==================== (same for STDERR). - Use
asyncio for concurrent subprocesses and retried network I/O.
Data Classes & TypedDict
- In dataclasses (
@dataclasses.dataclass), add a comment above each field describing its value. - 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.
Security & Sensitive Data
- URL Domain Validation (
py/incomplete-url-substring-sanitization): Never use substring checks ("example.com" in url, url.endswith(...)) to validate domains. Parse with urllib.parse.urlsplit(url) and verify parsed.netloc in {"example.com", "www.example.com"} or check exact hostname equality. - Credential Logging & Mock Names (
py/clear-text-logging-sensitive-data): Never log plaintext secrets, tokens, or passwords. In test fixtures or mock servers that print mock tokens, avoid sensitive variable names (password, secret, token); use mock_token and annotate intentional log lines with # codeql[py/clear-text-logging-sensitive-data].
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.
Formatting
- Do not line-break long string literals (such as URLs or hashes) to fit line length limits; allow them to exceed the limit.