exec
eval
Python
code execution
programming tips

How do I execute a string containing Python code in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python can execute code stored in a string, but that capability should be treated as a sharp tool. The language gives you exec for statements and eval for expressions, yet both functions can execute arbitrary code if you pass untrusted input. In practice, the hardest part of this task is not the syntax. It is deciding whether code execution is really the right solution.

Use eval Only for Simple Expressions

eval evaluates a single Python expression and returns its result.

python
expression = "2 + 3 * 4"
result = eval(expression)
print(result)

Output:

python
14

This is limited to expressions, not statements. You cannot define functions, run loops, or assign variables with eval. That limitation is useful because it narrows the surface area slightly, but it does not make eval safe for untrusted input.

Use exec for Statements or Multi-Line Code

If the string contains assignments, function definitions, or multiple lines, use exec.

python
1source = """
2total = 0
3for n in range(5):
4    total += n
5"""
6
7scope = {}
8exec(source, scope)
9print(scope["total"])

Output:

python
10

exec returns None, so if you need a result you usually place it in a namespace dictionary and read it back afterward. Passing an explicit dictionary makes the data flow easier to inspect than letting executed code mutate your module globals.

Control the Namespace Explicitly

Both exec and eval accept global and local namespace dictionaries. That lets you constrain what names are visible during execution.

python
1expression = "value * scale"
2globals_dict = {"__builtins__": {}}
3locals_dict = {"value": 7, "scale": 3}
4
5result = eval(expression, globals_dict, locals_dict)
6print(result)

This prevents direct access to normal built-ins, but it is not a full security boundary. Python execution is flexible enough that sandboxing with bare dictionaries is not reliable against malicious input. Namespace control is useful for predictability, not for true isolation.

Prefer Safer Alternatives When You Can

Many uses of eval and exec are really parsing problems in disguise. If the string is data, parse it as data. If it is a configuration, use a configuration format. If it is a mathematical expression, use a constrained parser.

For example, if the input is meant to be a Python literal such as a list or dictionary, ast.literal_eval is far safer than eval.

python
1import ast
2
3text = "[1, 2, 3]"
4value = ast.literal_eval(text)
5print(value)

literal_eval can handle strings, numbers, tuples, lists, dictionaries, booleans, and None, but not arbitrary executable code. That is exactly why it is often the better answer.

Compile Once for Repeated Execution

If the same dynamic code will run many times, compile it once and then execute the compiled object. That separates parsing from execution and avoids repeated compilation overhead.

python
1code = compile("x * 10 + 1", "<dynamic>", "eval")
2
3for x in [1, 2, 3]:
4    print(eval(code, {"__builtins__": {}}, {"x": x}))

This is useful in rule engines or plugin-like systems where the input is trusted and reused.

Decide Whether Dynamic Execution Is Justified

Good reasons to execute a string are relatively rare:

  1. A trusted plugin system.
  2. Interactive educational tooling.
  3. Controlled expression evaluation in internal tools.

Weak reasons include avoiding a real parser, treating configuration as code, or executing user input from a web request. If the input came from outside your trust boundary, executing it directly is usually a design error.

Common Pitfalls

  • Using eval or exec on untrusted input and treating a restricted namespace as complete security.
  • Reaching for dynamic execution when ast.literal_eval, JSON, or a real configuration format would solve the problem safely.
  • Expecting exec to return a value directly instead of reading results from a namespace dictionary.
  • Mixing executed code with module globals and making side effects difficult to track.
  • Forgetting that eval only supports expressions while exec handles statements and multi-line code.

Summary

  • Use eval for simple expressions and exec for statements or multi-line code.
  • Pass explicit namespace dictionaries so execution does not leak unpredictably into module state.
  • Prefer safer parsing tools such as ast.literal_eval when the input is data rather than code.
  • Compile reused dynamic code once when performance matters.
  • Never treat raw execution of untrusted strings as a safe default.

Course illustration
Course illustration

All Rights Reserved.