eval() in Python 2026: Dynamic Expression Evaluation + Security Risks & Safe Alternatives
The built-in eval() function evaluates a string as a Python expression and returns the result. In 2026 it remains one of the most powerful — and most dangerous — built-ins, used for dynamic calculation, configuration parsing, interactive shells, and some metaprogramming scenarios. However, its ability to execute arbitrary code makes it a top security risk when handling untrusted input.
With Python 3.12–3.14+ offering better AST parsing, improved restricted execution tools, and growing security best practices (restrictedpython, ast.literal_eval, sandboxing), eval() is used far more cautiously. This March 23, 2026 update explains how eval() works today, real-world (trusted) use cases, severe security warnings, performance notes, and modern safe alternatives.
TL;DR — Key Takeaways 2026
eval(expression, globals=None, locals=None)→ executes string as Python expression- 2026 security rule: NEVER use eval() on untrusted input — remote code execution risk
- Safe alternative:
ast.literal_eval()for simple literals (int, str, list, dict…) - Main safe use cases: trusted config expressions, interactive REPLs, calculator apps
- Performance: Fast for small expressions — cache compiled code objects if reused
1. Basic Usage — Evaluating Expressions
x = 10
print(eval("x * 2 + 5")) # 25
# With custom globals/locals (safer)
safe_globals = {"__builtins__": {}, "x": x}
print(eval("x * 3", safe_globals)) # 30
2. Real-World Patterns in 2026 (Trusted Input Only!)
Trusted Config Expression Evaluation
import math
def load_expression(expr: str) -> float:
# WARNING: Only use on trusted config!
safe_globals = {"__builtins__": {}, "pi": math.pi, "sin": math.sin}
return eval(expr, safe_globals)
# Trusted example (from your own config)
result = load_expression("sin(pi/2) + 1")
print(result) # 2.0
Simple Calculator / Interactive Shell
def simple_calculator():
while True:
expr = input("Enter expression (or q to quit): ")
if expr.lower() == "q":
break
try:
print(eval(expr))
except Exception as e:
print(f"Error: {e}")
3. Security Risks & Modern Alternatives in 2026
| Approach | Security Level | Use Case | Recommendation 2026 |
|---|---|---|---|
| eval(expr) | Very low (arbitrary code exec) | Never with user input | Avoid completely in untrusted contexts |
| ast.literal_eval(expr) | High (safe literals only) | Config, simple data structures | Preferred for safe parsing |
| restrictedpython / safe_eval | Medium–High | Restricted dynamic expressions | Use for semi-trusted plugins |
| subprocess / separate process | Very high | Untrusted code | Safest for unknown sources |
4. Best Practices & Performance in 2026
- Never eval untrusted input — use ast.literal_eval() for safe literals
- Restrict globals — always pass {"__builtins__": {}} or safe namespace
- Type hints 2026:
from typing import Any def safe_eval(expr: str, safe_globals: dict[str, Any]) -> Any: return eval(expr, {"__builtins__": {}} | safe_globals, {}) - Performance: compile once with compile(), then eval() multiple times
- Free-threading (3.14+): Safe when globals/locals are not modified concurrently
Worked Example: Safer Alternatives to eval()
Removing __builtins__ does not make eval() safe; attribute tricks can still reach dangerous objects, and even a harmless-looking expression such as 9**9**9 can freeze a process. Never pass untrusted input to eval(). For data, use ast.literal_eval() or json.loads(). For simple formulas, parse the expression with ast and allow only the node types you expect.
import ast
import operator as op
print(ast.literal_eval("[1, 2.5, {'a': True}]")) # data only
OPS = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
ast.Div: op.truediv, ast.USub: op.neg}
def calc(expr: str, variables: dict[str, float]) -> float:
def walk(node):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.Name) and node.id in variables:
return variables[node.id]
if isinstance(node, ast.BinOp) and type(node.op) in OPS:
return OPS[type(node.op)](walk(node.left), walk(node.right))
if isinstance(node, ast.UnaryOp) and type(node.op) in OPS:
return OPS[type(node.op)](walk(node.operand))
raise ValueError(f"not allowed: {ast.dump(node)[:40]}")
return walk(ast.parse(expr, mode="eval").body)
print(calc("price * qty - discount", {"price": 9.5, "qty": 4, "discount": 3})) # 35.0
for bad in ["__import__('os')", "2 ** 100"]:
try:
calc(bad, {})
except ValueError as exc:
print("rejected:", bad)
Common Mistakes
- Calling
eval(input()): any user can run arbitrary code - Trusting an empty
__builtins__as a sandbox - Using
eval()to parse numbers or lists: usefloat(),int()orliteral_eval()
Quick Summary
eval()evaluates one expression with full Python power- Only use it on code you wrote yourself
- Prefer
literal_eval,jsonor a whitelistedastevaluator
Conclusion — eval() in 2026: Powerful but Highly Restricted
eval() allows dynamic expression evaluation — useful for trusted calculators, configs, and metaprogramming — but it is one of Python’s biggest security risks when misused. In 2026, restrict it to trusted sources, prefer ast.literal_eval() for safe cases, and use restricted environments or subprocess for anything untrusted. When used correctly, it’s a clean way to run dynamic math expressions at runtime.
Next steps:
- Audit your codebase for eval() — replace untrusted uses immediately