Skip to content

Instantly share code, notes, and snippets.

@Zeturic
Created July 19, 2022 23:39
Show Gist options
  • Save Zeturic/bec8fff03076cb4b8f4d2526db10fe40 to your computer and use it in GitHub Desktop.
Save Zeturic/bec8fff03076cb4b8f4d2526db10fe40 to your computer and use it in GitHub Desktop.
Evaluate math expressions in Python using whitelisted AST nodes and eval
import ast
import math
from types import MappingProxyType
_empty_map = MappingProxyType({})
math_locals = MappingProxyType(
{name: value for (name, value) in vars(math).items() if not name.startswith('_')} |
{"abs": abs, "complex": complex, "min": min, "max": max, "pow": pow, "round": round}
)
_whitelist = (
ast.Module,
ast.Expr,
ast.Load,
ast.Expression,
ast.Add,
ast.Sub,
ast.UnaryOp,
ast.Num,
ast.BinOp,
ast.Mult,
ast.Div,
ast.Pow,
ast.BitOr,
ast.BitAnd,
ast.BitXor,
ast.USub,
ast.UAdd,
ast.FloorDiv,
ast.Mod,
ast.LShift,
ast.RShift,
ast.Invert,
ast.Call,
ast.Name,
)
def _only_whitelisted_nodes(root):
for node in ast.walk(root):
if not isinstance(node, _whitelist):
return False
return True
def evaluate(expr, locals = _empty_map):
if any(elem in expr for elem in '\n#'):
raise ValueError(expr) from None
try:
node = ast.parse(expr.strip(), mode='eval')
if not _only_whitelisted_nodes(node):
raise ValueError(expr) from None
return eval(compile(node, "<string>", "eval"), {'__builtins__': _empty_map}, locals)
except (SyntaxError, NameError):
raise ValueError(expr) from None
if __name__ == "__main__":
import readline
from traceback import print_exception
interactive_locals = {"quit": quit} | math_locals
while True:
string = input("⧽⧽⧽ ")
try:
value = evaluate(string, locals=interactive_locals)
interactive_locals["ANS"] = value
print(value)
except ValueError as e:
print_exception(e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment