The compile() built-in function converts Python source strings, bytes, or Abstract Syntax Tree (AST) objects into executable code objects.
Precompilation & Runtime Performance: Compiling dynamic code ahead of time eliminates repeated lexical parsing when running expressions repeatedly inside loops or web requests via eval() or exec(). To inspect runtime execution namespaces, see globals() and locals(), or inspect object representations with repr().
The compile() function takes up to six arguments, with the first three being mandatory:
# Formal syntax of built-in compile()
code_obj = compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
| Parameter | Type | Default | Operational Role |
|---|---|---|---|
source |
str, bytes, AST |
Required | Source text, byte sequence, or ast.AST node to compile into bytecode. |
filename |
str or bytes |
Required | Filename string recorded in tracebacks and debugging stack frames (e.g., "<string>"). |
mode |
str |
Required | Grammar mode: 'exec' (statements), 'eval' (single expression), or 'single' (REPL). |
flags |
int |
0 |
Bitwise compiler flags (e.g., ast.PyCF_ONLY_AST). |
dont_inherit |
bool |
False |
If True, suppresses inherited compiler future flags from the calling context. |
optimize |
int |
-1 |
Optimization level: -1 (default), 0 (none), 1 (no asserts), 2 (no asserts or docstrings). |
Selecting the correct mode informs Python's grammar parser how to interpret the code structure:
| Mode | Accepted Code Structure | Execution Runner | Return Value Behavior |
|---|---|---|---|
'exec' |
Arbitrary statement sequences, loops, definitions, imports | exec() |
Always returns None; modifies target namespaces. |
'eval' |
A single isolated Python expression | eval() |
Returns evaluated result of calculated expression. |
'single' |
A single interactive statement (like a REPL prompt) | exec() |
Evaluates statement and auto-prints non-None results. |
The following example demonstrates each compilation mode in action:
# 1. 'exec' mode: statement sequences
code_exec = compile("x = sum([10, 20, 30])\nprint('Sum:', x)", "<calc>", "exec")
exec(code_exec)
# 2. 'eval' mode: single expression returning a value
code_eval = compile("(15 * 4) + (100 // 5) - 8", "<math>", "eval")
print("Evaluated expression:", eval(code_eval))
# 3. 'single' mode: interactive statements (auto-prints non-None)
code_single = compile("'plus2net'.upper()", "<repl>", "single")
print("Interactive statement:")
exec(code_single)Terminal Output:
Sum: 60
Evaluated expression: 72
Interactive statement:
'PLUS2NET'
Repeatedly passing string expressions into eval() inside a loop forces Python to re-tokenize, parse, and compile the AST on every cycle. Precompiling with compile() once generates bytecode that executes significantly faster:
# 4. Loop optimization: raw eval() vs. precompiled bytecode
import time
expr, n = "x ** 2 + 3 * x + 5", 50000
t0 = time.perf_counter()
for x in range(n):
_ = eval(expr)
raw_t = time.perf_counter() - t0
compiled_expr = compile(expr, "<bench>", "eval")
t1 = time.perf_counter()
for x in range(n):
_ = eval(compiled_expr)
comp_t = time.perf_counter() - t1
print(f"Raw eval: {raw_t:.4f} s")
print(f"Precompiled: {comp_t:.4f} s ({raw_t / comp_t:.1f}x faster)")Terminal Output:
Raw eval: 0.1420 s
Precompiled: 0.0231 s (6.1x faster)
compile() accepts ast.AST tree structures produced by ast.parse(). This enables inspecting or modifying program nodes prior to code generation:
# 5. Compiling Abstract Syntax Tree (AST) objects
import ast
tree = ast.parse("print('Executed via AST compilation!')", mode="exec")
code_from_ast = compile(tree, filename="<ast_demo>", mode="exec")
exec(code_from_ast)Terminal Output:
Executed via AST compilation!
The optimize parameter controls bytecode optimization: optimize=1 strips out assert statements, while optimize=2 strips both assertions and docstrings:
# 6. Optimization levels: stripping asserts and docstrings
src = '''
def check(val):
assert val > 0, "positive only"
'''Docstring summary.'''
return val * 2
'''
opt0 = compile(src, "<opt0>", "exec", optimize=0)
opt2 = compile(src, "<opt2>", "exec", optimize=2)
print("opt0 constants:", opt0.co_consts)
print("opt2 constants:", opt2.co_consts)Terminal Output:
opt0 constants: (None, 'Docstring summary.')
opt2 constants: (None,)
Code objects contain rich inspection attributes such as co_consts, co_names, and co_filename. You can disassemble instructions using the standard dis module:
# 7. Code object inspection and bytecode disassembly
import dis
code_obj = compile("a * b + 10", "<inspect>", "eval")
print("Referenced names:", code_obj.co_names)
print("Constants: ", code_obj.co_consts)
print("\nBytecode Instructions:")
dis.dis(code_obj)Terminal Output:
Referenced names: ('a', 'b')
Constants: (10,)
Bytecode Instructions:
1 0 LOAD_NAME 0 (a)
2 LOAD_NAME 1 (b)
4 BINARY_MULTIPLY
6 LOAD_CONST 0 (10)
8 BINARY_ADD
10 RETURN_VALUE
compile() with eval() or exec(). Passing an empty {"__builtins__": {}} scope is insufficient because attackers can traverse object classes to access dangerous built-ins.
To safely evaluate user math expressions, validate the AST against an allowed node whitelist before compilation:
# 8. Safe expression evaluation via AST whitelist
import ast
def safe_math(expr: str, scope: dict) -> float:
tree = ast.parse(expr, mode='eval')
for node in ast.walk(tree):
if not isinstance(node, (ast.Expression, ast.BinOp, ast.UnaryOp,
ast.Constant, ast.Name, ast.Load,
ast.Add, ast.Sub, ast.Mult, ast.Div)):
raise ValueError(f"Blocked node: {type(node).__name__}")
return eval(compile(tree, "<safe>", "eval"), {"__builtins__": {}}, scope)
print("Safe result:", safe_math("price * qty + 5", {"price": 20, "qty": 3}))
try:
safe_math("__import__('os').system('ls')", {})
except ValueError as err:
print("Security block:", err)Terminal Output:
Safe result: 65
Security block: Blocked node: Call
Because compile() verifies syntax before code execution begins, syntax errors are caught cleanly. Setting a meaningful filename provides pinpoint precision in error tracebacks:
# 9. Compile-time syntax checking & traceback attribution
try:
compile("def bad_syntax( 123 ): pass", filename="plugin.py", mode="exec")
except SyntaxError as e:
print(f"Compile catch -> {e.filename}:{e.lineno}: {e.msg}")
try:
compile("x = 42", filename="<eval_check>", mode="eval")
except SyntaxError as e:
print("Mode error: ", e.msg)Terminal Output:
Compile catch -> plugin.py:1: invalid syntax
Mode error: invalid syntax
Practice dynamic compilation modes, loop benchmarking, and AST validation in our interactive companion Jupyter Notebook:
Explore interactive code cells, benchmark loop speedups, inspect disassembled bytecode with dis.dis(), and run self-validating assertions.
Run these unit checks directly in Python or your local environment to verify compile() mechanics across all modes and optimization levels:
# 10. Self-verifying unit test suite for compile()
import ast
# 1. 'exec' mode namespace mutations
ns = {}
exec(compile("val = 10 + 20", "<test>", "exec"), ns)
assert ns["val"] == 30, "exec mode failed"
# 2. 'eval' mode expression return
assert eval(compile("len([1, 2, 3]) * 4", "<test>", "eval")) == 12
# 3. AST compilation and execution
tree = ast.parse("y = 100")
ns_ast = {}
exec(compile(tree, "<ast>", "exec"), ns_ast)
assert ns_ast["y"] == 100
# 4. optimize=2 strips docstrings
c_doc = compile("'''docstring'''\nx = 1", "<opt>", "exec", optimize=2)
assert "docstring" not in c_doc.co_consts
print("All compile() unit assertions successfully verified!")Terminal Output:
All compile() unit assertions successfully verified!
It parses code into bytecode once. Repeated execution via eval() or exec() skips recurring parsing overhead.
The filename string is recorded in tracebacks and error frames, making debugging runtime errors straightforward.
compile() raises SyntaxError immediately at compilation time, preventing malformed code from executing.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.