2.34. Código dinâmico

Três built-ins recebem uma string de código-fonte Python e executam-na: eval(), exec() e compile(). Em conjunto permitem que o código construa e execute mais código em tempo de execução – o que é, ocasionalmente, a ferramenta certa, e muito mais frequentemente uma fonte de erros e falhas de segurança.

Aviso

Estas funções executam Python arbitrário. Passar input do utilizador para eval ou exec – uma string de um ficheiro que o utilizador pode editar, um payload recebido pela rede, um valor introduzido numa prompt REPL – permite que esse input faça tudo o que o script chamador poderia fazer, incluindo eliminar todos os ficheiros do dispositivo. Utilize-as deliberadamente, nunca em código que corra automaticamente, e nunca com dados que não controla.

2.34.1. eval

eval() executa uma única expressão e devolve o seu valor:

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

A expressão vê os globais e locais do chamador por omissão, razão pela qual name é resolvido no segundo exemplo. Passar dicionários explícitos permite isolar a avaliação numa sandbox:

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

Mesmo em sandbox, eval é perigoso. Existem técnicas bem conhecidas para escapar a essas sandboxes; não confie apenas no truque do __builtins__ vazio para input 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ções, ciclos – e devolve 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 âmbito local vs global. exec dentro de uma função raramente se comporta como o autor espera; se precisar, execute-o ao nível do módulo.

2.34.3. compile

compile() transforma uma string de código-fonte num objeto de código que pode ser passado a eval() ou exec() posteriormente. Use-o quando a mesma fonte vai ser executada muitas vezes – o parsing ocorre uma vez, 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 segundo argumento é 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 no estilo interativo que imprime o seu resultado.

2.34.4. Quando recorrer a estas funções

Quase nunca. A maioria dos casos de uso que os principiantes imaginam tem alternativas mais seguras:

  • Ler um ficheiro de configuração. Use json – dados estruturados, sem execução.

  • Avaliar um valor numérico introduzido pelo utilizador. Use int() / float() para fazer o parsing e depois aritmética. Se o utilizador precisar mesmo de introduzir uma fórmula, use um parser de expressões pequeno, não eval.

Quando precisar mesmo de eval / exec / compile, isole o ponto de chamada, registe a string exata que está prestes a ser executada e trate a fonte como o elemento mais suspeito do seu código.