O pré-processador do Python
(pydong.org)O pré-processador do Python
- A afirmação de que Python não tem pré-processador não é verdadeira
- Python tem um pré-processador muito poderoso
Codificação do código-fonte em Python
- Graças à PEP-0263, é possível definir a codificação do código-fonte
- A codificação pode ser configurada adicionando um comentário mágico nas duas primeiras linhas
- Exemplos:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
Arquivos de configuração de caminho (.pth)
- Se o interpretador Python iniciar sem a opção
-S, ele carrega automaticamente o pacotesite - É possível expandir o caminho de busca de módulos adicionando arquivos
.pthna pastasite-packages - Linhas em arquivos
.pthque começam comimportsão executadas - Com isso, é possível executar código arbitrário durante a inicialização do interpretador Python
Definição de codecs personalizados
- O interpretador Python precisa de duas coisas para aceitar isso:
- função
decode(data: bytes) -> tuple[str, int] - classe de decodificador incremental
- função
- Use
codecs.utf_8_decodepara fazer a decodificação real e depois passe a string resultante ao pré-processador - É recomendável capturar exceções, imprimi-las e lançá-las novamente
Fornecendo um decodificador incremental
- Implemente o decodificador incremental herdando de
codecs.BufferedIncrementalDecoder - Colete os dados no buffer e pré-processe o arquivo inteiro na chamada final de decodificação
Estendendo o Python
- É relativamente fácil estender o Python usando a biblioteca padrão da linguagem
- É possível usar o módulo
tokenizepara modificar o fluxo de tokens do arquivo ou o móduloastpara modificar a árvore de sintaxe abstrata
Incremento e decremento unários
- Python não possui operadores unários de incremento e decremento
x++,x--não são válidos++x,--xsão válidos, mas têm outro significado- É possível converter expressões de incremento e decremento unários em expressões Python
Exemplo
- Arquivo de entrada
incdec.py:# coding: magic.incdec i = 6 assert i-- == 6 assert i == 5 assert ++i == 6 assert --i == 5 assert i++ == 5 assert i == 6 assert (++i, 'i++') == (7, 'i++') print("PASSED") - Arquivo convertido:
i = 6 assert ((i, i := i - 1)[0]) == 6 assert i == 5 assert ((i, i := i + 1)[1]) == 6 assert ((i, i := i - 1)[1]) == 5 assert ((i, i := i + 1)[0]) == 5 assert i == 6 assert (((i, i := i + 1)[1]), 'i++') == (7, 'i++') print("PASSED")
Python com chaves (Bython)
- É possível usar chaves para delimitar escopos em vez de indentação no Python
- Use
tokenize.generate_tokenspara modificar o fluxo de tokens
Exemplo
- Arquivo de entrada
test.by:# coding: magic.braces def print_message(num_of_times) { for i in range(num_of_times) { print("braces ftw") } print({'x': 3}) } x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__" { print_message(2) print({k: v for k, v in x.items()}) } - Arquivo convertido:
def print_message(num_of_times): for i in range(num_of_times): print("braces ftw") print({'x': 3}) x = { 'foo': 42, 'bar': 5 } if __name__ == "__main__": print_message(2) print({k: v for k, v in x.items()})
Interpretando outras linguagens
- É possível fazer o interpretador Python interpretar outras linguagens
- Exemplos: C, C++, TOML
Exemplo
- Arquivo C++
test.cpp:#define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } - Arquivo convertido:
import cppyy cppyy.cppdef(r""" #define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } """) from cppyy.gbl import main if __name__ == "__main__": main()
Validação de dados
- É possível validar dados em formato TOML usando JSON Schema
- Use
jsonschemapara realizar a validação de fato
Exemplo
- Arquivo de schema
schema.json:{ "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "number"}, "scores": { "type": "array", "items": {"type": "number"} }, "address": {"$ref": "#/$defs/address"} }, "required": ["name"], "$defs": { "address": { "type": "object", "properties": { "street": {"type": "string"}, "postcode": {"type": "number"} }, "required": ["street"] } } } - Arquivo de dados válido
data_valid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, 20, 80, 90] [address] street = "Grove St. 4" postcode = 19201 - Arquivo de dados inválido
data_invalid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, "20", 80, 90] [address] street = "Grove St. 4" postcode = 19201
Conclusão
- Com codecs personalizados e arquivos de configuração de caminho, é possível alterar bastante o comportamento do interpretador Python
- Alguns exemplos são
pythonql,future-typing,future-fstrings,future-annotations - É possível usar
magic_codecpara experimentar facilmente seu próprio pré-processador
Resumo do GN⁺
- É possível usar o pré-processador do Python para realizar várias extensões de linguagem e validação de dados
- O texto explica como alterar o comportamento do interpretador Python por meio de codecs personalizados
- Este artigo oferece ferramentas e técnicas úteis para desenvolvedores Python
- Projetos com funcionalidades semelhantes incluem
pythonqlefuture-typing
1 comentários
Comentários no Hacker News
A mensagem de erro de sintaxe de
from __future__ import bracesestá hardcoded no cpython desde 2001Pensei em formas criativas de demissão usando import-hooks, mas foi uma pena que o regex de codec impeça usar algo como
μtf8sys.settracepara fazer monkey patch de todas as funções para a função chamada anteriormente, além de trocarstdoutestderra cada 17 minutosExiste um motivo para o Python não expor hooks de preprocessador, e acho que adultos razoáveis deveriam evitar isso
Um preprocessador é mais prático e útil
exece depois inserindoexit()Adoro a flexibilidade do Python
mmappara fazer um script se modificar sozinhoO melhor caso de uso com pyxl veio da inspiração no jsx
# coding: pyxlFico me perguntando se a transição do Python 2 para o 3 poderia ter sido tratada melhor
# coding: six.python2poderia tornar código Python2 válido em Python3, e# coding: six.python3poderia ajustar código Python3 para rodar em Python2Fico feliz que tenham gostado da ideia; em breve deve sair mais conteúdo
Faz tempo que eu não me surpreendia com uma ideia completamente nova
Se você quer geração de código inline em Python, pode usar o cog do Ned Batchelder
Fico pensando se dependências introduzidas por essa estratégia de hooks de codificação seriam detectadas por
pip freezeou pelo uv