1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
|
: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
.. 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.
|