vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns str() in Python 2026: String Conversion + Modern Formatting & Best Practices type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns super() in Python 2026: Method Resolution & Modern Inheritance Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices round() in Python 2026: Rounding Numbers + Modern Precision & Use Cases reversed() in Python 2026: Reverse Iteration + Modern Patterns & Best Practices repr() in Python 2026: Official String Representation + Modern Debugging & Serialization Use Cases pow() in Python 2026: Power & Modular Exponentiation + Modern Use Cases & Best Practices property() in Python 2026: Properties, Getters/Setters & Modern Patterns ord() in Python 2026: Unicode Code Point from Character + Modern Use Cases & Best Practices open() in Python 2026: File Handling + Modern I/O Patterns & Best Practices range() in Python 2026: Efficient Sequence Generation + Modern Patterns & Best Practices print() in Python 2026: Output Formatting + Modern CLI & Debugging Patterns View all in this category →

exec() in Python 2026: Dynamic Statement Execution + Security Risks & Safe Alternatives

Built In Function Mar 07, 2023

exec() in Python 2026: Dynamic Statement Execution + Security Risks & Safe Alternatives

The built-in exec() function dynamically executes Python statements (code blocks) from a string or code object. In 2026 it remains a powerful metaprogramming tool for dynamic script loading, configuration-driven execution, REPLs, code generation, and plugin systems — but like eval(), it is extremely dangerous when used with untrusted input due to arbitrary code execution risks.

With Python 3.12–3.14+ offering improved AST support, better restricted execution options, and heightened security awareness (restrictedpython, sandboxing, subprocess isolation), exec() is used far more restrictively. This March 23, 2026 update explains how exec() works today, real-world (trusted) use cases, severe security warnings, performance notes, and modern safe alternatives.

TL;DR — Key Takeaways 2026

  • exec(source, globals=None, locals=None) → executes string/code object as Python statements
  • 2026 security rule: NEVER use exec() on untrusted input — full code injection risk
  • Safe alternative: ast.literal_eval() for literals, restrictedpython for restricted code, subprocess for isolation
  • Main safe use cases: trusted config scripts, metaprogramming, REPLs, code generation (trusted sources only)
  • Performance: Compile once with compile(), exec() many times for speed

1. Basic Usage — Executing Statements


# Simple statement execution
code = "x = 10\nprint(x * 2)"
exec(code)
# Output: 20

# With custom namespace (safer)
namespace = {}
exec("def greet(): print('Hello 2026')", namespace)
namespace["greet"]()  # Hello 2026

2. Real-World Patterns in 2026 (Trusted Sources Only!)

Trusted Configuration Script Execution


def load_script_config(script: str) -> dict:
    # WARNING: Only use on trusted scripts!
    safe_globals = {"__builtins__": {}, "print": print}
    local = {}
    exec(script, safe_globals, local)
    return local

# Trusted example (from your own file)
script = "result = 42\nname = 'Alice'"
config = load_script_config(script)
print(config)  # {'result': 42, 'name': 'Alice'}

Dynamic Class / Function Generation


import builtins

def create_class(class_name: str, base_class):
    code = f"class {class_name}({base_class.__name__}):\n    def run(self): print('Dynamic!')"
    namespace = {}
    # a class statement needs __build_class__ and __name__, and run() needs print
    restricted = {"__build_class__": builtins.__build_class__, "print": print}
    exec(code, {"__builtins__": restricted, "__name__": "generated", base_class.__name__: base_class}, namespace)
    return namespace[class_name]

Dynamic = create_class("Dynamic", object)
Dynamic().run()  # Dynamic!

3. Security Risks & Modern Alternatives in 2026

Approach Security Level Use Case Recommendation 2026
exec(code)Very low (arbitrary code exec)Trusted code onlyAvoid with user input
ast.literal_eval()High (safe literals only)Simple data structuresPreferred for config parsing
restrictedpythonMedium–HighRestricted dynamic codeUse for semi-trusted plugins
subprocess / separate processVery highUntrusted codeSafest for unknown sources

4. Best Practices & Performance in 2026

  • Never exec untrusted input — use ast.literal_eval() or restrictedpython
  • Restrict namespace — always pass {"__builtins__": {}} or safe globals
  • Type hints 2026:
    
    from typing import Any, Dict
    
    def safe_exec(code_str: str, safe_globals: Dict[str, Any]) -> Dict[str, Any]:
        local: Dict[str, Any] = {}
        exec(compile(code_str, "<safe>", "exec"), {"__builtins__": {}} | safe_globals, local)
        return local
    
  • Performance: compile() once, exec() multiple times — significant speedup for repeated code
  • Free-threading (3.14+): Code objects immutable — safe to share; use locks for namespace modification

Worked Example: You Often Do Not Need exec()

Most dynamic-code use cases have a safer, clearer built-in alternative. Classes can be created with type(name, bases, namespace), functions can be chosen from a dictionary of callables, and attributes can be set with setattr(). These approaches never parse strings as code, so they cannot be abused through injection, and linters and type checkers can still understand them.

def run(self):
    return f"{type(self).__name__} running"

Dynamic = type("Dynamic", (object,), {"run": run, "version": 2})
d = Dynamic()
print(d.run(), d.version)              # Dynamic running 2

ACTIONS = {
    "upper": str.upper,
    "title": str.title,
    "strip": str.strip,
}
for name in ["title", "upper", "delete_everything"]:
    action = ACTIONS.get(name)
    print(name, "->", action("  hello pyinns ") if action else "unknown action")

ns = {}
exec("total = sum(range(5))", {"__builtins__": {"sum": sum, "range": range}}, ns)
print(ns)                              # {'total': 10}

The final line shows that exec() returns None: results must be read from the namespace dictionary you pass in.

Common Mistakes

  • Running untrusted code with exec(), even with a restricted namespace
  • Expecting exec() inside a function to create local variables: pass an explicit dict
  • Building code with f-strings from user input: an injection risk

Quick Summary

  • exec() runs statements and returns None
  • Use explicit namespace dicts to collect results
  • Prefer type(), dispatch dicts and setattr() over generated code

Conclusion — exec() in 2026: Powerful but Highly Restricted

exec() enables full dynamic statement execution — useful for trusted metaprogramming, configuration scripts, and REPLs — but it is one of Python’s highest security risks when misused. In 2026, restrict it to trusted sources, prefer ast.literal_eval() for literals, restrictedpython for semi-trusted code, and subprocess for isolation. When used correctly, it’s a clean way to run dynamic logic at runtime.

Next steps:

  • Audit your codebase for exec() — replace untrusted uses immediately

Related Tutorials

Share:
Last updated: March 2023
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices