A powerful, programmatic, regex-inspired AST pattern matching and manipulation library for Python.
Instead of relying on fragile regex on source code or magic string-to-AST parsers, ast_pattern_engine provides an internal DSL for building explicit, structural patterns.
It is designed with a "Gradual Pipeline" philosophy: instead of writing one massive, monolithic pattern expression to do everything at once, you chain small, focused patterns and visitors. Like casting a wide net and progressively filtering down in stages.
pip install ast_pattern_engineuv add ast_pattern_engine(Requires Python 3.10+)
Here is a simple pipeline that rewrites dict.get("key") calls into direct subscript access dict["key"]:
from typing import Any
import ast
from ast_pattern_engine import BottomUpPatternTransformer, Bind, NodePattern
source = "value = my_dict.get(other_dict.get('foo'))"
tree = ast.parse(source)
# 1. Build the explicit structural pattern
# Matches: <obj>.get(<key>)
pattern = [
NodePattern(
ast.Call,
func=NodePattern(ast.Attribute, attr="get", value=Bind("obj")),
args=Bind("key"),
)
]
# 2. Define the rewrite logic
def rewrite_dict_get(bindings: dict[str, Any]) -> list[ast.AST]:
obj = bindings["obj"]
key = bindings["key"][0] # args is a list
# Return the new node to replace the matched node
new_node = ast.Subscript(value=obj, slice=key, ctx=ast.Load())
return [new_node]
# 3. Apply the transformer
# We use BottomUpPatternTransformer so nested `.get()` calls
# are safely transformed from the inside-out.
transformer = BottomUpPatternTransformer(pattern, {"key": rewrite_dict_get})
transformer.visit(tree)
print(ast.unparse(tree))
# Output: value = my_dict[other_dict['foo']](See the examples/ directory for full runnable code).
The engine provides several primitives to build robust sequences:
NodePattern: Match specific AST node types and assert on their fields.Collect/Bind: Extract sub-trees out of a matched pattern to use in your handlers.OneOf: Match one of several possible patterns (similar to regex|).Repetition: Match a pattern sequentially 1 or more times (similar to regex*and+).Optional: Match a pattern 0 or 1 times (similar to regex?).Filter: Apply arbitrary Python lambdas to check node states during matching.
To reduce boilerplate when building patterns, the library includes a templates module with helpers for common operations:
match_call(func_name, **kwargs)match_assign(target_name, value)match_in_expr(pattern)