Skip to content

Pattern Nodes

Pattern nodes are the basic building blocks to construct matching logic over an AST.

Basic Nodes

Basic nodes allow matching structural properties, capturing values, and simple boolean combinations.

ast_pattern_engine.nodes.basic

AllOf

Bases: Pattern

Matches if all patterns in the sequence match the node.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

Sequence of patterns that must all match the node.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class AllOf(Pattern):
    """Matches if all patterns in the sequence match the node.

    Args:
        patterns: Sequence of patterns that must all match the node.
    """

    patterns: Sequence[Pattern]

    def __init__(self, patterns: Sequence[Pattern]):
        """AllOf node.

        Args:
            patterns: Sequence of patterns that must all match the node.
        """
        self.patterns = list(patterns)

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        new_bindings = dict(bindings)

        for pattern in self.patterns:
            new_bindings = pattern.match_node(
                node, new_bindings, _force_list=_force_list
            )
            if new_bindings is None:
                return None
        return new_bindings

__init__

__init__(patterns: Sequence[Pattern])

AllOf node.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

Sequence of patterns that must all match the node.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, patterns: Sequence[Pattern]):
    """AllOf node.

    Args:
        patterns: Sequence of patterns that must all match the node.
    """
    self.patterns = list(patterns)

AnyOf

Bases: Pattern

Match any of the patterns in the sequence.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

Sequence of patterns where at least one must match.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class AnyOf(Pattern):
    """Match any of the patterns in the sequence.

    Args:
        patterns: Sequence of patterns where at least one must match.
    """

    patterns: list[Pattern]

    def __init__(self, patterns: Sequence[Pattern]):
        """AnyOf node.

        Args:
            patterns: Sequence of patterns where at least one must match.
        """
        self.patterns = list(patterns)

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        merged = dict(bindings)
        matched_any = False

        for p in self.patterns:
            # Test each pattern against the original node
            res = p.match_node(node, {}, _force_list=_force_list)
            if res is not None:
                matched_any = True
                # Merge successful bindings
                for k, v in res.items():
                    if k in merged:
                        return None  # duplicate key safeguard
                    merged[k] = v

        if not matched_any:
            return None

        return merged

__init__

__init__(patterns: Sequence[Pattern])

AnyOf node.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

Sequence of patterns where at least one must match.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, patterns: Sequence[Pattern]):
    """AnyOf node.

    Args:
        patterns: Sequence of patterns where at least one must match.
    """
    self.patterns = list(patterns)

Bind

Bases: Pattern

Bind the current node to key.

This is syntactic sugar for:

Collect(WildCard(), "x")

Parameters:

Name Type Description Default
key str

The key to bind the node(s) or value(s) to.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class Bind(Pattern):
    """Bind the current node to `key`.

    This is syntactic sugar for:
    >>> Collect(WildCard(), "x")

    Args:
        key: The key to bind the node(s) or value(s) to.
    """

    key: str

    def __init__(self, key: str):
        """Bind node.

        Args:
            key: The key to bind the node(s) or value(s) to.
        """
        self.key = key

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        if self.key in bindings:
            if not _force_list:
                return None
            bindings[self.key] = self._to_list(bindings[self.key]) + [node]
        else:
            bindings[self.key] = [node] if _force_list else node
        return bindings

__init__

__init__(key: str)

Bind node.

Parameters:

Name Type Description Default
key str

The key to bind the node(s) or value(s) to.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, key: str):
    """Bind node.

    Args:
        key: The key to bind the node(s) or value(s) to.
    """
    self.key = key

Collect

Bases: Pattern

Collect the matched node under key and merge sub-bindings into current scope.

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
key str

The key to bind the pattern result to.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class Collect(Pattern):
    """Collect the matched node under `key` and merge sub-bindings into current scope.

    Args:
        pattern: The pattern to match.
        key: The key to bind the pattern result to.
    """

    pattern: Pattern
    key: str

    def __init__(self, pattern: Pattern, key: str):
        """Collect node.

        Args:
            pattern: The pattern to match.
            key: The key to bind the pattern result to.
        """
        self.pattern = pattern
        self.key = key

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ) -> None | dict[str, Any]:
        bindings = bindings or {}
        # Collect is a binding boundary — inner patterns always see _force_list=False
        inner = self.pattern.match_node(node, {}, _force_list=False)
        if inner is None:
            return None
        merged = dict(bindings)

        if _force_list:
            if inner:
                # Inside a repetition wrapper with inner bindings;
                # append the inner-dict itself to the list under key.
                if self.key in merged:
                    merged[self.key].append(inner)
                else:
                    merged[self.key] = [inner]
                return merged
            else:
                if self.key in merged:
                    merged[self.key].append(node)
                else:
                    merged[self.key] = [node]
                return merged

        # Outside repetition - store node and merge inner bindings
        if self.key in merged:
            return None  # Scalar expected, duplicate found
        merged[self.key] = node
        for k, v in inner.items():
            if k in merged:
                return None  # Scalar expected, duplicate found
            merged[k] = v
        return merged

__init__

__init__(pattern: Pattern, key: str)

Collect node.

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
key str

The key to bind the pattern result to.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, pattern: Pattern, key: str):
    """Collect node.

    Args:
        pattern: The pattern to match.
        key: The key to bind the pattern result to.
    """
    self.pattern = pattern
    self.key = key

Contains

Bases: Pattern

Matches a pattern that is contained anywhere within the node's sub-tree.

Parameters:

Name Type Description Default
pattern Sequence[Pattern]

The pattern or sequence of patterns to search for in the sub-tree.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class Contains(Pattern):
    """Matches a pattern that is contained anywhere within the node's sub-tree.

    Args:
        pattern: The pattern or sequence of patterns to search for in the sub-tree.
    """

    pattern: Sequence[Pattern]

    def __init__(self, pattern: Sequence[Pattern]):
        """Contains node.

        Args:
            pattern: The pattern or sequence of patterns to search for in the sub-tree.
        """
        self.pattern = list(pattern)

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        finder = SingleOccurrenceFinder(self.pattern)
        finder.visit(node)

        if finder.found_match():
            found_bindings = finder.match_bindings

            merged = dict(bindings)
            for k, v in found_bindings.items():
                if k in merged:
                    return None  # Duplicate key
                merged[k] = v
            return merged

        return None

__init__

__init__(pattern: Sequence[Pattern])

Contains node.

Parameters:

Name Type Description Default
pattern Sequence[Pattern]

The pattern or sequence of patterns to search for in the sub-tree.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, pattern: Sequence[Pattern]):
    """Contains node.

    Args:
        pattern: The pattern or sequence of patterns to search for in the sub-tree.
    """
    self.pattern = list(pattern)

Filter

Bases: Pattern

Match nodes where predicate(node) returns True and optionally bind node to key.

Parameters:

Name Type Description Default
predicate Callable[[Any], bool]

A callable that returns True if the node matches.

required
key str | None

Optional key to bind the matched node to.

None
Source code in src/ast_pattern_engine/nodes/basic.py
class Filter(Pattern):
    """Match nodes where `predicate(node)` returns `True` and optionally bind `node` to `key`.

    Args:
        predicate: A callable that returns True if the node matches.
        key: Optional key to bind the matched node to.
    """

    predicate: Callable[[Any], bool]
    key: str | None

    def __init__(self, predicate: Callable[[Any], bool], key: str | None = None):
        """Filter node.

        Args:
            predicate: A callable that returns True if the node matches.
            key: Optional key to bind the matched node to.
        """
        self.predicate = predicate
        self.key = key

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        if not self.predicate(node):
            return None
        if self.key is None:
            return bindings
        if self.key in bindings:
            if not _force_list:
                return None
            bindings[self.key] = self._to_list(bindings[self.key]) + [node]
        else:
            bindings[self.key] = [node] if _force_list else node
        return bindings

__init__

__init__(
    predicate: Callable[[Any], bool], key: str | None = None
)

Filter node.

Parameters:

Name Type Description Default
predicate Callable[[Any], bool]

A callable that returns True if the node matches.

required
key str | None

Optional key to bind the matched node to.

None
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, predicate: Callable[[Any], bool], key: str | None = None):
    """Filter node.

    Args:
        predicate: A callable that returns True if the node matches.
        key: Optional key to bind the matched node to.
    """
    self.predicate = predicate
    self.key = key

NodePattern

Bases: Pattern

Match an AST node of node_type with constraints on its fields.

Parameters:

Name Type Description Default
node_type type[AST]

The AST node class to match (e.g., ast.Assign).

required
**field_patterns Pattern | Any

Patterns or exact values to match against the node's fields.

