Динамическое выполнение кода с помощью exec и eval

Оценка заявлений с exec

>>> code = """for i in range(5):\n    print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world! 

Оценка выражения с помощью eval

>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20 

Прекомпиляция выражения для оценки его несколько раз

compile встроенную функцию можно использовать для прекомпилировать выражение к объекту кода; этот объект кода затем может быть передан в eval. Это ускорит повторное выполнение оцененного кода. Третий параметр compile должен быть строка 'eval' .

>>> code = compile('a * b + c', '<string>', 'eval')
>>> code
<code object <module> at 0x7f0e51a58830, file "<string>", line 1>
>>> a, b, c = 1, 2, 3
>>> eval(code)
5 

Оценка выражения с помощью eval с использованием пользовательских глобалов

>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42 

Как плюс, при этом код не может случайно ссылаться на имена, определенные снаружи:

 >>> eval('variables')
{'a': 6, 'b': 7}
>>> eval('variables', globals=variables)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'variables' is not defined

 

Использование defaultdict позволяет, например , имеющие неопределенные переменные , установленные на нуль:

>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables)  # note that 'c' is not explicitly defined
0 

Оценка строки, содержащей литерал Python, с помощью ast.literal_eval

Если у вас есть строка , которая содержит Python литералы, такие как строки, поплавки и т.д., вы можете использовать ast.literal_eval оценить его значение вместо eval.Это имеет дополнительную функцию, позволяющую использовать только определенный синтаксис.

>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>

Однако это небезопасно для выполнения кода, предоставленного ненадежным пользователем, и тривиально вывести из строя интерпретатор с тщательно продуманным вводом

>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5]    21358 segmentation fault (core dumped)  python3

Здесь вход строка () повторяется один миллион раз, что приводит к сбою в CPython анализатор. Разработчики CPython не рассматривают ошибки в парсере как проблемы безопасности.

Выполнение кода, предоставленного ненадежным пользователем, используя exec, eval или ast.literal_eval

Это не представляется возможным использовать eval или exec для выполнения кода из ненадежного пользователя надежно. Даже ast.literal_eval склонен к сбоям в анализатор. Иногда можно защититься от выполнения вредоносного кода, но это не исключает возможности прямых сбоев в анализаторе или токенизаторе.

Чтобы оценить код ненадежного пользователя, вам нужно обратиться к какому-либо стороннему модулю или, возможно, написать свой собственный анализатор и собственную виртуальную машину на Python.