El preprocesador de Python
(pydong.org)El preprocesador de Python
- La afirmación de que Python no tiene preprocesador no es cierta
- Python sí tiene un preprocesador muy potente
Codificación del código fuente en Python
- Gracias a PEP-0263, se puede definir la codificación del código fuente
- Se puede configurar la codificación agregando un comentario mágico en las primeras dos líneas
- Ejemplos:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
Archivos de configuración de ruta (.pth)
- Si el intérprete de Python se inicia sin la opción
-S, carga automáticamente el paquetesite - Se puede ampliar la ruta de búsqueda de módulos agregando archivos
.pthen la carpetasite-packages - Las líneas de los archivos
.pthque comienzan conimportse ejecutan - Esto permite ejecutar código arbitrario durante la inicialización del intérprete de Python
Definición de códecs personalizados
- El intérprete de Python necesita dos cosas para quedar satisfecho:
- una función
decode(data: bytes) -> tuple[str, int] - una clase de decodificador incremental
- una función
- Se usa
codecs.utf_8_decodepara realizar la decodificación real y luego pasar la cadena resultante al preprocesador - Es recomendable capturar las excepciones, mostrarlas y volver a lanzarlas
Proveer un decodificador incremental
- Se implementa un decodificador incremental heredando de
codecs.BufferedIncrementalDecoder - Los datos se acumulan en el búfer y, en la llamada final de decodificación, se preprocesa el archivo completo
Extender Python
- Extender Python usando su biblioteca estándar es relativamente sencillo
- Se puede modificar el flujo de tokens de un archivo con el módulo
tokenize, o modificar el árbol de sintaxis abstracta con el móduloast
Incremento y decremento unarios
- Python no tiene operadores unarios de incremento y decremento
x++,x--no son válidos++x,--xsí son válidos, pero tienen otro significado- Las expresiones de incremento y decremento unario pueden convertirse en expresiones de Python
Ejemplo
- Archivo 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") - Archivo transformado:
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 con llaves (Bython)
- Se puede delimitar el alcance con llaves en lugar de la indentación de Python
- Se usa
tokenize.generate_tokenspara modificar el flujo de tokens
Ejemplo
- Archivo 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()}) } - Archivo transformado:
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()})
Interpretar otros lenguajes
- Se puede hacer que el intérprete de Python interprete otros lenguajes
- Ejemplos: C, C++, TOML
Ejemplo
- Archivo C++
test.cpp:#define CODEC "coding:magic.cpp" #include <cstdio> int main() { puts("Hello World"); } - Archivo transformado:
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()
Validación de datos
- Se pueden validar datos en formato TOML usando un esquema JSON
- Se usa
jsonschemapara realizar la validación real
Ejemplo
- Archivo de esquema
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"] } } } - Archivo de datos 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 - Archivo de datos no válido
data_invalid.toml:# coding: magic.toml name = "John Doe" age = 42 scores = [40, "20", 80, 90] [address] street = "Grove St. 4" postcode = 19201
Conclusión
- Se puede cambiar enormemente el comportamiento del intérprete de Python usando códecs personalizados y archivos de configuración de ruta
- Algunos ejemplos son
pythonql,future-typing,future-fstrings,future-annotations - Se puede experimentar fácilmente con tu propio preprocesador usando
magic_codec
Resumen de GN⁺
- Se puede aprovechar el preprocesador de Python para realizar varias extensiones de lenguaje y validación de datos
- Se explica cómo cambiar el comportamiento del intérprete de Python mediante códecs personalizados
- Este artículo ofrece herramientas y técnicas útiles para desarrolladores de Python
- Proyectos con funciones similares incluyen
pythonql,future-typing, entre otros
1 comentarios
Comentarios de Hacker News
El mensaje de error de sintaxis de
from __future__ import bracesestá hardcodeado en cpython desde 2001Pensé en formas creativas de despido usando import-hooks, pero fue una lástima que la regex de codec impidiera usar algo como
μtf8sys.settracepara hacer monkey patch de todas las funciones con la función llamada previamente, y reemplazar stdout y stderr cada 17 minutosHay una razón por la que Python no expone hooks de preprocesador, y creo que cualquier adulto razonable debería evitarlos
El preprocesador es más cómodo y útil
exit()Me encanta la flexibilidad de Python
El mejor caso de uso de pyxl está inspirado en jsx
# coding: pyxlMe pregunto si la transición de Python 2 a 3 podría haberse manejado mejor
# coding: six.python2puede hacer que código de Python2 sea válido en Python3, y# coding: six.python3puede ajustar código de Python3 para que pueda ejecutarse en Python2Me alegra que les guste esta idea; pronto habrá más
Después de mucho tiempo, me sorprendió una idea completamente nueva
Si quieres generación de código inline en Python, puedes usar cog de Ned Batchelder
Me pregunto si las dependencias introducidas con esta estrategia de hooks de codificación son detectadas por
pip freezeo uv