2 points par GN⁺ 2025-04-11 | 3 commentaires | Partager sur WhatsApp
  • La PEP 750 introduit dans Python un nouveau littéral de chaîne, les chaînes de modèle (t"...")
  • Il s’agit d’une forme généralisée des f-strings, qui produit un type Template afin de permettre un traitement préalable combinant la chaîne et les valeurs interpolées
  • Cela peut être utile pour les templates web, les contrôles de sécurité, les DSL (Domain-Specific Language), etc.

Relation avec les autres PEP

  • Les f-strings ont été introduites par la PEP 498, puis leur syntaxe a été étendue dans la PEP 701
  • La PEP 501 proposait des chaînes de modèle généralisées (i-string), mais elle a été mise en attente
  • La PEP 750 est aujourd’hui une forme simplifiée et généralisée de la PEP 501, développée à partir des idées existantes

Motivation et nécessité

  • Les f-strings sont simples, mais comme elles ne permettent pas de prétraiter les valeurs interpolées, elles peuvent poser des problèmes de sécurité
  • Il existe un risque de vulnérabilités telles que l’injection SQL ou les attaques XSS
  • Les chaînes de modèle permettent de prétraiter les valeurs interpolées afin de les utiliser en toute sécurité

Exemple :

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

Spécification des chaînes de modèle

Littéraux de chaîne de modèle

  • Définis avec le préfixe t ou T
  • Évalués comme le type string.templatelib.Template
  • Prennent en charge une syntaxe similaire aux f-strings, y compris l’imbrication
  • Peuvent être combinés avec le préfixe r (rt, tr)
  • Ne peuvent pas être combinés avec les préfixes u et b
  • Il n’est pas possible de mélanger f-string et chaîne de modèle

Type Template

  • Type immuable avec les attributs suivants :
    • strings : tuple de fragments de chaîne
    • interpolations : tuple d’objets représentant les interpolations
    • values : tuple des valeurs interpolées
    • __iter__() : itérateur renvoyant successivement les chaînes et les interpolations

Type Interpolation

  • value : résultat évalué
  • expression : chaîne de l’expression interpolée d’origine
  • conversion : mode de conversion (r, s, a ou None)
  • format_spec : chaîne de format

Exemple :

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

Spécificateur de débogage =

  • t"{value=}" est interprété comme t"value={value!r}"
  • Les espaces sont également conservés tels quels (t"{value = }""value = {value!r}")

Concaténation de chaînes de modèle

  • L’opérateur + permet de combiner un Template avec un str, ou un Template avec un autre Template
  • Le résultat de la concaténation est toujours de type Template
  • La concaténation implicite de chaînes est aussi possible (t"Hello " t"World")

Méthodes de traitement des chaînes de modèle

Exemple : fonction de mise en minuscules/majuscules

  • 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)

Exemple : implémentation du même traitement qu’une f-string

  • Une fonction f() peut produire le même résultat qu’une f-string

Exemple : journalisation structurée

  • Les chaînes de modèle permettent d’afficher simultanément un message de log et des valeurs structurées
  • Cela peut être implémenté via StructuredMessage ou une sous-classe de logging.Formatter

Exemple : traitement de templates HTML

  • La fonction html() traite le contenu par échappement ou comme attribut selon la position d’insertion
  • Les templates imbriqués sont également pris en charge

Modèles d’utilisation avancés

  • Utilisation recommandée du filtrage par motif structurel (instruction match)
  • Les chaînes statiques peuvent servir de clés de cache, ce qui permet une mémoïsation efficace
  • Il est possible d’analyser et de traiter via une représentation intermédiaire telle que l’AST
  • lambda et await peuvent être utilisés pour une évaluation lazy ou async

Relation entre les chaînes de modèle et les chaînes de format existantes

  • Il est possible de définir des fonctions de template d’une manière proche de .format()
  • Un from_format() permettant d’analyser une chaîne externe et de la convertir en Template est également possible

Compatibilité, sécurité, apprentissage

  • Sur les anciennes versions de Python, cela peut provoquer une erreur de syntaxe
  • Du point de vue sécurité, le traitement par template améliore la sûreté
  • La syntaxe, proche des f-strings, facilite l’apprentissage

Pourquoi une nouvelle approche des templates ?

  • Les templates existants comme Jinja visent surtout les utilisateurs finaux ou les designers
  • Il est nécessaire d’avoir une prise en charge au niveau du langage Python pour que les développeurs puissent manipuler directement les templates
  • Cela permet de tirer parti d’avantages comme l’expressivité ou la vérification de types