{}
Source code in src/ast_pattern_engine/nodes/basic.py
class NodePattern(Pattern):
    """Match an AST node of `node_type` with constraints on its fields.

    Args:
        node_type: The AST node class to match (e.g., ast.Assign).
        **field_patterns: Patterns or exact values to match against the node's fields.
    """

    node_type: type[ast.AST]
    field_patterns: dict[str, Pattern | Any]

    def __init__(self, node_type: type[ast.AST], **field_patterns: Pattern | Any):
        """NodePattern node.

        Args:
            node_type: The AST node class to match (e.g., ast.Assign).
            **field_patterns: Patterns or exact values to match against the node's fields.
        """
        self.node_type = node_type
        self.field_patterns = field_patterns

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        if not isinstance(node, self.node_type):
            return None
        merged = dict(bindings)
        for field, pat in self.field_patterns.items():
            val = getattr(node, field, None)
            if isinstance(pat, Pattern):
                if val is None:
                    return None
                # Match list-valued field
                if isinstance(val, list) and not isinstance(pat, Bind):
                    res = _match_patterns([pat], val, 0, {}, _force_list=_force_list)
                    if not res:
                        return None
                    sub_bind = res[-1][0]
                else:
                    sub_bind = pat.match_node(val, {}, _force_list=_force_list)
                    if sub_bind is None:
                        return None
                # merge sub bindings
                for k, v in sub_bind.items():
                    if k in merged:
                        if not _force_list:
                            return None
                        merged[k] = self._to_list(merged[k]) + self._to_list(v)
                    else:
                        merged[k] = self._to_list(v) if _force_list else v
            else:
                if val != pat:
                    return None
        return merged

__init__

__init__(
    node_type: type[AST], **field_patterns: Pattern | Any
)

NodePattern node.

Parameters:

Name Type Description Default
node_type type[AST]

The AST node class to match (e.g., ast.Assign).

required
**field_patterns Pattern | Any

Patterns or exact values to match against the node's fields.

{}
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, node_type: type[ast.AST], **field_patterns: Pattern | Any):
    """NodePattern node.

    Args:
        node_type: The AST node class to match (e.g., ast.Assign).
        **field_patterns: Patterns or exact values to match against the node's fields.
    """
    self.node_type = node_type
    self.field_patterns = field_patterns

Not

Bases: Pattern

Match any node that is not matched by pattern.

Parameters:

Name Type Description Default
pattern Pattern

The pattern that must fail for this to match.

required
Source code in src/ast_pattern_engine/nodes/basic.py
class Not(Pattern):
    """Match any node that is not matched by `pattern`.

    Args:
        pattern: The pattern that must fail for this to match.
    """

    pattern: Pattern

    def __init__(self, pattern: Pattern):
        """Not node.

        Args:
            pattern: The pattern that must fail for this to match.
        """
        self.pattern = pattern

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}

        # Use _match_patterns so that SequencePatterns (like OneOf) don't
        # raise NotImplementedError when their .match() is called directly.
        res = _match_patterns([self.pattern], [node], 0, {})

        if res:
            return None
        return bindings

__init__

__init__(pattern: Pattern)

Not node.

Parameters:

Name Type Description Default
pattern Pattern

The pattern that must fail for this to match.

required
Source code in src/ast_pattern_engine/nodes/basic.py
def __init__(self, pattern: Pattern):
    """Not node.

    Args:
        pattern: The pattern that must fail for this to match.
    """
    self.pattern = pattern

WildCard

Bases: Pattern

Matches any node.

Source code in src/ast_pattern_engine/nodes/basic.py
class WildCard(Pattern):
    """Matches any node."""

    def __init__(self): ...

    def match_node(
        self,
        node: Any,
        bindings: dict[str, Any] | None = None,
        *,
        _force_list: bool = False,
    ):
        bindings = bindings or {}
        return bindings

Sequence Nodes

Sequence nodes allow matching groups of adjacent nodes, similar to regex patterns.

ast_pattern_engine.nodes.sequences

OneOf

Bases: SequencePattern

Matches one of several patterns.

Can be set to be strict and only match if exactly one pattern matches. If not set to be strict, the first successful match is returned.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

The patterns to match

required
strict bool

Whether to be strict and only match if exactly one pattern matches

False
key str | None

Optional key to bind the matched pattern to

None
Source code in src/ast_pattern_engine/nodes/sequences.py
class OneOf(SequencePattern):
    """Matches one of several patterns.

    Can be set to be strict and only match if *exactly* one pattern matches. If not
    set to be strict, the first successful match is returned.

    Args:
        patterns: The patterns to match
        strict: Whether to be strict and only match if *exactly* one pattern matches
        key: Optional key to bind the matched pattern to
    """

    patterns: list[Pattern]
    strict: bool
    key: str | None

    def __init__(
        self, patterns: Sequence[Pattern], strict: bool = False, key: str | None = None
    ) -> None:
        """OneOf node.

        Args:
            patterns: The patterns to match
            strict: Whether to be strict and only match if *exactly* one pattern matches
            key: Optional key to bind the matched pattern to
        """
        self.patterns = list(patterns)
        self.strict = strict
        self.key = key

__init__

__init__(
    patterns: Sequence[Pattern],
    strict: bool = False,
    key: str | None = None,
) -> None

OneOf node.

Parameters:

Name Type Description Default
patterns Sequence[Pattern]

The patterns to match

required
strict bool

Whether to be strict and only match if exactly one pattern matches

