ast
The ast module lets you parse Python source code into an Abstract Syntax Tree (AST). Each node in the tree represents a construct in the source code — expressions, statements, functions, classes, and so on. This is the same parsing that Python’s compiler uses internally, so you can inspect and transform code programmatically.
Main Functions
ast.parse()
Parses source code into an AST. This is the entry point for most ast operations.
import ast
source = """
def greet(name):
return f"Hello, {name}!"
"""
tree = ast.parse(source)
print(ast.dump(tree))
# Module(body=[FunctionDef(name='greet', args=arguments(...), body=[Return(...)])])
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
source | str | — | The Python source code to parse |
mode | str | "exec" | Parse mode: "exec" for module, "eval" for expressions, "single" for interactive |
filename | str | "<unknown>" | Filename for error reporting |
type_comments | bool | False | Whether to parse type comments |
Returns: A Module node containing the parsed AST.
ast.literal_eval()
Safely evaluates a string containing a Python literal or container display. Unlike eval(), this only allows literals — numbers, strings, lists, tuples, dicts, sets, booleans, and None.
import ast
# Safe evaluation of literals
result = ast.literal_eval("[1, 2, {'a': 3}]")
print(result)
# [1, 2, {'a': 3}]
# This would be dangerous with eval, but safe with literal_eval
json_string = '{"key": [1, 2, 3], "nested": {"x": true}}'
data = ast.literal_eval(json_string)
print(data)
# {'key': [1, 2, 3], 'nested': {'x': True}}
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
node_or_string | str | — | A string containing a Python literal |
Returns: The evaluated Python object.
ast.walk()
Generates all nodes in the tree, recursively, including the node itself.
import ast
source = """
def example():
x = 1 + 2
y = [i for i in range(10)]
return x + sum(y)
"""
tree = ast.parse(source)
for node in ast.walk(tree):
print(type(node).__name__)
# Module, FunctionDef, arguments, arg, Assign, AugAssign, Name, Constant, comprehension...
ast.dump()
Returns a formatted string representation of the AST. Useful for debugging.
import ast
tree = ast.parse("x = 1 + 2")
print(ast.dump(tree, indent=2))
# Module(
# body=[
# Assign(
# targets=[Name(id='x', ctx=Store())],
# value=BinOp(
# left=Constant(value=1),
# op=Add(),
# right=Constant(value=2)
# )
# )
# ]
# )
ast.fix_missing_locations()
Populates nodes that have lineno and col_offset set to None. Useful when you modify or create AST nodes and want them to have valid line numbers.
import ast
# Create a simple expression
expr = ast.Expression(body=ast.Constant(value=42))
# Fix missing locations
ast.fix_missing_locations(expr)
# Now we can compile it
code = compile(expr, '<string>', 'eval')
print(eval(code))
# 42
ast.increment_lineno()
Increments the line numbers of all nodes by a given offset. Useful when generating code from snippets.
import ast
source = "x = 1\ny = 2"
tree = ast.parse(source)
# Increment line numbers by 10
ast.increment_lineno(tree, 10)
print(tree.body[0].lineno) # 11
print(tree.body[1].lineno) # 12
Common Patterns
Inspecting function definitions
import ast
source = """
def add(a, b):
'''Add two numbers'''
return a + b
"""
tree = ast.parse(source)
func = tree.body[0]
print(f"Function name: {func.name}")
print(f"Arguments: {[arg.arg for arg in func.args.args]}")
print(f"Docstring: {ast.get_docstring(func)}")
# Function name: add
# Arguments: ['a', 'b']
# Docstring: Add two numbers
Finding all function calls
import ast
source = """
def process():
result = calculate(x, y)
print(result)
return result * 2
"""
class FunctionCallFinder(ast.NodeVisitor):
def visit_Call(self, node):
print(f"Found call: {ast.unparse(node)}")
self.generic_visit(node)
tree = ast.parse(source)
finder = FunctionCallFinder()
finder.visit(tree)
# Found call: calculate(x, y)
# Found call: print(result)
# Found call: result * 2
Transforming code programmatically
import ast
source = "x = 1 + 2"
class AddToMultiply(ast.NodeTransformer):
def visit_BinOp(self, node):
if isinstance(node.op, ast.Add):
node.op = ast.Mult()
return self.generic_visit(node)
tree = ast.parse(source)
transformed = AddToMultiply().visit(tree)
# Compile and run the transformed code
code = compile(ast.fix_missing_locations(ast.Module(body=transformed.body)), '<string>', 'exec')
exec(code)
print(x) # 2 (1 * 2 instead of 1 + 2)
Validating syntax without executing
import ast
def is_valid_python(code: str) -> bool:
try:
ast.parse(code)
return True
except SyntaxError:
return False
print(is_valid_python("x = 1 + 2")) # True
print(is_valid_python("x = 1 +")) # False
AST Node Classes
The ast module defines many node classes. Common ones include:
| Node | Represents |
|---|---|
Module | A complete module |
FunctionDef | A function definition |
AsyncFunctionDef | An async function definition |
ClassDef | A class definition |
Assign | An assignment statement |
AugAssign | An augmented assignment (+=, -=, etc.) |
Return | A return statement |
Call | A function call |
Name | A variable name |
Constant | A literal value |
BinOp | A binary operation (+, -, *, /) |
UnaryOp | A unary operation (-, +, not) |
If | An if statement |
For | A for loop |
While | A while loop |