Récapitulatif des modèles d’exemple

  • Filtrage par motif structurel et appariement sur les sous-attributs
  • Réutilisation des templates comme des fonctions
  • Prise en charge des templates imbriqués
  • Prise en charge de l’évaluation lazy/async
  • Optimisation du cache grâce à la séparation statique/dynamique

Autres considérations de conception

  • Un template n’est pas converti en chaîne, et __str__() n’est pas implémentée
  • Les classes associées sont fournies par le module string.templatelib
  • Template et Interpolation sont comparés selon l’identité des objets
  • Les opérations == et < ne sont pas prises en charge

Implémentation de référence et exemples

Idées rejetées

  • Utilisation de préfixes arbitraires (my_tag"...")
  • Évaluation différée de toutes les expressions interpolées
  • Implémentation sous forme de protocole
  • Redéfinition de __eq__ et __hash__
  • Restauration complète de la chaîne d’origine
  • Ajout d’un type Decoded
  • Prise en charge de chaînes de modèle binaires
  • Fonction de spécification du type de format ("html", "sql", etc.)
  • Restriction de la concaténation de chaînes
  • Autorisation de convertisseurs arbitraires (!x)

3 commentaires

 
carnoxen 2025-04-11

Je trouve que les formats les plus satisfaisants, il n’y a que JavaScript et Python. Les autres langages, bof…

 
kandk 2025-04-11

Il devrait exister une manière évidente de le faire — et, de préférence, une seule.

 
GN⁺ 2025-04-11
Avis Hacker News
  • Il est intéressant de voir comment différents langages gèrent le formatage des chaînes de caractères

    • Java essaie d’ajouter les f/t-strings, mais se heurte à des difficultés à cause d’un perfectionnisme qui cherche à résoudre tous les problèmes
    • Les développeurs Go semblent avoir largement ignoré cette question sans vraiment s’y intéresser
    • Python adopte une approche équilibrée en discutant de nouvelles méthodes de formatage de chaînes et en choisissant des implémentations adaptées à l’usage
    • Il est difficile de ne pas être d’accord avec l’approche de Python, qui apporte de la valeur avec .format(), les f-strings et les t-strings
  • Nick Humrich est l’un des auteurs ayant réécrit la PEP 501 pour introduire les t-strings, et il est très heureux de l’acceptation de cette PEP

    • Il a commencé à travailler sur la PEP 501 il y a 4 ans
  • Certains ne sont pas convaincus de la valeur d’une fonctionnalité au niveau du langage

    • On peut obtenir le même résultat avec une fonction qui renvoie une f-string
    • Si l’on veut une sécurité contre les injections, il suffit d’utiliser un type balisé et une fonction de sanitation qui renvoie une chaîne
    • C’est concis, mais distinguer l’exécution immédiate de l’exécution différée par une seule lettre peut rendre la lecture difficile pour ceux qui ne connaissent pas bien Python
  • Les f-strings sont appréciées, mais elles ont le défaut de ne pas permettre de différer l’évaluation

    • Il faut parfois utiliser str.format, ce qui est peu pratique
  • En tant que mainteneur de lit-html, certains trouvent intéressante la similarité avec les tagged template literals de JavaScript

    • La manière dont la classe Template de Python sépare la fonction balisée et les arguments est originale par rapport à JavaScript
    • Dans une structure de templates imbriqués, la fonction html() pourrait ne pas être nécessaire
  • Certains espèrent que les avantages des tagged template literals de JavaScript pour l’auto-escaping HTML ou la paramétrisation SQL s’appliqueront aussi à Python

  • Certains ont l’impression que Python est en train de devenir PHP

    • Les f-strings et les t-strings ajoutent de la complexité au langage
    • string.format leur semble être la meilleure option, et % reste acceptable puisqu’il est utilisé depuis longtemps
    • Ils aimeraient que l’équipe du langage se concentre sur des sujets plus importants
  • Il y a aussi des critiques sur l’ajout constant de nouveautés au langage

    • Cela donne l’impression d’un langage conçu par comité
  • Certains trouvent que cette PEP ressemble à la P1819 de C++

  • D’autres jugent que le code de la PEP est beaucoup trop verbeux

    • Python donne l’impression d’exprimer un excès de détails inutiles plutôt que d’être un pseudo-code exécutable
    • Comparé à Ruby, le code Python paraît plus verbeux