.. seealso::
* :ref:`Format strings <f-strings>`
- * :ref:`T-string literal syntax <t-strings>`
-
+ * :ref:`Template string literal (t-string) syntax <t-strings>`
+ * :pep:`750`
.. _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 strings are a mechanism for custom string processing.
+They have the full flexibility of Python's :ref:`f-strings`,
+but return a :class:`Template` instance that gives access
+to the static and interpolated (in curly braces) parts of a string
+*before* they are combined.
-Template
---------
+To write a t-string, use a ``'t'`` prefix instead of an ``'f'``, like so:
-The :class:`!Template` class describes the contents of a template string.
+.. code-block:: pycon
-:class:`!Template` instances are immutable: their attributes cannot be
-reassigned.
+ >>> pi = 3.14
+ >>> t't-strings are new in Python {pi!s}!'
+ Template(
+ strings=('t-strings are new in Python ', '.'),
+ interpolations=(Interpolation(3.14, 'pi', 's', ''),)
+ )
-.. class:: Template(*args)
+Types
+-----
- Create a new :class:`!Template` object.
+.. class:: Template
- :param args: A mix of strings and :class:`Interpolation` instances in any order.
- :type args: str | Interpolation
+ The :class:`!Template` class describes the contents of a template string.
+ It is immutable, meaning that attributes of a template cannot be reassigned.
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``:
+ :ref:`template string literal syntax <t-strings>`.
+ This syntax is identical to that of :ref:`f-strings <f-strings>`,
+ except that it uses a ``t`` prefix in place of an ``f``:
- >>> name = "World"
- >>> template = t"Hello {name}!"
+ >>> cheese = 'Red Leicester'
+ >>> template = t"We're fresh out of {cheese}, sir."
>>> type(template)
<class 'string.templatelib.Template'>
- Templates ars stored as sequences of literal :attr:`~Template.strings`
+ Templates are stored as sequences of literal :attr:`~Template.strings`
and dynamic :attr:`~Template.interpolations`.
- A :attr:`~Template.values` attribute holds the interpolation values:
+ A :attr:`~Template.values` attribute holds the values of the interpolations:
+ >>> cheese = 'Camembert'
+ >>> template = t'Ah! We do have {cheese}.'
>>> template.strings
- ('Hello ', '!')
+ ('Ah! We do have ', '.')
>>> template.interpolations
- (Interpolation('World', ...),)
+ (Interpolation('Camembert', ...),)
>>> template.values
- ('World',)
+ ('Camembert',)
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::
+ This may be easier to understand when tuples are aligned
- template.strings: ('Hello ', '!')
- template.values: ( 'World', )
+ .. code-block:: python
- While literal syntax is the most common way to create :class:`!Template`
- instances, it is also possible to create them directly using the constructor:
+ template.strings: ('Ah! We do have ', '.')
+ template.values: ( 'Camembert', )
- >>> from string.templatelib import Interpolation, Template
- >>> name = "World"
- >>> template = Template("Hello, ", Interpolation(name, "name"), "!")
- >>> list(template)
- ['Hello, ', Interpolation('World', 'name', None, ''), '!']
+ .. rubric:: Attributes
- 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:
+ .. attribute:: strings
+ :type: tuple[str, ...]
- >>> from string.templatelib import Template
- >>> template = Template("Hello ", "World", "!")
- >>> template.strings
- ('Hello World!',)
+ A :class:`tuple` of the static strings in the template.
- 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:
+ >>> cheese = 'Camembert'
+ >>> template = t'Ah! We do have {cheese}.'
+ >>> template.strings
+ ('Ah! We do have ', '.')
- >>> from string.templatelib import Interpolation, Template
- >>> template = Template(Interpolation("World", "name"), Interpolation("!", "punctuation"))
- >>> template.strings
- ('', '', '')
+ Empty strings *are* included in the tuple:
- .. attribute:: strings
- :type: tuple[str, ...]
+ >>> response = 'We do have '
+ >>> cheese = 'Camembert'
+ >>> template = t'Ah! {response}{cheese}.'
+ >>> template.strings
+ ('Ah! ', '', '.')
- A :ref:`tuple <tut-tuples>` of the static strings in the template.
+ The ``strings`` tuple is never empty, and always contains one more
+ string than the ``interpolations`` and ``values`` tuples:
- >>> name = "World"
- >>> t"Hello {name}!".strings
- ('Hello ', '!')
+ >>> t''.strings
+ ('',)
+ >>> t''.values
+ ()
+ >>> t'{'cheese'}'.strings
+ ('', '')
+ >>> t'{'cheese'}'.values
+ ('cheese',)
- Empty strings *are* included in the tuple:
+ .. attribute:: interpolations
+ :type: tuple[Interpolation, ...]
- >>> name = "World"
- >>> t"Hello {name}{name}!".strings
- ('Hello ', '', '!')
+ A :class:`tuple` of the interpolations in the template.
- The ``strings`` tuple is never empty, and always contains one more
- string than the ``interpolations`` and ``values`` tuples:
+ >>> cheese = 'Camembert'
+ >>> template = t'Ah! We do have {cheese}.'
+ >>> template.interpolations
+ (Interpolation('Camembert', 'cheese', None, ''),)
- >>> t"".strings
- ('',)
- >>> t"".values
- ()
- >>> t"{'cheese'}".strings
- ('', '')
- >>> t"{'cheese'}".values
- ('cheese',)
+ The ``interpolations`` tuple may be empty and always contains one fewer
+ values than the ``strings`` tuple:
- .. attribute:: interpolations
- :type: tuple[Interpolation, ...]
+ >>> t'Red Leicester'.interpolations
+ ()
- A tuple of the interpolations in the template.
+ .. attribute:: values
+ :type: tuple[object, ...]
- >>> name = "World"
- >>> t"Hello {name}!".interpolations
- (Interpolation('World', 'name', None, ''),)
+ A tuple of all interpolated values in the template.
- The ``interpolations`` tuple may be empty and always contains one fewer
- values than the ``strings`` tuple:
+ >>> cheese = 'Camembert'
+ >>> template = t'Ah! We do have {cheese}.'
+ >>> template.values
+ ('Camembert',)
- >>> t"Hello!".interpolations
- ()
+ The ``values`` tuple always has the same length as the
+ ``interpolations`` tuple. It is always equivalent to
+ ``tuple(i.value for i in template.interpolations)``.
- .. attribute:: values
- :type: tuple[Any, ...]
+ .. rubric:: Methods
- A tuple of all interpolated values in the template.
+ .. method:: __new__(*args: str | Interpolation)
- >>> name = "World"
- >>> t"Hello {name}!".values
- ('World',)
+ While literal syntax is the most common way to create a :class:`!Template`,
+ it is also possible to create them directly using the constructor:
- The ``values`` tuple always has the same length as the
- ``interpolations`` tuple. It is equivalent to
- ``tuple(i.value for i in template.interpolations)``.
+ >>> from string.templatelib import Interpolation, Template
+ >>> cheese = 'Camembert'
+ >>> template = Template(
+ ... 'Ah! We do have ', Interpolation(cheese, 'cheese'), '.'
+ ... )
+ >>> list(template)
+ ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
- .. describe:: iter(template)
+ If multiple strings are passed consecutively, 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:
- Iterate over the template, yielding each string and
- :class:`Interpolation` in order.
+ >>> from string.templatelib import Template
+ >>> template = Template('Ah! We do have ', 'Camembert', '.')
+ >>> template.strings
+ ('Ah! We do have Camembert.',)
- >>> name = "World"
- >>> list(t"Hello {name}!")
- ['Hello ', Interpolation('World', 'name', None, ''), '!']
+ If multiple interpolations are passed consecutively, 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:
- Empty strings are *not* included in the iteration:
+ >>> from string.templatelib import Interpolation, Template
+ >>> template = Template(
+ ... Interpolation('Camembert', 'cheese'),
+ ... Interpolation('.', 'punctuation'),
+ ... )
+ >>> template.strings
+ ('', '', '')
- >>> name = "World"
- >>> list(t"Hello {name}{name}")
- ['Hello ', Interpolation('World', 'name', None, ''), Interpolation('World', 'name', None, '')]
+ .. describe:: iter(template)
- .. describe:: template + other
- template += other
+ Iterate over the template, yielding each non-empty string and
+ :class:`Interpolation` in the correct order:
- Concatenate this template with another, returning a new
- :class:`!Template` instance:
+ >>> cheese = 'Camembert'
+ >>> list(t'Ah! We do have {cheese}.')
+ ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
- >>> name = "World"
- >>> list(t"Hello " + t"there {name}!")
- ['Hello there ', Interpolation('World', 'name', None, ''), '!']
+ .. caution::
- 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):
+ Empty strings are **not** included in the iteration:
- >>> 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, '')]
+ >>> response = 'We do have '
+ >>> cheese = 'Camembert'
+ >>> list(t'Ah! {response}{cheese}.') # doctest: +NORMALIZE_WHITESPACE
+ ['Ah! ',
+ Interpolation('We do have ', 'response', None, ''),
+ Interpolation('Camembert', 'cheese', None, ''),
+ '.']
+ .. describe:: template + other
+ template += other
-.. class:: Interpolation(value, expression="", conversion=None, format_spec="")
+ Concatenate this template with another, returning a new
+ :class:`!Template` instance:
- Create a new :class:`!Interpolation` object.
+ >>> cheese = 'Camembert'
+ >>> list(t'Ah! ' + t'We do have {cheese}.')
+ ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
- :param value: The evaluated, in-scope result of the interpolation.
- :type value: object
+ Concatenating a :class:`!Template` and a ``str`` is **not** supported.
+ This is because it is unclear 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):
- :param expression: The text of a valid Python expression, or an empty string.
- :type expression: str
+ >>> from string.templatelib import Interpolation, Template
+ >>> template = t'Ah! '
+ >>> # Treat 'We do have ' as a static string
+ >>> template += Template('We do have ')
+ >>> # Treat cheese as an interpolation
+ >>> cheese = 'Camembert'
+ >>> template += Template(Interpolation(cheese, 'cheese'))
+ >>> list(template)
+ ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, '')]
- :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
+.. class:: Interpolation
The :class:`!Interpolation` type represents an expression inside a template string.
+ It is immutable, meaning that attributes of an interpolation cannot be reassigned.
- :class:`!Interpolation` instances are immutable: their attributes cannot be
- reassigned.
+ Interpolations support pattern matching, allowing you to match against
+ their attributes with the :ref:`match statement <match>`:
+
+ >>> from string.templatelib import Interpolation
+ >>> interpolation = t'{1. + 2.:.2f}'.interpolations[0]
+ >>> interpolation
+ Interpolation(3.0, '1. + 2.', None, '.2f')
+ >>> match interpolation:
+ ... case Interpolation(value, expression, conversion, format_spec):
+ ... print(value, expression, conversion, format_spec, sep=' | ')
+ ...
+ 3.0 | 1. + 2. | None | .2f
+
+ .. rubric:: Attributes
.. attribute:: value
+ :type: object
- :returns: The evaluated value of the interpolation.
- :type: object
+ The evaluated value of the interpolation.
- >>> t"{1 + 2}".interpolations[0].value
- 3
+ >>> t'{1 + 2}'.interpolations[0].value
+ 3
.. attribute:: expression
+ :type: str
- :returns: The text of a valid Python expression, or an empty string.
- :type: str
+ The text of a valid Python expression, or an empty string.
- 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.
+ The :attr:`.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'
+ >>> t'{1 + 2}'.interpolations[0].expression
+ '1 + 2'
.. attribute:: conversion
+ :type: typing.Literal['a', 'r', 's'] | None
- :returns: The conversion to apply to the value, or ``None``.
- :type: ``Literal["a", "r", "s"] | None``
+ The conversion to apply to the value, or ``None``.
- The :attr:`!Interpolation.conversion` is the optional conversion to apply
- to the value:
+ The :attr:`!conversion` is the optional conversion to apply
+ to the value:
- >>> t"{1 + 2!a}".interpolations[0].conversion
- 'a'
+ >>> t'{1 + 2!a}'.interpolations[0].conversion
+ 'a'
- .. note::
+ .. 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`.
+ the :attr:`!conversion`.
+ For convenience, the :func:`convert` function can be used to mimic
+ f-string conversion semantics.
.. attribute:: format_spec
+ :type: str
- :returns: The format specification to apply to the value.
- :type: str
+ The format specification to apply to the value.
- The :attr:`!Interpolation.format_spec` is an optional, arbitrary string
- used as the format specification to present the value:
+ The :attr:`!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'
+ >>> t'{1 + 2:.2f}'.interpolations[0].format_spec
+ '.2f'
- .. note::
+ .. 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
+ t-strings is that code that *processes* the interpolation 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.
+ As a result, :attr:`!format_spec` values in interpolations
+ can be arbitrary strings,
+ including those that do not conform to the :func:`format` protocol.
- Interpolations support pattern matching, allowing you to match against
- their attributes with the :ref:`match statement <match>`:
+ .. rubric:: Methods
- >>> 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
+ .. method:: __new__(value: object, \
+ expression: str, \
+ conversion: typing.Literal['a', 'r', 's'] | None = None, \
+ format_spec: str = '')
+
+ Create a new :class:`!Interpolation` object from component parts.
+
+ :param value: The evaluated, in-scope result of the interpolation.
+ :param expression: The text of a valid Python expression,
+ or an empty string.
+ :param conversion: The :ref:`conversion <formatstrings>` to be used,
+ one of ``None``, ``'a'``, ``'r'``, or ``'s'``.
+ :param format_spec: An optional, arbitrary string used as the
+ :ref:`format specification <formatspec>` to present the value.
Helper functions
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`.
+ * ``'s'`` which calls :func:`str` on the value (like ``!s``),
+ * ``'r'`` which calls :func:`repr` (like ``!r``), and
+ * ``'a'`` which calls :func:`ascii` (like ``!a``).
If the conversion flag is ``None``, *obj* is returned unchanged.
See the linked sections for details on each type.
-Prefixes are case-insensitive (for example, ``B`` works the same as ``b``).
-The ``r`` prefix can be combined with ``f``, ``t`` or ``b``, so ``fr``,
-``rf``, ``tr``, ``rt``, ``br`` and ``rb`` are also valid prefixes.
+Prefixes are case-insensitive (for example, '``B``' works the same as '``b``').
+The '``r``' prefix can be combined with '``f``', '``t``' or '``b``', so '``fr``',
+'``rf``', '``tr``', '``rt``', '``br``', and '``rb``' are also valid prefixes.
.. versionadded:: 3.3
The ``'rb'`` prefix of raw bytes literals has been added as a synonym
Escape sequences
----------------
-Unless an ``'r'`` or ``'R'`` prefix is present, escape sequences in string and
+Unless an '``r``' or '``R``' prefix is present, escape sequences in string and
bytes literals are interpreted according to rules similar to those used by
Standard C. The recognized escape sequences are:
Bytes literals
--------------
-:dfn:`Bytes literals` are always prefixed with ``'b'`` or ``'B'``; they produce an
+:dfn:`Bytes literals` are always prefixed with '``b``' or '``B``'; they produce an
instance of the :class:`bytes` type instead of the :class:`str` type.
They may only contain ASCII characters; bytes with a numeric value of 128
or greater must be expressed with escape sequences (typically
Raw string literals
-------------------
-Both string and bytes literals may optionally be prefixed with a letter ``'r'``
-or ``'R'``; such constructs are called :dfn:`raw string literals`
+Both string and bytes literals may optionally be prefixed with a letter '``r``'
+or '``R``'; such constructs are called :dfn:`raw string literals`
and :dfn:`raw bytes literals` respectively and treat backslashes as
literal characters.
As a result, in raw string literals, :ref:`escape sequences <escape-sequences>`
.. 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.
.. 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:
+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.
+* Rather than evaluating to a ``str`` object, template string literals evaluate
+ to a :class:`string.templatelib.Template` object.
-- 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.
+* 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,
+* 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``).
+ evaluate the inner expression ``{precision}`` to determine the value of the
+ ``format_spec`` attribute.
+ If ``precision`` were to be ``2``, the resulting format specifier
+ would be ``'.2f'``.
+
+* When the equals sign ``'='`` is provided in an interpolation expression,
+ the text of the expression is appended to the literal string that precedes
+ the relevant interpolation.
+ This includes the equals sign and any surrounding whitespace.
+ The :class:`!Interpolation` instance for the expression will be created as
+ normal, except that :attr:`~string.templatelib.Interpolation.conversion` will
+ be set to '``r``' (:func:`repr`) by default.
+ If an explicit conversion or format specifier are provided,
+ this will override the default behaviour.
.. _numbers: