2.34. Dynamický kód

Tři vestavěné funkce přijímají řetězec se zdrojovým kódem Pythonu a spouštějí jej: eval(), exec() a compile(). Společně umožňují kódu konstruovat a vykonávat další kód za běhu – což je občas přesně ten správný nástroj a mnohem častěji zdroj chyb a bezpečnostních děr.

Varování

Tyto funkce vykonávají libovolný kód Pythonu. Předání uživatelského vstupu do eval nebo exec – řetězce ze souboru, který uživatel může upravit, payloadu přijatého přes síť, hodnoty zadané na výzvě REPL – umožní tomuto vstupu udělat cokoli, co by mohl udělat volající skript, až po smazání každého souboru na zařízení včetně. Používejte je uvážlivě, nikdy uvnitř kódu, který běží automaticky, a nikdy na datech, která nemáte pod kontrolou.

2.34.1. eval

eval() spustí jediný výraz a vrátí jeho hodnotu:

>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'

Výraz ve výchozím nastavení vidí globální a lokální proměnné volajícího, proto se v druhém příkladu name rozpozná. Předání explicitních slovníků vám umožní vyhodnocení izolovat do sandboxu:

eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})

I v sandboxu je eval nebezpečné. Existují dobře známé techniky, jak takové sandboxy obejít; nespoléhejte se pro nedůvěryhodný vstup jen na trik s prázdným __builtins__.

2.34.2. exec

exec() spouští blok kódu namísto jediného výrazu – příkazy, definice funkcí, smyčky – a vrací None:

exec("for i in range(3): print(i)")

Výstup:

0
1
2

Blok může definovat jména, která se poté stanou dostupnými, s několika výhradami ohledně lokálního versus globálního rozsahu. exec uvnitř funkce se zřídka chová tak, jak pisatel očekává; pokud jej potřebujete, spusťte jej na úrovni modulu.

2.34.3. compile

compile() převede řetězec se zdrojovým kódem na kódový objekt, který lze později předat funkci eval() nebo exec(). Použijte jej, když se stejný zdrojový kód bude spouštět mnohokrát – parsování proběhne jednou, vykonávání je rychlejší:

expr = compile("x * x", "<expr>", "eval")
for x in range(5):
    print(eval(expr))

Výstup:

0
1
4
9
16

Prostřední argument je popisek, který se objeví ve výpisech tracebacku, pokud kód vyvolá výjimku. Třetí argument je "eval" pro jediný výraz, "exec" pro blok nebo "single" pro příkaz interaktivního stylu, který vypíše svůj výsledek.

2.34.4. Kdy po nich sáhnout

Téměř nikdy. Většina případů použití, které si začátečníci představují, má bezpečnější alternativy:

  • Čtení konfiguračního souboru. Použijte json – strukturovaná data, žádné vykonávání.

  • Vyhodnocení číselné hodnoty zadané uživatelem. Použijte int() / float() k parsování a poté aritmetiku. Pokud uživatel skutečně potřebuje zadat vzorec, použijte malý parser výrazů, nikoli eval.

Když eval / exec / compile opravdu potřebujete, izolujte místo volání, logujte přesný řetězec, který se chystá vykonat, a se zdrojovým kódem zacházejte jako s tou nejpodezřelejší věcí ve svém kódu.