Python: Using sympy.sympify to perform a safe eval() on mathematical functions
Question: I am writing a program where users need to be able to use self written
mathematical functions containing functions from numpy and scipy, eg.
**scipy.special.wofz()**.

These functions will be stored in files and imported as strings by the
program. I looked around and saw, that **eval()** or **exec()** are not a safe
way to do it. eg. [here](http://stackoverflow.com/questions/3513292/python-
make-eval-safe).

The security issue would be that good users load a file from evil users who
get access to the good users system.

I was thinking about doing something like this:

    
    
    #!/bin/python
    from scipy.special import *
    from numpy import *
    import sympy
    
    # Define variable a
    vars = {"a":1}
    # This is the string I get from a file
    string = "wofz(a)"
    
    parsed_string = sympy.sympify(string)
    parsed_string.evalf(subs=vars)
    

However, this does not work. It only returns:

    
    
    wofz(a)
    

wofz(a) is not evaluated. Is this even supposed to work that way?

I had another idea: So I thought, once this mathematical function got through
sympify, it should be safe. I could just simply do something like this:

    
    
    globals = {wofz:wofz}
    eval(str(parsed_string), vars, globals)
    

which works fine and returns:

    
    
    (0.36787944117144233+0.60715770584139372j)
    

Is that safe? I know it's not nice.

Please help.


Answer: Use sympy, it's a way safer option.

    
    
    import sympy
    from sympy.core.function import Function
    from sympy.core import S
    from sympy import sympify
    from sympy.functions import im
    from scipy.special import wofz
    
    class Wofz(Function):
        is_real = True
        @classmethod
        def _should_evalf(csl,arg):
            return True
        def as_base_exp(cls):
            return cls,S.One
    
        def _eval_evalf(cls, prec):
            return sympy.numbers.Number(im(wofz(float(cls.args[0]))))
    
    print sympify("Wofz(2)",{'Wofz':Wofz}).evalf()
    

Output (you'll have to handle the imaginary part somehow):

    
    
    0.340026217066065
    

