5.34. Dynamic code

Three built-ins take a string of Python source and run it: eval(), exec(), and compile(). Together they let code construct and execute more code at runtime – which is occasionally exactly the right tool, and far more often a source of bugs and security holes.

Warning

These functions execute arbitrary Python. Passing user input to eval or exec – a string from a file the user can edit, a payload received over the network, a value typed at a REPL prompt – lets that input do anything the calling script could do, up to and including deleting every file on the device. Use them deliberately, never inside code that runs automatically, and never on data you do not control.

5.34.1. eval

eval() runs a single expression and returns its value:

>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'

The expression sees the caller’s globals and locals by default, which is why name resolves in the second example. Passing explicit dictionaries lets you sandbox the evaluation:

eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})

Even sandboxed, eval is dangerous. There are well-known techniques to escape such sandboxes; do not rely on the empty __builtins__ trick alone for untrusted input.

5.34.2. exec

exec() runs a block of code rather than a single expression – statements, function definitions, loops – and returns None:

exec("for i in range(3): print(i)")

Output:

0
1
2

The block can define names that become available afterwards, with some caveats about local vs global scope. exec inside a function rarely behaves the way the writer expects; if you need it, run it at module level.

5.34.3. compile

compile() turns a source string into a code object that can be passed to eval() or exec() later. Use it when the same source will run many times – the parsing happens once, the execution is faster:

expr = compile("x * x", "<expr>", "eval")
for x in range(5):
    print(eval(expr))

Output:

0
1
4
9
16

The middle argument is a label that appears in tracebacks if the code raises. The third argument is "eval" for a single expression, "exec" for a block, or "single" for an interactive-style statement that prints its result.

5.34.4. When to reach for these

Almost never. Most use cases beginners imagine have safer alternatives:

  • Reading a config file. Use json – structured data, no execution.

  • Evaluating a numeric value typed by the user. Use int() / float() to parse, then arithmetic. If the user really needs to enter a formula, use a small expression parser, not eval.

When you do need eval / exec / compile, isolate the call site, log the exact string that is about to be executed, and treat the source as the most suspicious thing in your code.