PEP 750: aprobadas las cadenas de plantilla (t-strings)
(peps.python.org)- 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><script>alert('evil')</script></p>"
Especificación de las cadenas de plantilla
Literales de cadena de plantilla
- Se definen con el prefijo
toT - 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
unib - 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 cadenainterpolations: tupla de objetos de valores interpoladosvalues: tupla con los valores interpolados__iter__(): iterador que devuelve cadenas y valores interpolados en orden
Tipo Interpolation
value: resultado evaluadoexpression: cadena original de la expresión interpoladaconversion: modo de conversión (r,s,ao 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 comot"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 combinarTemplateconstr, oTemplateconTemplate - 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
StructuredMessageo con una subclase delogging.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
lambdayawait
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 enTemplate
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 TemplateeInterpolationse comparan por identidad de objeto- No admiten operaciones
==ni<
Implementación de referencia y ejemplos
- Se ofrece la implementación de CPython
- También hay ejemplos y pruebas
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
El formateo más satisfactorio me sigue pareciendo que solo lo tienen JS y Python. Los demás lenguajes, pues...
Habrá una forma obvia de hacerlo, y preferiblemente solo una. (There should be one-- and preferably only one --obvious way to do it.)
Comentarios de Hacker News
Es interesante cómo distintos lenguajes manejan el formateo de cadenas
.format(), f-strings y t-stringsNick 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
No está claro si una función a nivel de lenguaje realmente aporta valor
Me gustan los f-strings, pero tienen el problema de que no se puede diferir su evaluación
str.format, y eso resulta incómodoComo mantenedor de lit-html, me parece interesante la similitud con los tagged template literals de JavaScript
Templatede Python separa la función etiquetada y los argumentos, a diferencia de JavaScripthtml()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
string.formates lo óptimo, y%también sigue siendo aceptable por el tiempo que lleva usándoseHay quejas sobre seguir agregando cosas nuevas al lenguaje
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