style
style ¶
Matcher
dataclass
¶
A composable, immutable AST matcher: a node predicate that is also a tree selector.
A Matcher wraps a single (node, parents) -> bool test. Node-local matchers
(e.g. calls) ignore parents; structural
matchers (e.g. under) consult it. Compose with
the boolean algebra & (intersection), | (union), and ~ (negation), refine with
where, and finish with a terminal —
over,
violations, or
exists.
The module-level vocabulary (M.imports, M.control_flow, ...) and factories
(M.kind, M.calls, ...) are the whole surface — author a new rule by combining
them, not by reaching for a framework helper.
Example
M.imports & M.child_of(M.control_flow) & ~M.under(M.type_checking)
where ¶
Refine with a node-local one-off predicate — the escape hatch for bespoke conditions.
diff ¶
diff(
pre: AST,
post: AST,
key: Callable[[AST], Hashable] = ast.unparse,
label: str | Callable[[AST], str] | None = None,
) -> Iterator[Violation]
Yield violations for matches in post whose key was absent from pre.
matches ¶
Test a single node (node-local matchers only; raises for structural matchers).
StyleDiffRule ¶
Bases: StyleRule
Base class for a diff rule: flags constructs newly introduced by the change.
Like StyleRule, but it compares the pre-edit and
post-edit trees. The declarative form flags nodes matching match in the new tree
that were absent from the old tree (by unparsed source); override check when the
"newly introduced" identity needs custom logic.
Example
StyleRule ¶
Bases: ABC
Base class for a single-tree AST style rule applied to Python edits and writes.
Subclass it and write the rule's message as the class docstring ({violations} is
substituted at fire time). Declare the rule as data by setting match to a
Matcher (and optionally label); override check
only for logic a matcher can't express. The class name is the rule's identity —
NoNestedImports becomes "no-nested-imports".
Example
Violation
dataclass
¶
A single style violation, located by line so the runner can scope it to the edit.
Attributes:
| Name | Type | Description |
|---|---|---|
line |
int
|
1-based line number of the offending construct in the post-edit file. |
label |
str
|
Short human-readable description, rendered as |
styleguide ¶
styleguide(
*rules: type[StyleRule],
block: bool = False,
only_if: Sequence[TCondition] = (),
skip_if: Sequence[TCondition] = (),
events: Event | None = None,
max_shown: int = 5,
) -> None
Register one change-scoped hook applying the given style rules to Python edits and writes.
Each rule is a StyleRule (or
StyleDiffRule) subclass whose docstring is its
message. The single registered hook parses the edited file once, runs every rule against the
post-edit tree, scopes each violation to the changed lines, and emits one aggregated warning
(or block, when block is set). Call again with different only_if / skip_if /
events / block to register a separately scoped hook.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rules
|
type[StyleRule]
|
|
()
|
block
|
bool
|
Block the tool call instead of warning. |
False
|
only_if
|
Sequence[TCondition]
|
Extra conditions ANDed onto the built-in |
()
|
skip_if
|
Sequence[TCondition]
|
Extra conditions ORed onto the built-in test-file skip. |
()
|
events
|
Event | None
|
Override the default |
None
|
max_shown
|
int
|
Maximum violations shown per rule. |
5
|
Example
styleguide(NoPrint, NoBareExcept) styleguide(NoSqlInjection, block=True, only_if=[FilePath("api/*/.py")])