mirror of
https://github.com/python/cpython.git
synced 2025-10-24 02:13:49 +00:00
346 lines
12 KiB
ReStructuredText
346 lines
12 KiB
ReStructuredText
: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 braces) 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
|
|
|
|
.. rubric:: Attributes
|
|
|
|
.. attribute:: value
|
|
:type: object
|
|
|
|
The evaluated value of the interpolation.
|
|
|
|
>>> t'{1 + 2}'.interpolations[0].value
|
|
3
|
|
|
|
.. attribute:: expression
|
|
:type: str
|
|
|
|
The text of a valid Python expression, or an empty string.
|
|
|
|
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'
|
|
|
|
.. 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.
|