Template Strings -- PEP 750¶
t_strings ¶
PEP 750 Template Strings (t-strings) — Python 3.14's most impactful feature.
T-strings generalize f-strings: instead of eagerly producing a str,
the t"..." prefix returns a Template object that preserves
both the static text fragments and the interpolated values. This lets
library code decide how to combine them — enabling safe SQL, safe
HTML, structured logging, and arbitrary DSLs.
Key anatomy of a Template object¶
Given t"Hello {name}! You are {age} years old.":
template.strings->('Hello ', '! You are ', ' years old.')template.interpolations-> tuple ofInterpolationobjectstemplate.values->(name_value, age_value)list(template)-> interleavedstr/Interpolation(empty boundary strings are skipped)
Interpolation attributes¶
.value— the evaluated Python object.expression— the source-code text (e.g.'name').conversion—'s','r','a', orNone.format_spec— e.g.'.2f'; defaults to''
References¶
- PEP 750 — https://peps.python.org/pep-0750/
- CPython tracking issue — https://github.com/python/cpython/issues/132661
- Jim Baker's tutorial — https://github.com/davepeck/pep750-examples
sql_safe ¶
Convert a t-string into a parameterised SQL query.
Every interpolation is replaced by a ? placeholder, and the
corresponding values are collected into a separate tuple. This is
exactly the interface that sqlite3.execute (and most DB-API 2.0
drivers) expect.
Why this matters: if you naively use an f-string to build SQL, a
malicious user can supply '; DROP TABLE users; -- as a value and
your query becomes valid, destructive SQL. With sql_safe, the
value is never interpolated into the query text.
user = "alice" sql_safe(t"SELECT * FROM users WHERE name = {user}") ('SELECT * FROM users WHERE name = ?', ('alice',))
aid = 1 ... val = "x" sql_safe(t"UPDATE t SET col = {val} WHERE id = {aid}") ('UPDATE t SET col = ? WHERE id = ?', ('x', 1))
Source code in src/concepts/t_strings.py
html_safe ¶
Render a t-string as HTML with all interpolations escaped.
Static text passes through unchanged (it's trusted, author-written
markup), but every interpolated value is run through
html.escape() so that injected markup, &, ", etc. are
neutralised.
title = "evil" html_safe(t"
{title}
") "<b onmouseover='steal()'>evil</b>
"name = "Alice & Bob" html_safe(t"
Hello, {name}!
") 'Hello, Alice & Bob!
'
Source code in src/concepts/t_strings.py
structured_log ¶
Extract interpolation expression names and values as a dict.
This enables structured (JSON) logging where the log message can be reconstructed from the template while the individual fields are machine-searchable.
The dict contains:
* "_message" — the rendered human-readable string
* one key per interpolation, named by its .expression
user = "alice" ... action = "login" result = structured_log(t"{user} performed {action}") result["user"] 'alice' result["action"] 'login' result["_message"] 'alice performed login'
Source code in src/concepts/t_strings.py
render ¶
Render a Template to a plain string, honouring conversions and
format specs — exactly like an f-string would.
This is the "identity" processor: render(t"...") behaves the same
as f"...". It's useful as a reference implementation and as a
building block for more complex processors.
x = 42 render(t"value = {x}") 'value = 42' pi = 3.14159 render(t"pi is {pi:.2f}") 'pi is 3.14' render(t"repr: {x!r}") 'repr: 42'