Python eval() function is used to parse an expression string as python expression and then execute it.

Python eval() function

Python eval() function signature is:


eval(expression, globals=None, locals=None)

expression – mandatory string parameter, this is being parsed and executed as python expression.

globals – dictionary used to specify the expressions available to execute. Standard built-in methods are available if not explicitly restricted using '__builtins__': None element.

locals – used to specify the local variables and methods available to eval() function.

Python eval() example

Let’s first look at a simple example of python eval() function.


x = 1
print(eval('x==1'))
print(eval('x+2'))

Output:


True
3

Python eval() with user input

Above example of eval() function is very limited and doesn’t justify its power. The power of eval() function lies in dynamic execution of statements. We can execute arbitrary code objects using eval() function.

Let’s look at a more complex example where we will ask the user to enter functions to execute.


# eval() with user input
from math import *
for l in range(1, 3):
    func = input("Enter Math Function to Evaluate:n")
    try:
        print(eval(func))
    except Exception as ex:
        print(ex)
        break
print('Done')

Below image shows a sample execution of the above python script.

Python eval() function With Examples

 

Without eval function, we can’t execute the user entered commands. This is the power of eval() function.

Security Risks with eval() function

With Great Power Comes Great Responsibility is very true if you are allowing user input to be executed as a command.

What if we have os module imported and user enters os.system('rm -rf /') command to be executed. This will start deleting system files and corrupt our environment.

That’s why when you are using eval() function to execute user input code, you need to make sure that user entered data is checked first and if they are fine then only its executed. This is when globals and locals parameters come in handy.

Python eval() globals and locals

Before we decide what functions we should make available to eval(), we need to find out what all functions and variables are present in the global and local scope. We can find this information using globals(), locals(), and dir() builtin functions.

Let’s look at an example where we will find out the functions and variables available in global and local scope.


from math import *
def square_root(n):
   return sqrt(n)
print(globals()) # dictionary representing the current global symbol table.
print(locals()) # dictionary representing the current local symbol table.
print(dir()) # list of names in the current local scope

Output:


{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x105b11400>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/pankaj/Documents/PycharmProjects/BasicPython/basic_examples/eval_example.py', '__cached__': None, 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, 'asinh': <built-in function asinh>, 'atan': <built-in function atan>, 'atan2': <built-in function atan2>, 'atanh': <built-in function atanh>, 'ceil': <built-in function ceil>, 'copysign': <built-in function copysign>, 'cos': <built-in function cos>, 'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>, 'erf': <built-in function erf>, 'erfc': <built-in function erfc>, 'exp': <built-in function exp>, 'expm1': <built-in function expm1>, 'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>, 'floor': <built-in function floor>, 'fmod': <built-in function fmod>, 'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>, 'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>, 'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>, 'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>, 'isnan': <built-in function isnan>, 'ldexp': <built-in function ldexp>, 'lgamma': <built-in function lgamma>, 'log': <built-in function log>, 'log1p': <built-in function log1p>, 'log10': <built-in function log10>, 'log2': <built-in function log2>, 'modf': <built-in function modf>, 'pow': <built-in function pow>, 'radians': <built-in function radians>, 'remainder': <built-in function remainder>, 'sin': <built-in function sin>, 'sinh': <built-in function sinh>, 'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>, 'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>, 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tau': 6.283185307179586, 'inf': inf, 'nan': nan, 'square_root': <function square_root at 0x105b6a2f0>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x105b11400>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/pankaj/Documents/PycharmProjects/BasicPython/basic_examples/eval_example.py', '__cached__': None, 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, 'asinh': <built-in function asinh>, 'atan': <built-in function atan>, 'atan2': <built-in function atan2>, 'atanh': <built-in function atanh>, 'ceil': <built-in function ceil>, 'copysign': <built-in function copysign>, 'cos': <built-in function cos>, 'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>, 'erf': <built-in function erf>, 'erfc': <built-in function erfc>, 'exp': <built-in function exp>, 'expm1': <built-in function expm1>, 'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>, 'floor': <built-in function floor>, 'fmod': <built-in function fmod>, 'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>, 'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>, 'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>, 'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>, 'isnan': <built-in function isnan>, 'ldexp': <built-in function ldexp>, 'lgamma': <built-in function lgamma>, 'log': <built-in function log>, 'log1p': <built-in function log1p>, 'log10': <built-in function log10>, 'log2': <built-in function log2>, 'modf': <built-in function modf>, 'pow': <built-in function pow>, 'radians': <built-in function radians>, 'remainder': <built-in function remainder>, 'sin': <built-in function sin>, 'sinh': <built-in function sinh>, 'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>, 'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>, 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tau': 6.283185307179586, 'inf': inf, 'nan': nan, 'square_root': <function square_root at 0x105b6a2f0>}
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'square_root', 'tan', 'tanh', 'tau', 'trunc']

That’s a lot of functions that eval() will have access to. Most of them are from __builtins__ and math module.

Let’s see what happens when we specify globals value as empty dictionary in eval function.


print(eval('dir()',{}))

Output:


['__builtins__']

So builtin methods are still available to eval function. If you want to limit access to only a few of the built-in methods, then you can specify its value for globals. For example, below code is allowing eval() function to execute only min built-in function.


print(eval('min(1,2)',{'__builtins__':{'min': min}})) # 1

Let’s look at another example where I am providing locals value and disabling all the built-in functions access for eval().


y=5
print(eval('y+1',{'__builtins__': None}, {'y': y})) # 6

Let’s look at a final example where I am allowing access to only a few of the methods from math module. We are also mapping square_root to sqrt function for better human readability.


from math import *
for l in range(1, 3):
    func = input("Enter Math Function to Evaluate.nAllowed Functions are: square_root(x) and pow(x,y):n")
    try:
        print(eval(func, {'square_root': sqrt, 'pow': pow}))
    except Exception as ex:
        print(ex)
        break
print('Done')

A sample output:


Enter Math Function to Evaluate.
Allowed Functions are: square_root(x) and pow(x,y):
square_root(16)
4.0
Enter Math Function to Evaluate.
Allowed Functions are: square_root(x) and pow(x,y):
log10(100)
name 'log10' is not defined
Done
python-eval-globals-locals

 

Did you notice any flaw in the above code?

I have not specified anything for builtin functions, so they will be available for eval() function.

Below is another example run to show that built-in functions are available to be executed.


Enter Math Function to Evaluate.
Allowed Functions are: square_root(x) and pow(x,y):
min(5,4)
4
Enter Math Function to Evaluate.
Allowed Functions are: square_root(x) and pow(x,y):
max(10,20)
20

Summary

Python eval() function is very powerful. Even though we have globals and locals variable to restrict access, they are not enough and workaround are available to harm your system. Read this article explaining why eval is dangerous. You shouldn’t use eval() function with untrusted user inputs.

You can checkout complete python script and more Python examples from our GitHub Repository.

By admin

Leave a Reply

%d bloggers like this: