Python compile(): Compiling Source Code & AST into Bytecode Objects

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().

1. Syntax & Formal Parameter Specifications 🔝

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).

2. The Three Compilation Modes: 'exec', 'eval', and 'single' 🔝

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'

3. Performance Boost: Loop Optimization via Precompilation 🔝

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)

4. Compiling Abstract Syntax Tree (AST) Objects 🔝

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!

5. Compiler Flags & Optimization Levels 🔝

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,)

6. Bytecode Disassembly & Code Object Introspection 🔝

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

7. Security Sandboxing, Injection Risks & AST Validation 🔝

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

8. Common Pitfalls, Mode Traps & Debugging 🔝

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

9. Interactive Notebook & Practice Exercises 🔝

Practice dynamic compilation modes, loop benchmarking, and AST validation in our interactive companion Jupyter Notebook:

Interactive Companion Jupyter Notebook

Explore interactive code cells, benchmark loop speedups, inspect disassembled bytecode with dis.dis(), and run self-validating assertions.

View on GitHub Run in Google Colab

Self-Verifying Assertion Suite

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!

10. Frequently Asked Questions (FAQ) 🔝

What is the advantage of compile()?

It parses code into bytecode once. Repeated execution via eval() or exec() skips recurring parsing overhead.

Does compile() run the code?

No. It only produces a bytecode object. To execute it, pass the code object to exec() or eval().

Why provide a filename?

The filename string is recorded in tracebacks and error frames, making debugging runtime errors straightforward.

When are syntax errors caught?

compile() raises SyntaxError immediately at compilation time, preventing malformed code from executing.

Further Reading & Related Tutorials 🔝




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer