Python Dynamic code execution with exec
and eval
helps the programmers in fulfilling multiple goals. Leanr more about the parameter here.
Argument
expression object
globals
locals
Details
The expression code as a string, or a code object
The statement code as a string, or a code object
The dictionary to use for global variables. If locals is not specified, this is also used for locals. If omitted, the globals() of calling scope are used.
A mapping object that is used for local variables. If omitted, the one passed for globals is used instead. If both are omitted, then the globals() and locals() of the calling scope are used for globals and locals respectively.
Python Dynamic code: Executing code provided by untrusted user using exec, eval, or ast.literal_eval
It is not possible to use eval or exec to execute code from untrusted user securely. Even ast.literal_eval is prone to crashes in the parser. It is sometimes possible to guard against malicious code execution, but it doesn’t exclude the possibility of outright crashes in the parser or the tokenizer.
To evaluate code by an untrusted user you need to turn to some third-party module, or perhaps write your own parser and your own virtual machine in Python.
Python Dynamic code: Evaluating a string containing a Python literal with ast.literal_eval
If you have a string that contains Python literals, such as strings, floats etc, you can use ast.literal_eval to evaluate its value instead of eval. This has the added feature of allowing only certain syntax.
import ast
code = """(1, 2, {'foo': 'bar'})"""
object = ast.literal_eval(code)
object
(1, 2, {'foo': 'bar'})
type(object)
However, this is not secure for execution of code provided by untrusted user, and it is trivial to crash an interpreter with carefully crafted input
import ast
ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
Here, the input is a string of () repeated one million times, which causes a crash in CPython parser. CPython developers do not consider bugs in parser as security issues.
Evaluating statements with exec
code = """for i in range(5):\n print('Hello world!')""" exec(code) Hello world! Hello world! Hello world!
Hello world!
Hello world!
Evaluating an expression with eval
expression = '5 + 3 * a'
a = 5
result = eval(expression)
result
20
Precompiling an expression to evaluate it multiple times
compile built-in function can be used to precompile an expression to a code object; this code object can then be passed to eval. This will speed up the repeated executions of the evaluated code. The 3rd parameter to compile needs to be the string ‘eval’.
code = compile('a * b + c', '', 'eval')
codeat 0x7f0e51a58830, file "", line 1>
a, b, c = 1, 2, 3
eval(code)
5
Evaluating an expression with eval using custom globals
variables = {'a': 6, 'b': 7}
eval('a * b', globals=variables)
42
As a plus, with this the code cannot accidentally refer to the names defined outside:
eval('variables') {'a': 6, 'b': 7}
eval('variables', globals=variables) Traceback (most recent call last):
File "", line 1, in File "", line 1, in
NameError: name 'variables' is not defined
Using defaultdict allows for example having undefined variables set to zero:
from collections import defaultdict
variables = defaultdict(int, {'a': 42})
eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0