| :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:`Template string literal (t-string) syntax <t-strings>` | |
| * :pep:`750` | |
| .. _template-strings: | |
| Template strings | |
| ---------------- | |
| .. versionadded:: 3.14 | |
| 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 brackets) parts of a string | |
| *before* they are combined. | |
| To write a t-string, use a ``'t'`` prefix instead of an ``'f'``, like so: | |
| .. code-block:: pycon | |
| >>> 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', ''),) | |
| ) | |
| Types | |
| ----- | |
| .. class:: Template | |
| 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:`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``: | |
| >>> cheese = 'Red Leicester' | |
| >>> template = t"We're fresh out of {cheese}, sir." | |
| >>> type(template) | |
| <class 'string.templatelib.Template'> | |
| Templates are stored as sequences of literal :attr:`~Template.strings` | |
| and dynamic :attr:`~Template.interpolations`. | |
| A :attr:`~Template.values` attribute holds the values of the interpolations: | |
| >>> cheese = 'Camembert' | |
| >>> template = t'Ah! We do have {cheese}.' | |
| >>> template.strings | |
| ('Ah! We do have ', '.') | |
| >>> template.interpolations | |
| (Interpolation('Camembert', ...),) | |
| >>> template.values | |
| ('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 | |
| .. code-block:: python | |
| template.strings: ('Ah! We do have ', '.') | |
| template.values: ( 'Camembert', ) | |
| .. rubric:: Attributes | |
| .. attribute:: strings | |
| :type: tuple[str, ...] | |
| A :class:`tuple` of the static strings in the template. | |
| >>> cheese = 'Camembert' | |
| >>> template = t'Ah! We do have {cheese}.' | |
| >>> template.strings | |
| ('Ah! We do have ', '.') | |
| Empty strings *are* included in the tuple: | |
| >>> response = 'We do have ' | |
| >>> cheese = 'Camembert' | |
| >>> template = t'Ah! {response}{cheese}.' | |
| >>> template.strings | |
| ('Ah! ', '', '.') | |
| 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 :class:`tuple` of the interpolations in the template. | |
| >>> cheese = 'Camembert' | |
| >>> template = t'Ah! We do have {cheese}.' | |
| >>> template.interpolations | |
| (Interpolation('Camembert', 'cheese', None, ''),) | |
| The ``interpolations`` tuple may be empty and always contains one fewer | |
| values than the ``strings`` tuple: | |
| >>> t'Red Leicester'.interpolations | |
| () | |
| .. attribute:: values | |
| :type: tuple[object, ...] | |
| A tuple of all interpolated values in the template. | |
| >>> cheese = 'Camembert' | |
| >>> template = t'Ah! We do have {cheese}.' | |
| >>> template.values | |
| ('Camembert',) | |
| 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)``. | |
| .. rubric:: Methods | |
| .. method:: __new__(*args: str | Interpolation) | |
| While literal syntax is the most common way to create a :class:`!Template`, | |
| it is also possible to create them directly using the constructor: | |
| >>> 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, ''), '.'] | |
| 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: | |
| >>> from string.templatelib import Template | |
| >>> template = Template('Ah! We do have ', 'Camembert', '.') | |
| >>> template.strings | |
| ('Ah! We do have Camembert.',) | |
| 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: | |
| >>> from string.templatelib import Interpolation, Template | |
| >>> template = Template( | |
| ... Interpolation('Camembert', 'cheese'), | |
| ... Interpolation('.', 'punctuation'), | |
| ... ) | |
| >>> template.strings | |
| ('', '', '') | |
| .. describe:: iter(template) | |
| Iterate over the template, yielding each non-empty string and | |
| :class:`Interpolation` in the correct order: | |
| >>> cheese = 'Camembert' | |
| >>> list(t'Ah! We do have {cheese}.') | |
| ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.'] | |
| .. caution:: | |
| Empty strings are **not** included in the iteration: | |
| >>> 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 | |
| Concatenate this template with another, returning a new | |
| :class:`!Template` instance: | |
| >>> cheese = 'Camembert' | |
| >>> list(t'Ah! ' + t'We do have {cheese}.') | |
| ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.'] | |
| 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): | |
| >>> 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, '')] | |
| .. 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. | |
| 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 | |
| Interpolations are :ref:`generic <generics>` over the types of their values. | |
| .. rubric:: Attributes | |
| .. attribute:: value | |
| :type: object | |
| The evaluated value of the interpolation. | |
| >>> t'{1 + 2}'.interpolations[0].value | |
| 3 | |
| .. attribute:: expression | |
| :type: str | |
| For interpolations created by t-string literals, :attr:`!expression` | |
| is the expression text found inside the curly brackets (``{`` & ``}``), | |
| including any whitespace, excluding the curly brackets themselves, | |
| and ending before the first ``!``, ``:``, or ``=`` if any is present. | |
| For manually created interpolations, :attr:`!expression` is the arbitrary | |
| string provided when constructing the interpolation instance. | |
| We recommend using valid Python expressions or the empty string for the | |
| ``expression`` field of manually created :class:`!Interpolation` | |
| instances, although this is not enforced at runtime. | |
| >>> t'{1 + 2}'.interpolations[0].expression | |
| '1 + 2' | |
| .. attribute:: conversion | |
| :type: typing.Literal['a', 'r', 's'] | None | |
| The conversion to apply to the value, or ``None``. | |
| The :attr:`!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:`!conversion`. | |
| For convenience, the :func:`convert` function can be used to mimic | |
| f-string conversion semantics. | |
| .. attribute:: format_spec | |
| :type: str | |
| The format specification to apply to 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' | |
| .. 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 interpolation will | |
| decide how to interpret and whether to apply the format specification. | |
| As a result, :attr:`!format_spec` values in interpolations | |
| can be arbitrary strings, | |
| including those that do not conform to the :func:`format` protocol. | |
| .. rubric:: Methods | |
| .. 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 | |
| ---------------- | |
| .. 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 (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. |