2 puntos por GN⁺ 2025-04-11 | 3 comentarios | Compartir por WhatsApp
  • PEP 750 introduce en Python un nuevo literal de cadena: cadenas de plantilla (t"...")
  • Es una forma generalizada de las f-strings que crea un tipo Template, lo que permite preprocesar la combinación de cadenas y valores interpolados antes de convertirla en texto
  • Puede ser útil para plantillas web, revisiones de seguridad y DSL (Domain-Specific Language)

Relación con otros PEP

  • Las f-strings se introdujeron con PEP 498 y su sintaxis se amplió en PEP 701
  • PEP 501 propuso cadenas de plantilla generales (i-string), pero quedó en pausa
  • El actual PEP 750 es una forma simplificada y generalizada de PEP 501, desarrollada a partir de esas ideas previas

Motivación y necesidad

  • Las f-strings son simples, pero como no permiten procesar previamente los valores interpolados, pueden generar problemas de seguridad
  • Existe el riesgo de provocar vulnerabilidades como inyección SQL o ataques XSS
  • Con cadenas de plantilla, los valores interpolados pueden procesarse de antemano para usarse de forma segura

Ejemplo:

  • evil = "<script>alert('evil')</script>"
  • template = t"<p>{evil}</p>"
  • assert html(template) == "<p>&lt;script&gt;alert('evil')&lt;/script&gt;</p>"

Especificación de las cadenas de plantilla

Literales de cadena de plantilla

  • Se definen con el prefijo t o T
  • Se evalúan como el tipo string.templatelib.Template
  • Admiten una sintaxis similar a la de las f-strings, incluyendo anidamiento
  • Pueden combinarse con el prefijo r (rt, tr)
  • No pueden combinarse con los prefijos u ni b
  • No se pueden mezclar f-strings y cadenas de plantilla

Tipo Template

  • Es un tipo inmutable y tiene las siguientes propiedades:
    • strings: tupla de fragmentos de cadena
    • interpolations: tupla de objetos de valores interpolados
    • values: tupla con los valores interpolados
    • __iter__(): iterador que devuelve cadenas y valores interpolados en orden

Tipo Interpolation

  • value: resultado evaluado
  • expression: cadena original de la expresión interpolada
  • conversion: modo de conversión (r, s, a o None)
  • format_spec: cadena de formato

Ejemplo:

  • name = "World"
  • template = t"Hello {name!r}"
  • assert template.interpolations[0].conversion == "r"

Especificador de depuración =

  • t"{value=}" se interpreta como t"value={value!r}"
  • Los espacios también se conservan tal cual (t"{value = }""value = {value!r}")

Concatenación de cadenas de plantilla

  • Con el operador + se puede combinar Template con str, o Template con Template
  • El resultado de la concatenación siempre es de tipo Template
  • También es posible la concatenación implícita de cadenas (t"Hello " t"World")

Cómo procesar cadenas de plantilla

Ejemplo: función para convertir mayúsculas/minúsculas

  • def lower_upper(template):
    • parts = []
    • for s in template:
      • if isinstance(s, str): parts.append(s.lower())
      • else: parts.append(str(s.value).upper())
    • return "".join(parts)

Ejemplo: implementación con el mismo procesamiento que una f-string

  • Con la función f() se puede generar el mismo resultado que una f-string

Ejemplo: logging estructurado

  • Con cadenas de plantilla es posible emitir al mismo tiempo el mensaje de log y los valores estructurados
  • Puede implementarse con StructuredMessage o con una subclase de logging.Formatter

Ejemplo: procesamiento de plantillas HTML

  • La función html() procesa el contenido según la posición de inserción, escapándolo adecuadamente o tratándolo como atributo
  • También admite plantillas anidadas

Patrones de uso avanzados

  • Se recomienda usar pattern matching estructural (sentencia match)
  • Las cadenas estáticas pueden usarse como clave de caché, permitiendo una memoización eficiente
  • También puede analizarse y procesarse como una representación intermedia, por ejemplo un AST
  • Para evaluación lazy o async se pueden usar lambda y await

Relación entre las cadenas de plantilla y las cadenas de formato existentes

  • Es posible definir funciones de plantilla de manera similar a .format()
  • También sería posible from_format() para analizar cadenas externas y convertirlas en Template

Compatibilidad, seguridad y aprendizaje

  • En versiones antiguas de Python puede producirse un error de sintaxis
  • En términos de seguridad, el procesamiento con plantillas mejora la seguridad
  • Como la sintaxis es similar a la de las f-strings, es fácil de aprender

¿Por qué un nuevo enfoque de plantillas?

  • Las plantillas existentes, como Jinja, suelen estar dirigidas a usuarios finales o diseñadores
  • Hace falta soporte a nivel del lenguaje Python para que los desarrolladores puedan trabajar directamente con plantillas
  • Así se pueden aprovechar ventajas como la expresividad y la verificación de tipos

Resumen de patrones de ejemplo

  • Pattern matching estructural y matching de subpropiedades
  • Reutilización de plantillas como si fueran funciones
  • Soporte para plantillas anidadas
  • Soporte para evaluación lazy/async
  • Optimización de caché separando partes estáticas y dinámicas

Otras consideraciones de diseño

  • Las plantillas no se convierten en cadenas y __str__() no está implementado
  • Las clases relacionadas se ofrecen en el módulo string.templatelib
  • Template e Interpolation se comparan por identidad de objeto
  • No admiten operaciones == ni <

Implementación de referencia y ejemplos

Ideas rechazadas

  • Uso de prefijos arbitrarios (my_tag"...")
  • Evaluación diferida de todas las expresiones interpoladas
  • Implementación mediante protocolo
  • Redefinir __eq__ y __hash__
  • Restauración completa de la cadena original
  • Agregar un tipo Decoded
  • Soporte para cadenas de plantilla binarias
  • Función para especificar tipos de formato ("html", "sql", etc.)
  • Restringir la concatenación de cadenas
  • Permitir conversores arbitrarios (!x)

3 comentarios

 
carnoxen 2025-04-11

El formateo más satisfactorio me sigue pareciendo que solo lo tienen JS y Python. Los demás lenguajes, pues...

 
kandk 2025-04-11

Habrá una forma obvia de hacerlo, y preferiblemente solo una. (There should be one-- and preferably only one --obvious way to do it.)

 
GN⁺ 2025-04-11
Comentarios de Hacker News
  • Es interesante cómo distintos lenguajes manejan el formateo de cadenas

    • Java está intentando agregar f/t-strings, pero está teniendo dificultades por un perfeccionismo que busca resolver todos los problemas
    • Los desarrolladores de Go parecen haber ignorado este problema casi por completo sin considerarlo mucho
    • Python está adoptando un enfoque equilibrado, discutiendo nuevos métodos de formateo de cadenas y usando la implementación adecuada que elige
    • Es difícil no estar de acuerdo con el enfoque de Python, y se está obteniendo valor de .format(), f-strings y t-strings
  • Nick Humrich es uno de los autores que reescribió PEP 501 para introducir t-strings, y está muy contento con la aceptación de este PEP

    • Empezó a trabajar en PEP 501 hace 4 años
  • No está claro si una función a nivel de lenguaje realmente aporta valor

    • Se puede obtener el mismo resultado con una función que devuelva un f-string
    • Si se quiere seguridad contra inyecciones, basta con usar un tipo etiquetado y una función de saneamiento que devuelva una cadena
    • Es conciso, pero distinguir entre ejecución inmediata y diferida con una sola letra puede dificultar la lectura para quienes no están familiarizados con Python
  • Me gustan los f-strings, pero tienen el problema de que no se puede diferir su evaluación

    • A veces hay que usar str.format, y eso resulta incómodo
  • Como mantenedor de lit-html, me parece interesante la similitud con los tagged template literals de JavaScript

    • Es singular la manera en que la clase Template de Python separa la función etiquetada y los argumentos, a diferencia de JavaScript
    • En estructuras de plantillas anidadas, quizá no haga falta la función html()
  • Parece prometedor que lo útil de los tagged template literals de JavaScript para el autoescape de HTML o la parametrización de SQL también pueda aplicarse en Python

  • Hay opiniones de que Python se está convirtiendo en PHP

    • Los f-strings y t-strings añaden complejidad al lenguaje
    • Se considera que string.format es lo óptimo, y % también sigue siendo aceptable por el tiempo que lleva usándose
    • Ojalá el equipo del lenguaje se concentre en cosas más importantes
  • Hay quejas sobre seguir agregando cosas nuevas al lenguaje

    • Da la sensación de que el lenguaje fue diseñado por un comité
  • Hay quien opina que este PEP es similar al P1819 de C++

  • También hay opiniones de que el código del PEP es demasiado verboso

    • Parece que Python expresa demasiadas cosas innecesarias en lugar de ser pseudocódigo ejecutable
    • Comparado con Ruby, el código de Python es más verboso