The :meth:`str.format` method and the :class:`Formatter` class share the same
syntax for format strings (although in the case of :class:`Formatter`,
subclasses can define their own format string syntax). The syntax is
-related to that of :ref:`formatted string literals <f-strings>`, but it is
-less sophisticated and, in particular, does not support arbitrary expressions.
+related to that of :ref:`formatted string literals <f-strings>` and
+:ref:`template string literals <t-strings>`, but it is less sophisticated
+and, in particular, does not support arbitrary expressions.
.. index::
single: {} (curly brackets); in string formatting
"Weight in tons {0.weight}" # 'weight' attribute of first positional arg
"Units destroyed: {players[0]}" # First element of keyword argument 'players'.
+.. _formatstrings-conversion:
+
The *conversion* field causes a type coercion before formatting. Normally, the
job of formatting a value is done by the :meth:`~object.__format__` method of the value
itself. However, in some cases it is desirable to force a type to be formatted
"Format specifications" are used within replacement fields contained within a
format string to define how individual values are presented (see
-:ref:`formatstrings` and :ref:`f-strings`).
+:ref:`formatstrings`, :ref:`f-strings`, and :ref:`t-strings`).
They can also be passed directly to the built-in
:func:`format` function. Each formattable type may define how the format
specification is to be interpreted.
-.. _template-strings:
+.. _template-strings-pep292:
-Template strings
-----------------
+Template strings ($-strings)
+----------------------------
+
+.. note::
+
+ The feature described here was introduced in Python 2.4. It is unrelated
+ to, and should not be confused with, the newer
+ :ref:`template strings <template-strings>` and
+ :ref:`t-string literal syntax <t-strings>` introduced in Python 3.14.
+ T-string literals evaluate to instances of a different
+ :class:`~string.templatelib.Template` class, found in the
+ :mod:`string.templatelib` module.
Template strings provide simpler string substitutions as described in
:pep:`292`. A primary use case for template strings is for
--- /dev/null
+:mod:`!string.templatelib` --- Support for template string literals
+===================================================================
+
+.. module:: string.templatelib
+ :synopsis: Support for template string literals.
+
+**Source code:** :source:`Lib/string/templatelib.py`
+
+--------------
+
+.. seealso::
+
+ * :ref:`Format strings <f-strings>`
+ * :ref:`T-string literal syntax <t-strings>`
+
+
+.. _template-strings:
+
+Template strings
+----------------
+
+.. versionadded:: 3.14
+
+Template strings are a formatting mechanism that allows for deep control over
+how strings are processed. You can create templates using
+:ref:`t-string literal syntax <t-strings>`, which is identical to
+:ref:`f-string syntax <f-strings>` but uses a ``t`` instead of an ``f``.
+While f-strings evaluate to ``str``, t-strings create a :class:`Template`
+instance that gives you access to the static and interpolated (in curly braces)
+parts of a string *before* they are combined.
+
+
+.. _templatelib-template:
+
+Template
+--------
+
+The :class:`!Template` class describes the contents of a template string.
+
+:class:`!Template` instances are immutable: their attributes cannot be
+reassigned.
+
+.. class:: Template(*args)
+
+ Create a new :class:`!Template` object.
+
+ :param args: A mix of strings and :class:`Interpolation` instances in any order.
+ :type args: str | Interpolation
+
+ The most common way to create a :class:`!Template` instance is to use the
+ :ref:`t-string literal syntax <t-strings>`. This syntax is identical to that of
+ :ref:`f-strings <f-strings>` except that it uses a ``t`` instead of an ``f``:
+
+ >>> name = "World"
+ >>> template = t"Hello {name}!"
+ >>> type(template)
+ <class 'string.templatelib.Template'>
+
+ Templates ars stored as sequences of literal :attr:`~Template.strings`
+ and dynamic :attr:`~Template.interpolations`.
+ A :attr:`~Template.values` attribute holds the interpolation values:
+
+ >>> template.strings
+ ('Hello ', '!')
+ >>> template.interpolations
+ (Interpolation('World', ...),)
+ >>> template.values
+ ('World',)
+
+ The :attr:`!strings` tuple has one more element than :attr:`!interpolations`
+ and :attr:`!values`; the interpolations “belong” between the strings.
+ This may be easier to understand when tuples are aligned::
+
+ template.strings: ('Hello ', '!')
+ template.values: ( 'World', )
+
+ While literal syntax is the most common way to create :class:`!Template`
+ instances, it is also possible to create them directly using the constructor:
+
+ >>> from string.templatelib import Interpolation, Template
+ >>> name = "World"
+ >>> template = Template("Hello, ", Interpolation(name, "name"), "!")
+ >>> list(template)
+ ['Hello, ', Interpolation('World', 'name', None, ''), '!']
+
+ If two or more consecutive strings are passed, they will be concatenated
+ into a single value in the :attr:`~Template.strings` attribute. For example,
+ the following code creates a :class:`Template` with a single final string:
+
+ >>> from string.templatelib import Template
+ >>> template = Template("Hello ", "World", "!")
+ >>> template.strings
+ ('Hello World!',)
+
+ If two or more consecutive interpolations are passed, they will be treated
+ as separate interpolations and an empty string will be inserted between them.
+ For example, the following code creates a template with empty placeholders
+ in the :attr:`~Template.strings` attribute:
+
+ >>> from string.templatelib import Interpolation, Template
+ >>> template = Template(Interpolation("World", "name"), Interpolation("!", "punctuation"))
+ >>> template.strings
+ ('', '', '')
+
+ .. attribute:: strings
+ :type: tuple[str, ...]
+
+ A :ref:`tuple <tut-tuples>` of the static strings in the template.
+
+ >>> name = "World"
+ >>> t"Hello {name}!".strings
+ ('Hello ', '!')
+
+ Empty strings *are* included in the tuple:
+
+ >>> name = "World"
+ >>> t"Hello {name}{name}!".strings
+ ('Hello ', '', '!')
+
+ The ``strings`` tuple is never empty, and always contains one more
+ string than the ``interpolations`` and ``values`` tuples:
+
+ >>> t"".strings
+ ('',)
+ >>> t"".values
+ ()
+ >>> t"{'cheese'}".strings
+ ('', '')
+ >>> t"{'cheese'}".values
+ ('cheese',)
+
+ .. attribute:: interpolations
+ :type: tuple[Interpolation, ...]
+
+ A tuple of the interpolations in the template.
+
+ >>> name = "World"
+ >>> t"Hello {name}!".interpolations
+ (Interpolation('World', 'name', None, ''),)
+
+ The ``interpolations`` tuple may be empty and always contains one fewer
+ values than the ``strings`` tuple:
+
+ >>> t"Hello!".interpolations
+ ()
+
+ .. attribute:: values
+ :type: tuple[Any, ...]
+
+ A tuple of all interpolated values in the template.
+
+ >>> name = "World"
+ >>> t"Hello {name}!".values
+ ('World',)
+
+ The ``values`` tuple always has the same length as the
+ ``interpolations`` tuple. It is equivalent to
+ ``tuple(i.value for i in template.interpolations)``.
+
+ .. describe:: iter(template)
+
+ Iterate over the template, yielding each string and
+ :class:`Interpolation` in order.
+
+ >>> name = "World"
+ >>> list(t"Hello {name}!")
+ ['Hello ', Interpolation('World', 'name', None, ''), '!']
+
+ Empty strings are *not* included in the iteration:
+
+ >>> name = "World"
+ >>> list(t"Hello {name}{name}")
+ ['Hello ', Interpolation('World', 'name', None, ''), Interpolation('World', 'name', None, '')]
+
+ .. describe:: template + other
+ template += other
+
+ Concatenate this template with another, returning a new
+ :class:`!Template` instance:
+
+ >>> name = "World"
+ >>> list(t"Hello " + t"there {name}!")
+ ['Hello there ', Interpolation('World', 'name', None, ''), '!']
+
+ Concatenation between a :class:`!Template` and a ``str`` is *not* supported.
+ This is because it is ambiguous whether the string should be treated as
+ a static string or an interpolation. If you want to concatenate a
+ :class:`!Template` with a string, you should either wrap the string
+ directly in a :class:`!Template` (to treat it as a static string) or use
+ an :class:`!Interpolation` (to treat it as dynamic):
+
+ >>> from string.templatelib import Template, Interpolation
+ >>> template = t"Hello "
+ >>> # Treat "there " as a static string
+ >>> template += Template("there ")
+ >>> # Treat name as an interpolation
+ >>> name = "World"
+ >>> template += Template(Interpolation(name, "name"))
+ >>> list(template)
+ ['Hello there ', Interpolation('World', 'name', None, '')]
+
+
+.. class:: Interpolation(value, expression="", conversion=None, format_spec="")
+
+ Create a new :class:`!Interpolation` object.
+
+ :param value: The evaluated, in-scope result of the interpolation.
+ :type value: object
+
+ :param expression: The text of a valid Python expression, or an empty string.
+ :type expression: str
+
+ :param conversion: The optional :ref:`conversion <formatstrings>` to be used, one of r, s, and a.
+ :type conversion: ``Literal["a", "r", "s"] | None``
+
+ :param format_spec: An optional, arbitrary string used as the :ref:`format specification <formatspec>` to present the value.
+ :type format_spec: str
+
+ The :class:`!Interpolation` type represents an expression inside a template string.
+
+ :class:`!Interpolation` instances are immutable: their attributes cannot be
+ reassigned.
+
+ .. attribute:: value
+
+ :returns: The evaluated value of the interpolation.
+ :type: object
+
+ >>> t"{1 + 2}".interpolations[0].value
+ 3
+
+ .. attribute:: expression
+
+ :returns: The text of a valid Python expression, or an empty string.
+ :type: str
+
+ The :attr:`~Interpolation.expression` is the original text of the
+ interpolation's Python expression, if the interpolation was created
+ from a t-string literal. Developers creating interpolations manually
+ should either set this to an empty string or choose a suitable valid
+ Python expression.
+
+ >>> t"{1 + 2}".interpolations[0].expression
+ '1 + 2'
+
+ .. attribute:: conversion
+
+ :returns: The conversion to apply to the value, or ``None``.
+ :type: ``Literal["a", "r", "s"] | None``
+
+ The :attr:`!Interpolation.conversion` is the optional conversion to apply
+ to the value:
+
+ >>> t"{1 + 2!a}".interpolations[0].conversion
+ 'a'
+
+ .. note::
+
+ Unlike f-strings, where conversions are applied automatically,
+ the expected behavior with t-strings is that code that *processes* the
+ :class:`!Template` will decide how to interpret and whether to apply
+ the :attr:`!Interpolation.conversion`.
+
+ .. attribute:: format_spec
+
+ :returns: The format specification to apply to the value.
+ :type: str
+
+ The :attr:`!Interpolation.format_spec` is an optional, arbitrary string
+ used as the format specification to present the value:
+
+ >>> t"{1 + 2:.2f}".interpolations[0].format_spec
+ '.2f'
+
+ .. note::
+
+ Unlike f-strings, where format specifications are applied automatically
+ via the :func:`format` protocol, the expected behavior with
+ t-strings is that code that *processes* the :class:`!Template` will
+ decide how to interpret and whether to apply the format specification.
+ As a result, :attr:`!Interpolation.format_spec` values in
+ :class:`!Template` instances can be arbitrary strings, even those that
+ do not necessarily conform to the rules of Python's :func:`format`
+ protocol.
+
+ Interpolations support pattern matching, allowing you to match against
+ their attributes with the :ref:`match statement <match>`:
+
+ >>> from string.templatelib import Interpolation
+ >>> interpolation = Interpolation(3.0, "1 + 2", None, ".2f")
+ >>> match interpolation:
+ ... case Interpolation(value, expression, conversion, format_spec):
+ ... print(value, expression, conversion, format_spec)
+ ...
+ 3.0 1 + 2 None .2f
+
+
+Helper functions
+----------------
+
+.. function:: convert(obj, /, conversion)
+
+ Applies formatted string literal :ref:`conversion <formatstrings-conversion>`
+ semantics to the given object *obj*.
+ This is frequently useful for custom template string processing logic.
+
+ Three conversion flags are currently supported:
+
+ * ``'s'`` which calls :func:`str` on the value,
+ * ``'r'`` which calls :func:`repr`, and
+ * ``'a'`` which calls :func:`ascii`.
+
+ If the conversion flag is ``None``, *obj* is returned unchanged.
single: f'; formatted string literal
single: f"; formatted string literal
-A string literal with ``'f'`` or ``'F'`` in its prefix is a
-:dfn:`formatted string literal`; see :ref:`f-strings`. The ``'f'`` may be
-combined with ``'r'``, but not with ``'b'`` or ``'u'``, therefore raw
+A string literal with ``f`` or ``F`` in its prefix is a
+:dfn:`formatted string literal`; see :ref:`f-strings`. The ``f`` may be
+combined with ``r``, but not with ``b`` or ``u``, therefore raw
formatted strings are possible, but formatted bytes literals are not.
In triple-quoted literals, unescaped newlines and quotes are allowed (and are
.. versionadded:: 3.6
A :dfn:`formatted string literal` or :dfn:`f-string` is a string literal
-that is prefixed with ``'f'`` or ``'F'``. These strings may contain
+that is prefixed with ``f`` or ``F``. These strings may contain
replacement fields, which are expressions delimited by curly braces ``{}``.
While other string literals always have a constant value, formatted strings
are really expressions evaluated at run time.
and :meth:`str.format`, which uses a related format string mechanism.
+.. _t-strings:
+.. _template-string-literals:
+
+t-strings
+---------
+
+.. versionadded:: 3.14
+
+A :dfn:`template string literal` or :dfn:`t-string` is a string literal
+that is prefixed with ``t`` or ``T``. These strings follow the same
+syntax and evaluation rules as :ref:`formatted string literals <f-strings>`, with
+the following differences:
+
+- Rather than evaluating to a ``str`` object, t-strings evaluate to a
+ :class:`~string.templatelib.Template` object from the
+ :mod:`string.templatelib` module.
+
+- The :func:`format` protocol is not used. Instead, the format specifier and
+ conversions (if any) are passed to a new :class:`~string.templatelib.Interpolation`
+ object that is created for each evaluated expression. It is up to code that
+ processes the resulting :class:`~string.templatelib.Template` object to
+ decide how to handle format specifiers and conversions.
+
+- Format specifiers containing nested replacement fields are evaluated eagerly,
+ prior to being passed to the :class:`~string.templatelib.Interpolation` object.
+ For instance, an interpolation of the form ``{amount:.{precision}f}`` will
+ evaluate the expression ``{precision}`` before setting the ``format_spec``
+ attribute of the resulting :class:`!Interpolation` object; if ``precision``
+ is (for example) ``2``, the resulting format specifier will be ``'.2f'``.
+
+- When the equal sign ``'='`` is provided in an interpolation expression, the
+ resulting :class:`~string.templatelib.Template` object will have the expression
+ text along with a ``'='`` character placed in its
+ :attr:`~string.templatelib.Template.strings` attribute. The
+ :attr:`~string.templatelib.Template.interpolations` attribute will also
+ contain an ``Interpolation`` instance for the expression. By default, the
+ :attr:`~string.templatelib.Interpolation.conversion` attribute will be set to
+ ``'r'`` (that is, :func:`repr`), unless there is a conversion explicitly
+ specified (in which case it overrides the default) or a format specifier is
+ provided (in which case, the ``conversion`` defaults to ``None``).
+
+
.. _numbers:
Numeric literals