I'm using this code to evaluate mathematical expressions from strings. And it works:
#!/usr/bin/env python
from __future__ import division
from math import *
expression = raw_input()
# Math functions
safe_list = ['math', 'factorial', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'hypot', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
# Create safe directory
safe_dict = dict([(key, locals().get(key, None)) for key in safe_list])
safe_dict['abs'] = abs
result = eval(expression, {"__builtins__": None}, safe_dict)
print result
I wrapped it in a function like this:
#!/usr/bin/env python
from __future__ import division
from math import *
def calculate(expression):
# Math functions
safe_list = ['math', 'factorial', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'hypot', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
# Create safe directory
safe_dict = dict([(key, locals().get(key, None)) for key in safe_list])
safe_dict['abs'] = abs
result = eval(expression, {"__builtins__": None}, safe_dict)
if isinstance(result, float) and result.is_integer():
result = int(result)
print result
expr = raw_input()
calculate(expr)
And it still works for the basic operations, but none of the functions defined in safe_dict are working. 5**5 works with both programs, but sin(pi) worked with the first sample of code and it's not working with the second one. The traceback is
Traceback (most recent call last):
File "stack.py", line 20, in <module>
calculate(expression)
File "stack.py", line 14, in calculate
result = eval(expression, {"__builtins__": None}, safe_dict)
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable
3 Answers 3
The reason it fails is that the functions you import from the math module are not local variables inside the function; they are global. So when you read locals() and insert into the dict, it inserts None for every single one. You would see this if you removed the get(key, None) and just accessed locals()[key] directly.
A better way is to use getattr on the math module. Do import math (not from math import *) and then do safe_dict = dict((k, getattr(math, k)) for k in safe_list).
1 Comment
get when there is no good way to handle the missing key is not a good idea. (and obviously just returning None as the default in get is generally not solving any problems).Within your calculate() function, locals() does not include the functions imported from math. Therefore, safe_dict is populated with a lot of None.
You could build up the safe_dict outside of calculate(), which would also have the advantage of performing better if you're making multiple calls to calculate().
1 Comment
This might be because you have 'math' in safe_list, and math is a module. To me, it seems like eval is doing math(), which will cause it to choke the way it is doing.
Try removing 'math' from safe_list and see if that helps at all.
On a side note: I'm not sure this is really the best way to do what you want to do. I think a design change is called for.
__builtins__, and you named your listsafe_list, but you are not safe. If you don't trust the input, don't useeval.evalis dangerous but I couldn't really think of any other way that would let me use Python math functions.astmight get you there. docs.python.org/library/ast.html'[', ']', '_'or';'. But I'll giveasta try.