False
key str | None

Optional key to bind the matched pattern to

None
Source code in src/ast_pattern_engine/nodes/sequences.py
def __init__(
    self, patterns: Sequence[Pattern], strict: bool = False, key: str | None = None
) -> None:
    """OneOf node.

    Args:
        patterns: The patterns to match
        strict: Whether to be strict and only match if *exactly* one pattern matches
        key: Optional key to bind the matched pattern to
    """
    self.patterns = list(patterns)
    self.strict = strict
    self.key = key

Optional

Bases: SequencePattern

Matches a pattern zero or one times.

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
key str | None

Optional key to bind the matched pattern to.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
class Optional(SequencePattern):
    """Matches a pattern zero or one times.

    Args:
        pattern: The pattern to match.
        key: Optional key to bind the matched pattern to.
    """

    pattern: Pattern
    key: str | None

    def __init__(self, pattern: Pattern, key: str | None = None) -> None:
        """Optional node.

        Args:
            pattern: The pattern to match.
            key: Optional key to bind the matched pattern to.
        """
        self.pattern = pattern
        self.key = key

__init__

__init__(pattern: Pattern, key: str | None = None) -> None

Optional node.

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
key str | None

Optional key to bind the matched pattern to.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
def __init__(self, pattern: Pattern, key: str | None = None) -> None:
    """Optional node.

    Args:
        pattern: The pattern to match.
        key: Optional key to bind the matched pattern to.
    """
    self.pattern = pattern
    self.key = key

PatternGroup

Bases: SequencePattern

Matches a compound pattern/pattern group to an AST node sequence.

Parameters:

Name Type Description Default
pattern Sequence[Pattern]

The compound pattern to match.

required
key str | None

Optional key to bind the matched pattern to.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
class PatternGroup(SequencePattern):
    """Matches a compound pattern/pattern group to an AST node sequence.

    Args:
        pattern: The compound pattern to match.
        key: Optional key to bind the matched pattern to.
    """

    pattern: Sequence[Pattern]
    key: str | None

    def __init__(self, pattern: Sequence[Pattern], key: str | None = None) -> None:
        """PatternGroup node.

        Args:
            pattern: The compound pattern to match.
            key: Optional key to bind the matched pattern to.
        """
        self.pattern = list(pattern)
        self.key = key

__init__

__init__(
    pattern: Sequence[Pattern], key: str | None = None
) -> None

PatternGroup node.

Parameters:

Name Type Description Default
pattern Sequence[Pattern]

The compound pattern to match.

required
key str | None

Optional key to bind the matched pattern to.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
def __init__(self, pattern: Sequence[Pattern], key: str | None = None) -> None:
    """PatternGroup node.

    Args:
        pattern: The compound pattern to match.
        key: Optional key to bind the matched pattern to.
    """
    self.pattern = list(pattern)
    self.key = key

Repetition

Bases: SequencePattern

Matches a single pattern zero or more times.

Also supports specifying min and max match count thresholds

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
min_matches int

Minimum number of matches required. Default is 1.

1
max_matches int | None

Maximum number of allowed matches. Defaults to None.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
class Repetition(SequencePattern):
    """Matches a single pattern zero or more times.

    Also supports specifying min and max match count thresholds

    Args:
        pattern: The pattern to match.
        min_matches: Minimum number of matches required. Default is 1.
        max_matches: Maximum number of allowed matches. Defaults to None.
    """

    pattern: Pattern
    min_matches: int
    max_matches: int | None

    def __init__(
        self,
        pattern: Pattern,
        min_matches: int = 1,
        max_matches: int | None = None,
    ):
        """Repetition node.

        Args:
            pattern: The pattern to match.
            min_matches: Minimum number of matches required. Default is 1.
            max_matches: Maximum number of allowed matches. Defaults to None.
        """
        self.pattern = pattern
        self.min_matches = min_matches
        self.max_matches = max_matches

__init__

__init__(
    pattern: Pattern,
    min_matches: int = 1,
    max_matches: int | None = None,
)

Repetition node.

Parameters:

Name Type Description Default
pattern Pattern

The pattern to match.

required
min_matches int

Minimum number of matches required. Default is 1.

1
max_matches int | None

Maximum number of allowed matches. Defaults to None.

None
Source code in src/ast_pattern_engine/nodes/sequences.py
def __init__(
    self,
    pattern: Pattern,
    min_matches: int = 1,
    max_matches: int | None = None,
):
    """Repetition node.

    Args:
        pattern: The pattern to match.
        min_matches: Minimum number of matches required. Default is 1.
        max_matches: Maximum number of allowed matches. Defaults to None.
    """
    self.pattern = pattern
    self.min_matches = min_matches
    self.max_matches = max_matches