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ão eval.

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.