web-dev-qa-db-fra.com

Comment exécuter une chaîne contenant du code Python en Python?

Comment exécuter une chaîne contenant du code Python en Python?

270
hekevintran

Dans l'exemple, une chaîne est exécutée sous forme de code à l'aide de la fonction exec.

import sys
import StringIO

# create file-like string to capture output
codeOut = StringIO.StringIO()
codeErr = StringIO.StringIO()

code = """
def f(x):
    x = x + 1
    return x

print 'This is my output.'
"""

# capture output and errors
sys.stdout = codeOut
sys.stderr = codeErr

exec code

# restore stdout and stderr
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

print f(4)

s = codeErr.getvalue()

print "error:\n%s\n" % s

s = codeOut.getvalue()

print "output:\n%s" % s

codeOut.close()
codeErr.close()
57
hekevintran

Pour les instructions, utilisez exec(string) (Python 2/3) ou exec string (Python 2):

>>> mycode = 'print "hello world"'
>>> exec(mycode)
Hello world

Lorsque vous avez besoin de la valeur d’une expression, utilisez eval(string) :

>>> x = eval("2+2")
>>> x
4

Cependant, la première étape devrait être de vous demander si vous en avez vraiment besoin. L'exécution du code doit généralement être la position de dernier recours: c'est lent, moche et dangereux s'il peut contenir un code saisi par l'utilisateur. Vous devez toujours commencer par rechercher des alternatives, telles que les fonctions d'ordre supérieur, pour voir si elles peuvent mieux répondre à vos besoins.

262
Brian

Rappelez-vous que depuis la version 3 exec est une fonction!
utilisez toujours exec(mystring) au lieu de exec mystring.

19
bheks

eval et exec sont la solution correcte et peuvent être utilisés de manière plus sûre.

Comme indiqué dans Le manuel de référence de Python et expliqué clairement dans this tutorial, les fonctions eval et exec prennent deux paramètres supplémentaires qui permettent à l'utilisateur de spécifier les fonctions et variables globales et locales disponibles.

Par exemple:

public_variable = 10

private_variable = 2

def public_function():
    return "public information"

def private_function():
    return "super sensitive information"

# make a list of safe functions
safe_list = ['public_variable', 'public_function']
safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
# add any needed builtins back in
safe_dict['len'] = len

>>> eval("public_variable+2", {"__builtins__" : None }, safe_dict)
12

