From 08f25fcd0d2a940b66b4aa2f678a7c9b9f1f39c7 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 31 Jul 2025 14:29:24 -0700 Subject: [PATCH 1/5] First attempt at updating PEP 750 What's New in Python 3.14 --- Doc/whatsnew/3.14.rst | 106 ++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 36 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 49406a1ece6235..66185713452891 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -251,64 +251,98 @@ Also added in 3.14: :ref:`concurrent.futures.InterpreterPoolExecutor PEP 750: Template strings ------------------------- -Template string literals (t-strings) are a generalization of f-strings, -using a ``t`` in place of the ``f`` prefix. Instead of evaluating -to :class:`str`, t-strings evaluate to a new :class:`!string.templatelib.Template` type: +Template strings are a new mechanism for custom string processing. They share +the familiar syntax of f-strings but, unlike f-strings, return a +:class:`~string.templatelib.Template` instance instead of a simple ``str``. +Templates provide access to the static and interpolated (in curly braces) parts +of a string *before* they are combined. -.. code-block:: python +To write a t-string, use a ``'t'`` prefix instead of an ``'f'``: - from string.templatelib import Template +.. doctest:: - name = "World" - template: Template = t"Hello {name}" + >>> variety = "Stilton" + >>> template = t"Try some {variety} cheese!" + >>> type(template) + -The template can then be combined with functions that operate on the template's -structure to produce a :class:`str` or a string-like result. -For example, sanitizing input: +Iterate over :class:`!Template` instances to access their parts in order: -.. code-block:: python +.. testsetup:: + + variety = "Stilton" + template = t"Try some {variety} cheese!" + +.. doctest:: - evil = "" - template = t"

{evil}

" - assert html(template) == "

<script>alert('evil')</script>

