Python eval() function

Updated on Jan 07, 2020


The eval() allows us to execute arbitrary strings as Python code. It accepts a source string and returns an object.

Its syntax is as follows:

Syntax:

eval(expr, globals=None, locals=None)
PARAMETER            DESCRIPTION
expr (required) expr can be any valid Python expression
globals (optional) Global namespace to use while executing the source. It must be a dictionary. If not provided then the current global namespace will be used.
locals (optional) Local namespace to use while executing the source. It can be any mapping. If omitted, it defaults to globals dictionary.

If both globals and locals are omitted, the current global and local namespaces are used.

Here is an example demonstrating how eval() works:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> 
>>> eval("5 == 5")
True
>>> 
>>> 
>>> eval("4 < 10")
True
>>> 
>>> 
>>> eval("8 + 4 - 2 * 3")
6
>>> 
>>> 
>>> eval("'py ' * 5")
'py py py py py '
>>> 
>>>
>>> eval("10 ** 2")
100
>>> 
>>>
>>> eval("'hello' + 'py'")
'hellopy'
>>>

Try it out:

print(eval("5 == 5"))

print(eval("4 < 10"))

print(eval("8 + 4 - 2 * 3"))

print(eval("'py ' * 5"))

print(eval("10 ** 2"))

print(eval("'hello' + 'py'"))


The eval() is not just limited to simple expression. We can execute functions, call methods, reference variables and so on.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>>
>>> eval("abs(-11)")
11
>>> 
>>> 
>>> eval('"hello".upper()')
'HELLO'
>>> 
>>> 
>>> import os
>>>
>>> 
>>> eval('os.getcwd()') # get current working directory
'/home/thepythonguru'
>>> 
>>>
>>> x = 2
>>> 
>>> eval("x+4") # x is referenced inside the expression
6
>>>

Try it out:

print(eval("abs(-11)"))

print(eval('"hello".upper()'))

import os

# get current working directory
print(eval('os.getcwd()')) 

x = 2
 
print(eval("x+4")) # x is referenced inside the expression


Note that the eval() works only with an expression. Trying to pass a statement causes a SyntaxError.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
>>> 
>>> eval('a=1') # assignment statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a=1
    ^
SyntaxError: invalid syntax
>>> 
>>>
>>> eval('import re') # import statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    import re
         ^
SyntaxError: invalid syntax
>>>

Evil eval() #


You should never pass untrusted source to the eval() directly. As it is quite easy for the malicious user to wreak havoc on your system. For example, the following code can be used to delete all the files from the system.

1
2
3
>>>
eval('os.system("RM -RF /")') # command is deliberately capitalized
>>>

The above code would fail if the os module is not available in your current global scope. But we can easily circumvent this by using the __import__() built-in function.

1
2
3
>>>
>>> eval("__import__('os').system('RM -RF /')") # command is deliberately capitalized
>>>

So is there any way to make eval() safe?

Specifying Namespaces #


The eval() optionally accepts two mappings that serve as a global and local namespaces for the expression to be executed. If mapping(s) are not provided then the current values of global and local namespaces will be used.

Here are some examples:

Example 1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> 
>>> globals = {
... 'a': 10,
... 'fruits': ['mangoes', 'peaches', 'bananas'],
... }
>>> 
>>>
>>> locals = {}
>>>
>>>
>>> eval("str(a) + ' ' + fruits[0]", globals, locals)
'10 mangoes'
>>>

Example 2:

1
2
3
4
>>> 
>>> eval('abs(-100)', {}, {})
100
>>>

Even though we have passed empty dictionaries as global and local namespaces, eval() still has access to built-ins (i.e __builtins__ ).

1
2
3
4
5
6
7
8
>>>
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError',
...
...
'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted'
, 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
>>>

To remove built-ins from the global namespace pass a dictionary containing a key __builtins__ with the value None.

Example 3:

1
2
3
4
5
6
7
>>> 
>>> eval('abs(-100)', {'__builtins__':None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
>>>

Even after removing the access to built-ins functions, eval() is still not safe. Consider the following listing.

1
2
3
>>>
>>> eval("5**98765432111123", {'__builtins__':None}, {})
>>>

This deceptively simple looking expression is enough to tank your CPU.

The key takeaway is that only use eval() with the trusted source.


Other Tutorials (Sponsors)