>>> eval("private_variable+2", {"__builtins__" : None }, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_variable' is not defined

>>> exec("print \"'%s' has %i characters\" % (public_function(), len(public_function()))", {"__builtins__" : None}, safe_dict)
'public information' has 18 characters

>>> exec("print \"'%s' has %i characters\" % (private_function(), len(private_function()))", {"__builtins__" : None}, safe_dict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'private_function' is not defined

En substance, vous définissez l’espace de noms dans lequel le code sera exécuté.

18
alan

eval() est juste pour les expressions, alors que eval('x+1') fonctionne, eval('x=1') ne fonctionnera pas par exemple. Dans ce cas, il vaut mieux utiliser exec, ou même mieux: essayez de trouver une meilleure solution :)

11
LGB

Vous exécutez le code en utilisant exec, comme avec la session IDLE suivante:

>>> kw = {}
>>> exec( "ret = 4" ) in kw
>>> kw['ret']

4
9
gus

Évitez exec et eval

Utiliser exec et eval en Python est très mal vu.

Il y a de meilleures alternatives

De la réponse du haut (souligné par moi):

Pour les instructions, utilisez exec.

Lorsque vous avez besoin de la valeur d'une expression, utilisez eval.

Cependant, la première étape devrait être de vous demander si vous en avez vraiment besoin. L'exécution du code doit généralement être la position de dernier recours: c'est lent, moche et dangereux s'il peut contenir un code saisi par l'utilisateur. Vous devez toujours commencer par examiner les options , telles que les fonctions d'ordre supérieur, pour voir si elles peuvent mieux répondre à vos besoins.

De Alternatives à exec/eval?

définir et obtenir des valeurs de variables avec les noms dans des chaînes

[Alors que eval] fonctionnerait, il est généralement déconseillé d'utiliser des noms de variable ayant une signification pour le programme lui-même.

Au lieu de cela, mieux utiliser un dict.

Ce n'est pas idiomatique

De http://lucumr.pocoo.org/2011/2/1/exec-in-python/ (l'emphase mienne)

Python n'est pas PHP

N'essayez pas de contourner les idiomes Python, car une autre langue le fait différemment. Les espaces de noms sont en Python pour une raison et juste parce que cela vous donne l'outil exec cela ne signifie pas que vous devriez utiliser cet outil.

C'est dangereux

De http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html (c'est moi qui souligne)

Donc, eval n’est pas sûr, même si vous supprimez tous les globals et les éléments intégrés!

Le problème avec toutes ces tentatives de protection de eval () est qu’elles sont des listes noires. Ils suppriment explicitement les choses qui pourraient être dangereuses. C'est une bataille perdue d'avance car s'il ne reste qu'un élément à gauche de la liste, vous pouvez attaquer le système.

Alors, peut-on sécuriser eval? Dur à dire. A ce stade, ma meilleure hypothèse est que vous ne pouvez pas faire de mal si vous ne pouvez utiliser aucun double trait de soulignement. Par conséquent, si vous excluez une chaîne comportant un double soulignement, vous êtes en sécurité. Peut être...

Il est difficile de lire et de comprendre

De http://stupidpythonideas.blogspot.it/2013/05/why-evalexec-is-bad.html (c'est moi qui souligne):

Premièrement, exec rend plus difficile la lecture de votre code par les êtres humains. Afin de comprendre ce qui se passe, je ne dois pas simplement lire votre code, je dois le lire, déterminer quelle chaîne il va générer, puis lire ce code virtuel. Ainsi, si vous travaillez en équipe, éditez des logiciels open source ou demandez de l'aide, par exemple StackOverflow, vous empêchez les autres de vous aider. Et s'il y a une chance que vous développiez ou développiez ce code dans 6 mois, vous le rendez plus difficile pour vous-même.

9
Caridorc

Départ eval :

x = 1
print eval('x+1')
->2
4
ryeguy

La solution la plus logique serait d'utiliser la fonction eval () . Une autre solution consiste à écrire cette chaîne dans un fichier python temporaire et à l'exécuter. 

3
John T

Utilisez eval .

2
Pablo Santa Cruz

Comme les autres l'ont mentionné, c'est "exec" .. 

mais, si votre code contient des variables, vous pouvez utiliser "global" pour y accéder, également pour empêcher le compilateur de générer l'erreur suivante:

NameError: le nom 'p_variable' n'est pas défini

exec('p_variable = [1,2,3,4]')
global p_variable
print(p_variable)
1
Ghanem

Ok .. Je sais que ce n’est pas vraiment une réponse, mais peut-être une note pour ceux qui le regardent comme moi. Je voulais exécuter du code spécifique pour différents utilisateurs/clients, mais je voulais aussi éviter le exec/eval. Au départ, je cherchais à stocker le code dans une base de données pour chaque utilisateur et à faire ce qui précède.

J'ai fini par créer les fichiers sur le système de fichiers dans un dossier 'customer_filters' et en utilisant le module 'imp', si aucun filtre n'était appliqué à ce client, cela fonctionnait

import imp


def get_customer_module(customerName='default', name='filter'):
    lm = None
    try:
        module_name = customerName+"_"+name;
        m = imp.find_module(module_name, ['customer_filters'])
        lm = imp.load_module(module_name, m[0], m[1], m[2])
    except:
        ''
        #ignore, if no module is found, 
    return lm

m = get_customer_module(customerName, "filter")
if m is not None:
    m.apply_address_filter(myobj)

so customerName = "jj" exécuterait apply_address_filter à partir du fichier customer_filters\jj_filter.py

0
Brian