Python Language
Exécution de code dynamique avec `exec` et` eval`
Recherche…
Syntaxe
- eval (expression [, globals = None [, locaux = None]])
- exec (objet)
- exec (objet, globales)
- exec (objet, globales, locaux)
Paramètres
Argument | Détails |
---|---|
expression | Le code de l'expression sous la forme d'une chaîne ou d'un objet de code |
object | Le code d'instruction sous la forme d'une chaîne ou d'un objet de code |
globals | Le dictionnaire à utiliser pour les variables globales. Si les locaux ne sont pas spécifiés, cela est également utilisé pour les locaux. Si omis, les globals() de la portée appelante sont utilisés. |
locals | Un objet de mappage utilisé pour les variables locales. Si omis, celui passé pour les globals est utilisé à la place. Si les deux sont omis, les globals() et locals() de la portée d'appel sont utilisés respectivement pour les globals et les locals . |
Remarques
Dans exec
, si les globals
sont des locals
(c'est-à-dire qu'ils font référence au même objet), le code est exécuté comme s'il était au niveau du module. Si les globals
et les locals
sont des objets distincts, le code est exécuté comme s'il était dans un corps de classe .
Si l'objet globals
est transmis mais ne spécifie pas la clé __builtins__
, les fonctions et les noms intégrés à Python sont automatiquement ajoutés à la portée globale. Pour supprimer la disponibilité des fonctions telles que l' print
ou isinstance
dans le cadre exécuté, laissez - globals
ont la clé __builtins__
mis en correspondance avec la valeur None
. Cependant, ce n'est pas une fonctionnalité de sécurité.
La syntaxe spécifique à Python 2 ne doit pas être utilisée; la syntaxe Python 3 fonctionnera dans Python 2. Ainsi, les formes suivantes sont obsolètes: <s>
-
exec object
-
exec object in globals
-
exec object in globals, locals
Évaluation des instructions avec exec
>>> code = """for i in range(5):\n print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Evaluer une expression avec eval
>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20
Précompiler une expression pour l'évaluer plusieurs fois
compile
fonction intégrée de compile
peut être utilisée pour précompiler une expression dans un objet de code; Cet objet de code peut ensuite être passé à eval. Cela accélérera les exécutions répétées du code évalué. Le 3ème paramètre à compile
doit être la chaîne '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
Évaluation d'une expression avec eval à l'aide de globales personnalisés
>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42
En plus, le code ne peut pas se référer accidentellement aux noms définis à l'extérieur:
>>> 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
Utiliser defaultdict
permet par exemple d'avoir des variables indéfinies définies à zéro:
>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0
Evaluer une chaîne contenant un littéral Python avec ast.literal_eval
Si vous avez une chaîne qui contient des littéraux Python, tels que des chaînes, des flottants, etc., vous pouvez utiliser ast.literal_eval
pour évaluer sa valeur au lieu de eval
. Cela a pour caractéristique supplémentaire de n'autoriser qu'une certaine syntaxe.
>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>
Cependant, ceci n'est pas sécurisé pour l'exécution du code fourni par un utilisateur non fiable, et il est trivial d'interrompre un interpréteur avec une entrée soigneusement conçue.
>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
Ici, l'entrée est une chaîne de caractères ()
répétée un million de fois, ce qui provoque un blocage de l'analyseur CPython. Les développeurs CPython ne considèrent pas les bogues de l'analyseur comme des problèmes de sécurité.
Code d'exécution fourni par un utilisateur non approuvé à l'aide de exec, eval ou ast.literal_eval
Il n'est pas possible d'utiliser eval
ou exec
pour exécuter du code à partir d'utilisateurs non fiables en toute sécurité. Même ast.literal_eval
est susceptible de se ast.literal_eval
dans l'analyseur. Il est parfois possible de se prémunir contre l’exécution de code malveillant, mais cela n’exclut pas la possibilité d’un blocage brutal de l’analyseur ou du tokenizer.
Pour évaluer le code par un utilisateur non fiable, vous devez vous tourner vers un module tiers, ou peut-être écrire votre propre analyseur syntaxique et votre propre machine virtuelle en Python.