exec()

Execute arbitrary Python STATEMENTS from a string. Never on untrusted input. Rarely the right tool.

Built-in functionPython 3.0+
Common call
exec("x = 1")
Returns
None — side effects only
Replaces
nothing — usually you should NOT reach for exec
Watch out
runs ARBITRARY Python; side effects on the caller's scope; no return value
exec(sourcesourceA string containing Python statements (or a compiled code object). Can be a multi-line block — assignments, def, class, import, control flow.type: str | code · required, globalsglobalsOptional globals dict for execution. If None, uses the caller's globals.type: dict · default: None=None, localslocalsOptional locals dict. Defaults to the globals dict.type: dict · default: None=None)
None

Parameters

NameTypeRequiredDescription
sourcestr | codeyesA string containing Python statements (or a compiled code object). Can be a multi-line block — assignments, def, class, import, control flow.
globalsdictno (None)Optional globals dict for execution. If None, uses the caller's globals.
localsdictno (None)Optional locals dict. Defaults to the globals dict.

Return value

NoneAlways returns None. exec RUNS Python statements — assignment, def, class, import, control flow — with side effects in the given namespace. If globals / locals are omitted, uses the caller's scope.

Common patterns

Dynamic class or function generation (framework code)
Occasionally used by ORMs and dataclass-like frameworks.
body = f"def __init__(self, {args}):\n    " + inits
exec(body, ns)
Trusted config that includes Python
Some tools (Django settings, IPython config) are Python files exec'd as a config.
with open("settings.py") as f:
    exec(f.read(), config_globals)
For DATA, do not exec
Use JSON, YAML, TOML, or a real config format — never exec a data string.
import json
config = json.loads(text)
For dispatch, use a dict of callables
exec is almost never needed for dynamic function selection.
HANDLERS = {"add": handle_add}
HANDLERS[cmd](*args)

Examples

1. Assignment
exec("x = 1") x
Returns
1
2. Custom namespace
ns = {} exec("x = 42", ns) ns["x"]
Returns
42
3. Multi-line
exec("a = 1\nb = 2\nprint(a + b)")
Returns
3 # printed
4. Returns None
result = exec("x = 1")
Returns
None # always
5. Function def
exec("def f(): return 42") f()
Returns
42
6. Untrusted RCE
exec(user_input)
Returns
DANGER: arbitrary code execution

Pitfalls

1. exec is ARBITRARY CODE EXECUTION — never on untrusted input
Everything eval's warning says goes double for exec. eval at least tries to be an expression evaluator; exec runs anything. On user input, you have handed the attacker a Python interpreter.
Attacker's dream
exec(request.form["config"])
the attacker owns your process
Never do this
use a real config format
2. exec ALWAYS returns None
A common expectation from other languages: "the last expression is the return". Not in Python. exec returns None; get results by reading the namespace it wrote into.
Assumed return
x = exec("42")
x is None
Read the namespace
ns = {}
exec("result = 42", ns)
x = ns["result"]
42
3. Inside a function, exec cannot modify local variables directly
Python compiles function locals into a fixed slot layout. exec writes into a namespace dict; the compiler does not know about it, so the function's named locals are unaffected.
Locals unchanged
def f():
    exec("x = 1")
    return x
NameError: name x is not defined
Use a namespace
def f():
    ns = {}
    exec("x = 1", ns)
    return ns["x"]
1
4. Rarely the right tool — reach for it last
If you find yourself wanting exec, first check: can I use a dict of callables? An ast walk? A real parser? importlib for dynamic imports? These are almost always safer and clearer.
Reflex reach for exec
exec(dynamic_code)
usually avoidable
Structured alternative
HANDLERS[key](args)
safer

When to use

Use it
  • Framework code that generates classes or functions from templates (dataclasses, ORMs)
  • Trusted config files that ARE Python (Django settings, IPython)
  • REPL implementations where you WANT arbitrary Python
  • Almost nothing else
Reach for something else
  • Any input from users, network, or files → do NOT exec
  • Data serialization → JSON / pickle / dataclasses
  • Dynamic dispatch → dict of callables
  • Dynamic imports → importlib.import_module

Notes

Complexity
Compile + execution — variable
Return
None
CPython impl
Python/bltinmodule.c :: builtin_exec
Memory
Allocates code objects; side effects on namespace
Thread-safe
Depends entirely on the executed code

FAQ

eval takes an EXPRESSION and returns its value. exec takes STATEMENTS (arbitrary Python code) and returns None. Use eval when you need a value; use exec when you need side effects on a namespace.

History

3.0
exec became a function (was a statement in Python 2).