Skip to content

Instantly share code, notes, and snippets.

@eevmanu
Last active June 17, 2025 02:43
Show Gist options
  • Save eevmanu/17eeaf10a46b54c305c0e3c59e21029b to your computer and use it in GitHub Desktop.
Save eevmanu/17eeaf10a46b54c305c0e3c59e21029b to your computer and use it in GitHub Desktop.
presentation about t-strings, slides content to generate slides using marp (Markdown Presentation Ecosystem) format
theme paginate marp
default
true
true

PEP 750: Template Strings

  • Overview of new t-strings feature in Python 3.14+

  • Deck links to code snippets in snippets/ directory


Executive Summary

  • t"Hello {name}" → returns a Template object

  • Separates literal parts and evaluated expressions

  • Enables inspection, transformation, sanitization before rendering


Key Benefits

  • Interception & Transformation Sanitize or escape interpolations to prevent injection

  • Separation of Concerns Cache static literal text; recompute only dynamic values

  • Flexible Processing Render to HTML, AST, JSON, DSL outputs

  • Eager but Inspectable Eager f-string evaluation + introspection metadata


The Core Benefit: a Distinct Template Type

  • t-strings produce a Template object that retains

  • the literal slices (Template.strings)

  • the raw values (Template.values)


Run Interactive Mode

Use uv to run interactive mode in a ephemeral virtual environment with Python beta version - 3.14.0b1:

$ uv run --python 3.14.0b1 -- python

Pattern 1: Sanitized HTML Generation

from string.templatelib import Template, Interpolation
from html import escape

def html(template: Template) -> str:
    parts = []
    for part in template:
        if isinstance(part, str):
            parts.append(part)
        else:
            value = escape(str(part.value))
            parts.append(value)
    return "".join(parts)

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

Pattern 2: Structured Logging

import json, logging
from string.templatelib import Template, Interpolation

def format_log(template: Template) -> tuple[str, dict]:
    msg = []
    data = {}
    for part in template:
        if isinstance(part, str):
            msg.append(part)
        else:
            data[part.expression] = part.value
            msg.append(str(part.value))
    return "".join(msg), data

logger = logging.getLogger()
action, amount, item = "traded", 42, "shrubs"
template = t"User {action}: {amount:.2f} {item}"
message, payload = format_log(template)
# message → "User traded: 42.00 shrubs"
# payload → {"action":"traded","amount":42,"item":"shrubs"}

Pattern 3: Reusable Templates

from string.templatelib import Template, Interpolation

def make_greeting(name: str) -> Template:
    return t"Hello, {name}!"

def convert(value: object, conversion: Literal["a", "r", "s"] | None)
    -> object:
    """Convert the value to a string using the specified conversion."""
    # Python has no convert() built-in function, so we have to implement it.
    if conversion == "a":
        return ascii(value)
    if conversion == "r":
        return repr(value)
    if conversion == "s":
        return str(value)
    return value

Pattern 3: Reusable Templates (continued)

def f(template: Template) -> str:
    """Implement f-string behavior using the PEP 750 t-string behavior."""
    parts = []
    for item in template:
        match item:
            case str() as s:
                parts.append(s)
            case Interpolation(value, _, conversion, format_spec):
                value = convert(value, conversion)
                value = format(value, format_spec)
                parts.append(value)
    return "".join(parts)

# Reuse with different names
for person in ["Alice", "Bob", "Carol"]:
    tmpl = make_greeting(person)
    # Render via f-string behavior
    print(f(tmpl))  # “Hello, Alice!”, then “Hello, Bob!”, etc.

Some Rejected Ideas

Overridden eq and hash for Template and Interpolation

  • Identity left to user code to avoid caching confusion

Making Template and Interpolation Into Protocols

  • Concrete classes are simpler; protocols add complexity

An Additional Decoded Type

  • Over-engineered; raw vs cooked text handled via templates

Resources

@eevmanu
Copy link
Author

eevmanu commented Jun 17, 2025

to complete presentation with demo

since this is on a not released yet python version (3.14) the first option to use would be godbolt.org (compiler explorer) but for now, is not possible

other simple alternatives are:

  • github codespaces + devcontainer.json
  • replit + workspace state declaration using nix (replit.nix)

both alternatives are remote micro-VM or container back-ends / remote sandbox model

alternatives in the spectrum of browser-native sandboxes compiled to WebAssembly like:

  • webcontainers (from stackblitz)
  • pyodide

are not ready since former doesn't have a template nowadays ready for 3.14, need to download cpython package and use wasm tools to build python.wasm file and inject into its environment, and the latter is just releasing compatibility with 3.13 and they are recently exploring 3.14, so not ready any time soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment