2.34. Código dinâmico¶
Três funções embutidas recebem uma string de código-fonte Python e a executam: eval(), exec() e compile(). Juntas, elas permitem que o código construa e execute mais código em tempo de execução – o que ocasionalmente é exatamente a ferramenta certa e, com muito mais frequência, uma fonte de bugs e falhas de segurança.
Aviso
Essas funções executam Python arbitrário. Passar entrada do usuário para eval ou exec – uma string de um arquivo que o usuário pode editar, uma carga útil recebida pela rede, um valor digitado em um prompt do REPL – permite que essa entrada faça qualquer coisa que o script chamador poderia fazer, até mesmo apagar todos os arquivos do dispositivo. Use-as deliberadamente, nunca dentro de código que roda automaticamente, e nunca em dados que você não controla.
2.34.1. eval¶
eval() executa uma única expressão e retorna seu valor:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
Por padrão, a expressão enxerga os globais e locais do chamador, e é por isso que name é resolvido no segundo exemplo. Passar dicionários explícitos permite isolar (sandbox) a avaliação:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Mesmo isolado em sandbox, eval é perigoso. Existem técnicas bem conhecidas para escapar de tais sandboxes; não confie apenas no truque do __builtins__ vazio para entrada não confiável.
2.34.2. exec¶
exec() executa um bloco de código em vez de uma única expressão – instruções, definições de função, laços – e retorna None:
exec("for i in range(3): print(i)")
Saída:
0
1
2
O bloco pode definir nomes que ficam disponíveis depois, com algumas ressalvas sobre escopo local versus global. exec dentro de uma função raramente se comporta da maneira que quem escreve espera; se precisar dele, execute-o no nível do módulo.
2.34.3. compile¶
compile() transforma uma string de código-fonte em um objeto de código que pode ser passado a eval() ou exec() mais tarde. Use-o quando o mesmo código-fonte for rodar muitas vezes – a análise acontece uma vez, e a execução é mais rápida:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Saída:
0
1
4
9
16
O argumento do meio é um rótulo que aparece nos tracebacks se o código levantar uma exceção. O terceiro argumento é "eval" para uma única expressão, "exec" para um bloco, ou "single" para uma instrução de estilo interativo que imprime seu resultado.
2.34.4. Quando recorrer a essas funções¶
Quase nunca. A maioria dos casos de uso que os iniciantes imaginam tem alternativas mais seguras:
Ler um arquivo de configuração. Use
json– dados estruturados, sem execução.Avaliar um valor numérico digitado pelo usuário. Use
int()/float()para fazer o parsing e, depois, aritmética. Se o usuário realmente precisar inserir uma fórmula, use um pequeno parser de expressões, nãoeval.
Quando você de fato precisar de eval / exec / compile, isole o local da chamada, registre a string exata que está prestes a ser executada e trate o código-fonte como a coisa mais suspeita do seu programa.