Динамическое выполнение кода с помощью 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.