Created
July 19, 2022 23:39
-
-
Save Zeturic/bec8fff03076cb4b8f4d2526db10fe40 to your computer and use it in GitHub Desktop.
Evaluate math expressions in Python using whitelisted AST nodes and eval
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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