Python Language
Ejecución de código dinámico con `exec` y` eval`
Buscar..
Sintaxis
- eval (expresión [, globals = None [, locals = None]])
- exec (objeto)
- exec (objeto, globals)
- exec (objeto, globales, locales)
Parámetros
Argumento | Detalles |
---|---|
expression | El código de expresión como una cadena o un objeto de code |
object | El código de la declaración como una cadena, o un objeto de code |
globals | El diccionario a utilizar para variables globales. Si no se especifican los locales, esto también se usa para los locales. Si se omite, se utilizan los globals() del ámbito de llamada. |
locals | Un objeto de mapeo que se utiliza para las variables locales. Si se omite, se usa el que se pasa para los globals . Si se omiten ambos, entonces se utilizan los globals() y locals() del ámbito de llamada para los globals y locals respectivamente. |
Observaciones
En exec
, si los globals
son locals
(es decir, se refieren al mismo objeto), el código se ejecuta como si estuviera en el nivel del módulo. Si los elementos globals
y locals
son objetos distintos, el código se ejecuta como si estuviera en un cuerpo de clase .
Si las globals
de objeto se pasa en, pero no especifica __builtins__
clave, entonces Python funciones integradas y los nombres se añaden automáticamente al ámbito global. Para suprimir la disponibilidad de funciones como la print
o la isinstance
en el ámbito ejecutado, permita que los globals
tengan la clave __builtins__
asignada al valor None
. Sin embargo, esto no es una característica de seguridad.
La sintaxis específica de Python 2 no debe usarse; La sintaxis de Python 3 funcionará en Python 2. Por lo tanto, los siguientes formularios están en desuso: <s>
-
exec object
-
exec object in globals
-
exec object in globals, locals
Evaluando declaraciones con exec
>>> code = """for i in range(5):\n print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Evaluando una expresión con eval
>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20
Precompilando una expresión para evaluarla varias veces.
compile
función incorporada de compile
se puede utilizar para precompilar una expresión en un objeto de código; este objeto de código se puede pasar a eval. Esto acelerará las ejecuciones repetidas del código evaluado. El tercer parámetro para compile
debe ser la cadena '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
Evaluar una expresión con eval utilizando globales personalizados
>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42
Como un plus, con esto el código no puede referirse accidentalmente a los nombres definidos fuera:
>>> 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
El uso de defaultdict
permite, por ejemplo, tener variables indefinidas configuradas en cero:
>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0
Evaluar una cadena que contiene un literal de Python con ast.literal_eval
Si tiene una cadena que contiene literales de Python, como cadenas, flotadores, etc., puede usar ast.literal_eval
para evaluar su valor en lugar de eval
. Esto tiene la característica adicional de permitir solo cierta sintaxis.
>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>
Sin embargo, esto no es seguro para la ejecución del código proporcionado por un usuario no confiable, y es trivial bloquear un intérprete con una entrada cuidadosamente diseñada
>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
Aquí, la entrada es una cadena de ()
repetida un millón de veces, lo que provoca un bloqueo en el analizador CPython. Los desarrolladores de CPython no consideran los errores en el analizador como problemas de seguridad.
Código de ejecución proporcionado por un usuario no confiable que utiliza exec, eval o ast.literal_eval
No es posible usar eval
o exec
para ejecutar código de un usuario no confiable de forma segura. Incluso ast.literal_eval
es propenso a bloqueos en el analizador. A veces es posible protegerse contra la ejecución de código malicioso, pero no excluye la posibilidad de bloqueos directos en el analizador o el tokenizador.
Para evaluar el código por un usuario que no es de confianza, debe recurrir a algún módulo de terceros, o tal vez escribir su propio analizador y su propia máquina virtual en Python.