Python t-strings are a Python 3.14 feature for building templates that preserve static text and interpolated values as separate pieces. Their syntax resembles f-strings, but their purpose is different. Instead of immediately returning a completed string, a t-string returns a structured object that another function can inspect, validate, escape, transform, or render.
This design is useful whenever combining text and data directly is too limited. HTML generators, structured logging systems, localization tools, query builders, command wrappers, and template engines can examine every interpolation before deciding how to handle it.
What is a t-string?
A t-string uses the t prefix instead of f:
product = "Python Course"
price = 99.90
template = t"Product: {product} — ${price:.2f}"
print(type(template))
The result is not a str. It is an instance of string.templatelib.Template. The object stores literal text fragments and interpolation objects. Code receiving the template can therefore distinguish text written by the developer from dynamic values.
That distinction is the key difference from f-strings. An f-string evaluates expressions and returns final text. A t-string evaluates expressions but preserves the template structure for later processing.
Inspecting strings and values
A Template exposes strings, interpolations, and values. The first contains static fragments. The second contains complete interpolation objects. The third provides a convenient tuple containing only evaluated values.
name = "Anna"
points = 1250
template = t"User {name} has {points} points"
print(template.strings)
print(template.values)
print(template.interpolations)
The strings tuple has three entries: text before the name, text between the name and points, and final text. The values tuple has two entries. A template always has one more static string than interpolations.
For related fundamentals, review Python type hints and the guide to working with strings in Python.
The Interpolation object
Every item in template.interpolations is an Interpolation instance. It stores four important elements: evaluated value, original expression text, requested conversion, and format specification.
total = 123.456
i = t"Total: {total!r:.2f}".interpolations[0]
print(i.value)
print(i.expression)
print(i.conversion)
print(i.format_spec)
Unlike f-strings, conversions and formatting do not have to be applied automatically. The template processor decides what each field means. An HTML renderer may escape values, a logger may preserve numbers as numbers, and a localization system may format data according to locale.
Rendering a t-string
A small renderer demonstrates how processing works. The function below iterates through the template, applies optional conversion and formatting, and joins the result.
from string.templatelib import Interpolation, convert
def render(template):
parts = []
for item in template:
if isinstance(item, str):
parts.append(item)
continue
value = convert(item.value, item.conversion)
if item.format_spec:
value = format(value, item.format_spec)
parts.append(str(value))
return "".join(parts)
customer = "Carlos"
balance = 1500.5
print(render(t"Customer {customer}: ${balance:.2f}"))
This renderer is intentionally simple. Production code can reject unsupported types, escape dangerous characters, record source expressions, and apply field-specific policies. To organize reusable processors, read how to create Python modules and packages and how to document Python code with docstrings.
T-strings and security
T-strings do not automatically make an operation secure. They provide the structure needed for a processor to enforce a security policy. The benefit disappears if a function simply concatenates all pieces without validation.
For HTML, interpolated values can be escaped before rendering. For database operations, a processor can turn interpolations into query parameters instead of inserting values into SQL text. For operating-system commands, a wrapper can reject invalid arguments or pass a list of arguments to a subprocess API. The general rule remains unchanged: data must not be treated as code.
A template object is therefore an opportunity to establish a clear trust boundary. Static fragments are known by the application, while dynamic values can be inspected according to their type and destination.
Concatenating templates
A Template can be concatenated with another Template, but it cannot be directly added to a normal string. The restriction prevents ambiguity: should the normal string become trusted static text or an untrusted dynamic value?
greeting = t"Hello, "
name = "Beatrice"
message = greeting + t"{name}!"
To add static text programmatically, wrap it in Template("text"). To add a dynamic value, create an Interpolation. Being explicit helps processors classify each piece correctly.
Pattern matching interpolations
Interpolation objects support structural pattern matching. You can unpack value, expression, conversion, and format specification with match and case.
from string.templatelib import Interpolation
item = t"{42:.2f}".interpolations[0]
match item:
case Interpolation(value, expression, conversion, format_spec):
print(value, expression, conversion, format_spec)
This approach is convenient for declarative processors that handle different kinds of values. Learn the syntax in the Python match-case guide.
Practical processor ideas
An HTML processor can accept only strings, numbers, and explicitly safe markup objects. A logging processor can turn the expression text into a field name and keep the original value for search and aggregation. A translation processor can treat static text as a message key and apply locale-aware formatting to interpolations.
A query builder can collect dynamic values in a separate parameter list. However, such a builder still needs a carefully designed API and must never assume that all static fragments are valid SQL. T-strings improve visibility, but validation rules remain the responsibility of the library.
When should you use t-strings?
Use t-strings when a library or application needs a structured template and must control final rendering. Good use cases include HTML generation, structured logs, internationalization, validation tools, and domain-specific APIs that distinguish instructions from data.
F-strings remain simpler when you only need a local human-readable message from trusted values. T-strings are not intended to replace every f-string. They provide a protocol for tools that need to understand how a message was assembled.
Compatibility and requirements
T-strings were added in Python 3.14. Older interpreters do not recognize the prefix. Check the runtime with python --version and declare the minimum version in pyproject.toml. The tomllib guide is useful when working with modern project configuration.
The official string.templatelib documentation describes the complete API. The motivation and design decisions are documented in PEP 750.
Conclusion
Python t-strings introduce a structured representation for templates. They preserve static text, values, expressions, conversions, and format specifications until a processor renders the final result. This makes it easier to build extensible libraries that understand context instead of receiving an opaque string.
Start with a small renderer, validate accepted types, and define rules for each output destination. HTML, SQL, shell commands, localization, and logging require different policies. With those boundaries in place, t-strings provide an elegant foundation for modern Python template processing.







