Since we are a tight knit religious community based around the communochaotic teachings of Kek, I thought I would share with my fellow community members a secret ritual device I created that has served me well in many different scenarios.
Many times when processing user data it would be very convenient for the user to be able to enter their own formulaic expressions and evaluate those down to a real number, just like a spreadsheet does.
But a danger is that malignant users are clever at constructing input that escapes the pen, roots the server, and exerts its own control. Thus they must be sanitized and sandboxed, a tedious and error prone process.
Thus here is a simple expression evaluation that can handle arbitrary user input without allowing escalation of privileges or access to system functions.
def calculator(expression, **context):
context.update({"__builtins__": {}})
return float(eval(expression, context))
This is remarkably short code for all that it does. In Python, eval() evaluates python code given as a string. As we know, this is a very dangerous practice when the string comes from outside our control! Normally eval() inherits the symbol table from the point it is called, which includes lots of dangerous things like access to system functions. Fortunately eval() has an optional second argument that overrides that symbol table. Unfortunately if eval() is given a symbol table without the built in functions, it adds them in itself. Fortunately, if you specify the built in functions reference to be nothing, then it short-circuits that and no surrounding context, not even built-in global functions, are pulled in.
In addition to short-circuiting the built-ins, it's also nice to be able to easily supply specific symbols that are useful for a particular expression. We do this through the use of Python's keyword-argument (kwarg) facility for functions, which allows functions to have arbitrary numbers of named arguments.
Here's a couple examples of use.
>> print calculator("2+3.01/97")
2.03103092784
>> c1 = 101.2
>> c2 = 0.3
>> print calculator("2*a+34.1*b", a=c1, b=c2)
212.63
This makes it easy to allow people to specify arithmetic expressions in fields that normally handle plain numbers. And to handle to a certain extent named constants or variables, but only the ones that are explicitly specified by the program.
Note that eval() will fail if the users gives an invalid expression, so one may wish to handle thrown exceptions from the calling function, or do this:
def calculator(expression, **context):
try:
context.update({"__builtins__": {}})
return float(eval(expression, context))
except:
return
Shadilay my brothers!