" + >>> list(template) + ['Try some ', Interpolation('Stilton', 'variety', None, ''), ' cheese!'] -As another example, generating HTML attributes from data: +It's easy to write (or call) code to process :class:`!Template` instances. +For example, here's a function that renders static parts lowercase and +:class:`~string.templatelib.Interpolation` instances uppercase: .. code-block:: python - attributes = {"src": "shrubbery.jpg", "alt": "looks nice"} - template = t"" - assert html(template) == 'looks nice' + from string.templatelib import Template, Interpolation + + def lower_upper(template: Template) -> str: + """Render static parts lowercase and interpolations uppercase.""" + parts: list[str] = [] + for part in template: + if isinstance(part, Interpolation): + parts.append(str(part.value).upper()) + else: + parts.append(part.lower()) + return "".join(parts) -Compared to using an f-string, the ``html`` function has access to template attributes -containing the original information: static strings, interpolations, and values -from the original scope. Unlike existing templating approaches, t-strings build -from the well-known f-string syntax and rules. Template systems thus benefit -from Python tooling as they are much closer to the Python language, syntax, -scoping, and more. + name = "Wenslydale" + template = t"Mister {name}" + assert lower_upper(template) == "mister WENSLYDALE" -Writing template handlers is straightforward: +Because :class:`Template` instances distinguish between static strings and +interpolations at runtime, they are useful for sanitizing user input. Here's +a simple example that escapes user input in HTML: .. code-block:: python from string.templatelib import Template, Interpolation + from html import escape - def lower_upper(template: Template) -> str: - """Render static parts lowercased and interpolations uppercased.""" + def html(template: Template) -> str: + """Escape HTML in interpolations.""" parts: list[str] = [] - for item in template: - if isinstance(item, Interpolation): - parts.append(str(item.value).upper()) + for part in template: + if isinstance(part, Interpolation): + parts.append(escape(str(part.value))) else: - parts.append(item.lower()) + parts.append(part) return "".join(parts) - name = "world" - assert lower_upper(t"HELLO {name}") == "hello WORLD" + user_supplied = "" + template = t"

{user_supplied}

" + rendered = html(template) + assert rendered == "

<script>alert('cheese')</script>

" + +Beyond simply sanitizing input, template processing code can provide flexibility +and better developer experience. For instance, a more advanced ``html()`` +implementation could accept a ``dict`` of HTML attributes directly in the +template: + +.. code-block:: python + + attributes = {"src": "limburger.jpg", "alt": "a cheese"} + template = t"" + rendered = html(template) + assert rendered == 'a cheese' + +Of course, template processing code does not need to return a ``str`` or +string-like result. For example, an even *more* advanced ``html()`` could +return a custom ``Element`` type representing a DOM-like structure. -With this in place, developers can write template systems to sanitize SQL, make -safe shell operations, improve logging, tackle modern ideas in web development -(HTML, CSS, and so on), and implement lightweight, custom business DSLs. +With t-strings in place, developers can write systems that sanitize SQL, +make safe shell operations, improve logging, tackle modern ideas in web +development (HTML, CSS, and so on), and implement lightweight custom business DSLs. (Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono, Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran, From d6fed461bf1398084620912dfc18c43b49c4353c Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 31 Jul 2025 14:43:34 -0700 Subject: [PATCH 2/5] Fix :class: ref bug --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 66185713452891..62546e8818a595 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -300,7 +300,7 @@ For example, here's a function that renders static parts lowercase and template = t"Mister {name}" assert lower_upper(template) == "mister WENSLYDALE" -Because :class:`Template` instances distinguish between static strings and +Because :class:`!Template` instances distinguish between static strings and interpolations at runtime, they are useful for sanitizing user input. Here's a simple example that escapes user input in HTML: From ee6ac99798ed662fed62f037a141d1e57d583676 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 31 Jul 2025 15:29:20 -0700 Subject: [PATCH 3/5] Fix doctest indentation --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 62546e8818a595..355b7c426456fb 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -264,7 +264,7 @@ To write a t-string, use a ``'t'`` prefix instead of an ``'f'``: >>> variety = "Stilton" >>> template = t"Try some {variety} cheese!" >>> type(template) - + Iterate over :class:`!Template` instances to access their parts in order: From 99331f442705ba9b7a6b8c7a4eeccefe7c1d127b Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 31 Jul 2025 15:30:50 -0700 Subject: [PATCH 4/5] Move one sentence. --- Doc/whatsnew/3.14.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 355b7c426456fb..aa58ae319fb6dc 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -254,8 +254,6 @@ PEP 750: Template strings Template strings are a new mechanism for custom string processing. They share the familiar syntax of f-strings but, unlike f-strings, return a :class:`~string.templatelib.Template` instance instead of a simple ``str``. -Templates provide access to the static and interpolated (in curly braces) parts -of a string *before* they are combined. To write a t-string, use a ``'t'`` prefix instead of an ``'f'``: @@ -266,7 +264,9 @@ To write a t-string, use a ``'t'`` prefix instead of an ``'f'``: >>> type(template) -Iterate over :class:`!Template` instances to access their parts in order: +Templates provide access to the static and interpolated (in curly braces) parts +of a string *before* they are combined. Iterate over :class:`!Template` +instances to access their parts in order: .. testsetup:: From eacac3337e80c2a5ec3fb3194059fb783c0228e3 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 31 Jul 2025 15:33:29 -0700 Subject: [PATCH 5/5] Too many 'for example' --- Doc/whatsnew/3.14.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index aa58ae319fb6dc..4fecebb4facdef 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -337,8 +337,8 @@ template: assert rendered == 'a cheese' Of course, template processing code does not need to return a ``str`` or -string-like result. For example, an even *more* advanced ``html()`` could -return a custom ``Element`` type representing a DOM-like structure. +string-like result. An even *more* advanced ``html()`` could return a custom ``Element`` +type representing a DOM-like structure. With t-strings in place, developers can write systems that sanitize SQL, make safe shell operations, improve logging, tackle modern ideas in web