May 26, 2025·4 min read·engineeringpythontemplateslanguage-designprerelease

Python 3.14 beta 2 preserves interpolation for library processing

Python 3.14.0b2 includes template strings, which preserve literal text and interpolated values separately. Libraries can process those values before producing a final string.

An f-string produces its final text before a downstream library can distinguish trusted syntax from an inserted value. A function receiving that text cannot recover the boundary reliably. Python's template strings preserve the boundary as structured data, giving a processor access to the values before it decides how to combine them.

The May 26 release of Python 3.14.0b2 includes this new syntax among the developing series' changes. The second beta is a useful point to examine what the language supplies and what remains a library's responsibility. A template object is not automatically a safe SQL query or escaped HTML. It is the input from which a suitable processor can construct those results.

Why another interpolation syntax exists

PEP 750 identifies a limitation of f-strings. Their interpolation happens before the caller can intercept and transform the inserted values. Directly embedding input into SQL or HTML can therefore mix data with syntax in ways that create injection vulnerabilities.

The proposal generalizes familiar interpolation syntax by replacing the f prefix with t. A t-string evaluates to a Template object from string.templatelib. That object retains the literal string segments and interpolation objects separately.

I find this a useful extension of Python's library-oriented design. The language supplies a structured representation, while the consuming library defines domain behavior. That is consistent with the parts of Python I like, where a common convention lets libraries integrate without each inventing its own source syntax.

The historical path is longer than the new prefix suggests. PEP 750 describes earlier interpolation-template work in PEP 501, which was deferred while Python gained experience with f-strings. The current design builds on the formalized f-string grammar in PEP 701. That history explains why t-strings resemble existing syntax while returning a different kind of object.

The values remain available

This small example shows the information a processor receives:

name = "Ada"
message = t"Hello {name}."

assert message.strings == ("Hello ", ".")
assert message.values == ("Ada",)
assert message.interpolations[0].expression == "name"

The string parts include the material around the interpolation. The interpolation retains the evaluated value and information about its expression. A processor does not have to parse the final text to infer where that value came from.

PEP 750 specifies a nonempty tuple of literal string parts, with one more element than the number of interpolations. It also provides an interpolations tuple and a convenience values property. Iterating a template yields its nonempty literal parts and interpolation objects in order.

That representation supports processing rules that depend on both structure and values. For example, a logging processor can retain a value as data instead of immediately converting the entire message into text. A domain processor can reject a value whose type is inappropriate for the location where it appears. Those are possible library designs, not behavior conferred by the prefix alone.

Evaluation is still eager

Template construction evaluates interpolation expressions immediately. Preserving their results does not mean storing an executable expression to run later. The distinction matters for side effects and for data that changes after a template is created.

An interpolation also retains an optional conversion and a format specification. The processor decides how to interpret them. A conversion such as !r remains available as metadata, and formatting policy belongs to the code that consumes the template. That flexibility makes the object useful outside ordinary text rendering.

The proposal deliberately avoids an implicit string rendering policy. There is no special Template.__str__() implementation that merges the pieces into their intended output. A library must receive and process the object explicitly. Otherwise the structured boundary could disappear before the domain-specific rules had a chance to run.

That is also why mechanically changing an f-string prefix is insufficient to secure an application. The receiving API must understand templates and apply the correct rules. Escaping suitable for an HTML text node does not establish safety for every possible HTML context, and a SQL processor still needs to distinguish query syntax from bound data.

The beta also concerns binary compatibility

The release announcement asks third-party maintainers to test against the beta and publish prerelease wheels so dependent projects can perform their own compatibility work. It recommends waiting for the first release candidate before regular production releases because ABI changes remain possible.

This gives two different meanings to beta testing. A pure-Python library may need to verify how it handles a new object or syntax. An extension package must also account for the interpreter interface against which its binaries are built. A working application requires both the language-level behavior and compatible dependencies.

My main reason for using Python is the ecosystem around it, especially libraries that solve difficult problems below the application layer. A new language capability becomes practically useful when those dependencies can consume it. Template strings make that relationship particularly explicit because the interpreter intentionally leaves the processing policy to library code.

The second beta remains a preview, with changes still possible before the release-candidate phase. Within that scope, t-strings establish a concrete contract for experimentation. The literal text stays separate from evaluated interpolation values, allowing a processor to apply its rules before it constructs output for another system.