Python Language
Esecuzione di codice dinamico con `exec` e` eval`
Ricerca…
Sintassi
- eval (espressione [, globals = None [, locals = None]])
- exec (oggetto)
- exec (oggetto, globals)
- exec (oggetto, globals, gente del posto)
Parametri
Discussione | Dettagli |
---|---|
expression | Il codice dell'espressione come una stringa o un oggetto code |
object | Il codice dell'istruzione come una stringa o un oggetto code |
globals | Il dizionario da usare per le variabili globali. Se la gente del posto non è specificata, questo è anche usato per i locali. Se omesso, vengono utilizzati i globals() dell'ambito di chiamata. |
locals | Un oggetto di mappatura che viene utilizzato per le variabili locali. Se omesso, viene utilizzato invece quello per le globals . Se entrambi vengono omessi, i globals() e i locals() dell'ambito chiamante vengono utilizzati rispettivamente per globals e locals . |
Osservazioni
In exec
, se i globals
sono locals
(cioè si riferiscono allo stesso oggetto), il codice viene eseguito come se fosse a livello di modulo. Se globals
e locals
sono oggetti distinti, il codice viene eseguito come se fosse in un corpo di classe .
Se l'oggetto globals
viene passato, ma non specifica la chiave __builtins__
, le funzioni e i nomi incorporati di Python vengono automaticamente aggiunti all'ambito globale. Per sopprimere la disponibilità di funzioni come print
o isinstance
nell'ambito eseguito, lasciare che le globals
abbiano la chiave __builtins__
mappata sul valore None
. Tuttavia, questa non è una funzione di sicurezza.
La sintassi specifica di Python 2 non dovrebbe essere utilizzata; la sintassi Python 3 funzionerà in Python 2. Quindi i seguenti moduli sono deprecati: <s>
-
exec object
-
exec object in globals
-
exec object in globals, locals
Valutazione di dichiarazioni con exec
>>> code = """for i in range(5):\n print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Valutare un'espressione con eval
>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20
Precompilare un'espressione per valutarla più volte
compile
funzione incorporata può essere utilizzata per precompilare un'espressione a un oggetto codice; questo oggetto codice può quindi essere passato a valutazione. Ciò velocizzerà le ripetute esecuzioni del codice valutato. Il terzo parametro da compile
deve essere la stringa '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
Valutare un'espressione con eval utilizzando globals personalizzati
>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42
Come un vantaggio, con questo il codice non può accidentalmente fare riferimento ai nomi definiti al di fuori:
>>> 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
L'uso di defaultdict
consente ad esempio di avere variabili non definite impostate su zero:
>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0
Valutare una stringa contenente un letterale Python con ast.literal_eval
Se hai una stringa che contiene letterali Python, come stringhe, float ecc., Puoi usare ast.literal_eval
per valutare il suo valore invece di eval
. Questo ha la caratteristica aggiunta di consentire solo una certa sintassi.
>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>
Tuttavia, questo non è sicuro per l'esecuzione del codice fornito da un utente non fidato, ed è banale arrestare un interprete con un input accuratamente predisposto
>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
Qui, l'input è una stringa di ()
ripetuta un milione di volte, che provoca un crash nel parser CPython. Gli sviluppatori CPython non considerano i bug nel parser come problemi di sicurezza.
Esecuzione del codice fornito dall'utente non fidato mediante exec, eval o ast.literal_eval
Non è possibile utilizzare eval
o exec
per eseguire il codice dell'utente inaffidabile. Anche ast.literal_eval
è soggetto a crash nel parser. A volte è possibile evitare l'esecuzione di codice dannoso, ma non esclude la possibilità di arresto anomalo nel parser o nel tokenizer.
Per valutare il codice da un utente non fidato è necessario passare a qualche modulo di terze parti, o magari scrivere il proprio parser e la propria macchina virtuale in Python.