Skip to content

matchers

matchers

Composable AST matchers for style rules — import this module as M.

Every factory (kind, calls, under, ...) and preset (imports, control_flow, ...) is a module-level Matcher builder or constant; author a rule by combining them with &, |, and ~.

Example

from captain_hook.style import matchers as M M.imports & M.child_of(M.control_flow) & ~M.under(M.type_checking)

Matcher dataclass

Matcher(
    test: Predicate,
    label: str = "matcher",
    structural: bool = False,
)

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
where(predicate: Callable[[AST], bool]) -> Matcher

Refine with a node-local one-off predicate — the escape hatch for bespoke conditions.

over
over(tree: AST) -> Iterator[ast.AST]

Yield every node in tree that matches.

violations
violations(
    tree: AST,
    label: str | Callable[[AST], str] | None = None,
) -> Iterator[Violation]

Yield a Violation for each match, located at its line.

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.

exists
exists(tree: AST) -> bool

Return whether any node in tree matches.

matches
matches(node: AST) -> bool

Test a single node (node-local matchers only; raises for structural matchers).

kind

kind(
    *types: type[AST], label: str | None = None
) -> Matcher

Match any of the given AST node types — the primitive for a category not shipped.

calls

calls(name: str) -> Matcher

Match a call to the bare-name function name (e.g. calls("zip")).

kwarg

kwarg(name: str) -> Matcher

Match a call that passes the keyword argument name (combine with calls).

ref

ref(name: str) -> Matcher

Match the bare name reference name (e.g. ref("Any") for x: Any).

named

named(pattern: str | Pattern[str]) -> Matcher

Match a class/function/assignment/parameter whose bound name matches pattern (re.search).

annotated

annotated(inner: Matcher | None = None) -> Matcher

Match an annotation owner (annotated variable, parameter, or function return).

Excludes *args/**kwargs. When inner is given, the owner's annotation expression must also match it (e.g. annotated(ref("Any"))).

under

under(other: Matcher) -> Matcher

Match a node with any ancestor matching other (negate with ~ for "not inside").

child_of

child_of(other: Matcher) -> Matcher

Match a node whose immediate parent matches other.

following

following(boundary: Matcher) -> Matcher

Match a body statement that follows the first sibling matching boundary.