From 4ec455858376338b448ad4317aa36735ce04b879 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 8 Oct 2025 09:28:46 +0200 Subject: [PATCH] Add support for t-strings Python 3.14 introduces [t-strings][PEP750], a generalisation of f-strings which yields a processable `Template` object instead of reifying a string. The safety assumption around t-strings is that the literal parts of the template are considered "safe" and the interpolations are considered unsafe. So for markupsafe the literal parts are `Markup`-ed while the interpolations are `escape`-ed. This commit adds support for t-strings to both `Markup` and `escape`, essentially matching the handling of `__html__`. It does *not* add support for t-strings to: - `Markup.__add__` / `Markup.__radd__`, the semantics seem a bit dubious and it should be easy enough to `Markup` or `escape` the t-string / template object in that case. - `EscapeFormatter` as I'm not entirely sure whether / how a template object should interact with format specs. Fixes #511 [PEP750]: https://peps.python.org/pep-0750/ --- CHANGES.rst | 1 + docs/formatting.rst | 20 ++++++++++++++++++++ src/markupsafe/__init__.py | 35 +++++++++++++++++++++++++++++++++-- tests/test_markupsafe.py | 27 +++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7a94e8f8..c5ee9875 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,7 @@ Unreleased - Drop support for Python 3.9. - Remove previously deprecated code. +- Support Python 3.14 template strings. :issue:`511` Version 3.0.3 diff --git a/docs/formatting.rst b/docs/formatting.rst index c14f9170..3a786650 100644 --- a/docs/formatting.rst +++ b/docs/formatting.rst @@ -6,6 +6,26 @@ String Formatting The :class:`Markup` class can be used as a format string. Objects formatted into a markup string will be escaped first. +t-strings +--------- + +.. versionadded:: 3.1.0 + +On Python 3.14 :ref:`python:t-strings` can be passed to :class:`Markup` +(or :func:`escape`), any interpolated value will be escaped while literal +content will be treated as as markup. + +.. code-block:: pycon + + >>> uid, name = 3, "