Skip to content

Commit 05b5beb

Browse files
committed
Substantial updates to string.templatelib.rst docs
1 parent eaec534 commit 05b5beb

File tree

1 file changed

+162
-67
lines changed

1 file changed

+162
-67
lines changed

Doc/library/string.templatelib.rst

Lines changed: 162 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,60 @@
2020
Template strings
2121
----------------
2222

23-
Documentation forthcoming for PEP 750 template strings, also known as t-strings.
24-
2523
.. versionadded:: 3.14
2624

25+
Template strings are a generalization of :ref:`f-strings <f-strings>`
26+
that allow for powerful string processing. The :class:`Template` class
27+
provides direct access to the static and interpolated (substituted) parts of
28+
a string.
2729

28-
.. _templatelib-template:
29-
30-
Template
31-
--------
32-
33-
The :class:`!Template` class describes the contents of a template string.
34-
35-
The most common way to create a new :class:`!Template` instance is to use the t-string literal syntax. This syntax is identical to that of :ref:`f-strings`, except that the string is prefixed with a ``t`` instead of an ``f``. For example, the following code creates a :class:`Template` that can be used to format strings:
30+
The most common way to create a :class:`!Template` instance is to use the
31+
:ref:`t-string literal syntax <t-strings>`. This syntax is identical to that of
32+
:ref:`f-strings`, except that the string is prefixed with a ``t`` instead of
33+
an ``f``. For example, the following code creates a :class:`!Template`:
3634

3735
>>> name = "World"
3836
>>> greeting = t"Hello {name}!"
3937
>>> type(greeting)
4038
<class 'string.templatelib.Template'>
41-
>>> print(list(greeting))
39+
>>> list(greeting)
4240
['Hello ', Interpolation('World', 'name', None, ''), '!']
4341

44-
It is also possible to create a :class:`!Template` directly, using its constructor. This takes an arbitrary collection of strings and :class:`Interpolation` instances:
42+
The :class:`Interpolation` class represents an expression inside a template
43+
string. It contains the evaluated value of the interpolation (``'World'`` in
44+
this example), the original expression text (``'name'``), and optional
45+
conversion and format specification attributes.
4546

46-
>>> from string.templatelib import Interpolation, Template
47+
Templates can be processed in a variety of ways. For instance, here's a
48+
simple example that converts static strings to lowercase and interpolated
49+
values to uppercase:
50+
51+
>>> from string.templatelib import Template
52+
>>> def lower_upper(template: Template) -> str:
53+
... return ''.join(
54+
... part.lower() if isinstance(part, str) else part.value.upper()
55+
... for part in template
56+
... )
57+
...
4758
>>> name = "World"
48-
>>> greeting = Template("Hello, ", Interpolation(name, "name"), "!")
49-
>>> print(list(greeting))
50-
['Hello, ', Interpolation('World', 'name', None, ''), '!']
59+
>>> greeting = t"Hello {name}!"
60+
>>> lower_upper(greeting)
61+
'hello WORLD!'
62+
63+
More interesting use cases include sanitizing user input (e.g., to prevent SQL
64+
injection or cross-site scripting attacks) or processing domain specific
65+
languages.
66+
67+
68+
.. _templatelib-template:
69+
70+
Template
71+
--------
72+
73+
The :class:`!Template` class describes the contents of a template string.
74+
75+
:class:`!Template` instances are shallow immutable: their attributes cannot be
76+
reassigned.
5177

5278
.. class:: Template(*args)
5379

@@ -56,18 +82,28 @@ It is also possible to create a :class:`!Template` directly, using its construct
5682
:param args: A mix of strings and :class:`Interpolation` instances in any order.
5783
:type args: str | Interpolation
5884

85+
While :ref:`t-string literal syntax <t-strings>` is the most common way to
86+
create :class:`!Template` instances, it is also possible to create them
87+
directly using the constructor:
88+
89+
>>> from string.templatelib import Interpolation, Template
90+
>>> name = "World"
91+
>>> greeting = Template("Hello, ", Interpolation(name, "name"), "!")
92+
>>> list(greeting)
93+
['Hello, ', Interpolation('World', 'name', None, ''), '!']
94+
5995
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:
6096

6197
>>> from string.templatelib import Template
6298
>>> greeting = Template("Hello ", "World", "!")
63-
>>> print(greeting.strings)
99+
>>> greeting.strings
64100
('Hello World!',)
65101

66102
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 a single value in the :attr:`~Template.strings` attribute:
67103

68104
>>> from string.templatelib import Interpolation, Template
69105
>>> greeting = Template(Interpolation("World", "name"), Interpolation("!", "punctuation"))
70-
>>> print(greeting.strings)
106+
>>> greeting.strings
71107
('', '', '')
72108

73109
.. attribute:: strings
@@ -76,120 +112,179 @@ It is also possible to create a :class:`!Template` directly, using its construct
76112
A :ref:`tuple <tut-tuples>` of the static strings in the template.
77113

78114
>>> name = "World"
79-
>>> print(t"Hello {name}!".strings)
115+
>>> t"Hello {name}!".strings
80116
('Hello ', '!')
81117

82118
Empty strings *are* included in the tuple:
83119

84120
>>> name = "World"
85-
>>> print(t"Hello {name}{name}!".strings)
121+
>>> t"Hello {name}{name}!".strings
86122
('Hello ', '', '!')
87123

124+
The ``strings`` tuple is never empty, and always contains one more
125+
string than the ``interpolations`` and ``values`` tuples:
126+
127+
>>> t"".strings
128+
('',)
129+
>>> t"{'cheese'}".strings
130+
('', '')
131+
>>> t"{'cheese'}".values
132+
('cheese',)
133+
88134
.. attribute:: interpolations
89135
:type: tuple[Interpolation, ...]
90136

91137
A tuple of the interpolations in the template.
92138

93139
>>> name = "World"
94-
>>> print(t"Hello {name}!".interpolations)
140+
>>> t"Hello {name}!".interpolations
95141
(Interpolation('World', 'name', None, ''),)
96142

143+
The ``interpolations`` tuple may be empty and always contains one fewer
144+
values than the ``strings`` tuple:
145+
146+
>>> t"Hello!".interpolations
147+
()
97148

98149
.. attribute:: values
99150
:type: tuple[Any, ...]
100151

101152
A tuple of all interpolated values in the template.
102153

103154
>>> name = "World"
104-
>>> print(t"Hello {name}!".values)
155+
>>> t"Hello {name}!".values
105156
('World',)
106157

158+
The ``values`` tuple is always the same length as the
159+
``interpolations`` tuple.
160+
107161
.. method:: __iter__()
108162

109-
Iterate over the template, yielding each string and :class:`Interpolation` in order.
163+
Iterate over the template, yielding each string and
164+
:class:`Interpolation` in order.
110165

111166
>>> name = "World"
112-
>>> print(list(t"Hello {name}!"))
167+
>>> list(t"Hello {name}!")
113168
['Hello ', Interpolation('World', 'name', None, ''), '!']
114169

115170
Empty strings are *not* included in the iteration:
116171

117172
>>> name = "World"
118-
>>> print(list(t"Hello {name}{name}"))
173+
>>> list(t"Hello {name}{name}")
119174
['Hello ', Interpolation('World', 'name', None, ''), Interpolation('World', 'name', None, '')]
120175

121-
:returns: An iterable of all the parts in the template.
122-
:rtype: typing.Iterator[str | Interpolation]
176+
.. method:: __add__(other)
177+
178+
Concatenate this template with another template, returning a new
179+
:class:`!Template` instance:
180+
181+
>>> name = "World"
182+
>>> list(t"Hello " + t"there {name}!")
183+
['Hello there ', Interpolation('World', 'name', None, ''), '!']
184+
185+
Concatenation between a :class:`!Template` and a ``str`` is *not* supported.
186+
This is because it is ambiguous whether the string should be treated as
187+
a static string or an interpolation. If you want to concatenate a
188+
:class:`!Template` with a string, you should either wrap the string
189+
directly in a :class:`!Template` (to treat it as a static string) or use
190+
an :class:`!Interpolation` (to treat it as dynamic):
191+
192+
>>> from string.templatelib import Template, Interpolation
193+
>>> greeting = t"Hello "
194+
>>> greeting += Template("there ") # Treat as static
195+
>>> name = "World"
196+
>>> greeting += Template(Interpolation(name, "name")) # Treat as dynamic
197+
>>> list(greeting)
198+
['Hello there ', Interpolation('World', 'name', None, '')]
199+
123200

124-
.. class:: Interpolation(*args)
201+
.. class:: Interpolation(value, expression="", conversion=None, format_spec="")
125202

126203
Create a new :class:`!Interpolation` object.
127204

128205
:param value: The evaluated, in-scope result of the interpolation.
129206
:type value: object
130207

131-
:param expression: The original *text* of the interpolation's Python :ref:`expressions <expressions>`.
208+
:param expression: The text of a valid Python expression, or an empty string
132209
:type expression: str
133210

134211
:param conversion: The optional :ref:`conversion <formatstrings>` to be used, one of r, s, and a,.
135-
:type value: Literal["a", "r", "s"] | None
212+
:type conversion: Literal["a", "r", "s"] | None
136213

137214
:param format_spec: An optional, arbitrary string used as the :ref:`format specification <formatspec>` to present the value.
215+
:type format_spec: str
138216

139-
The :class:`!Interpolation` type represents an expression inside a template string. It is shallow immutable -- its attributes cannot be reassigned.
217+
The :class:`!Interpolation` type represents an expression inside a template string.
140218

141-
>>> name = "World"
142-
>>> template = t"Hello {name}"
143-
>>> template.interpolations[0].value
144-
'World'
145-
>>> template.interpolations[0].value = "Galaxy"
146-
Traceback (most recent call last):
147-
File "<input>", line 1, in <module>
148-
AttributeError: readonly attribute
219+
:class:`!Interpolation` instances are shallow immutable: their attributes cannot be
220+
reassigned.
149221

150-
While f-strings and t-strings are largely similar in syntax and expectations, the :attr:`~Interpolation.conversion` and :attr:`~Interpolation.format_spec` behave differently. With f-strings, these are applied to the resulting value automatically. For example, in this ``format_spec``:
222+
.. property:: value
151223

152-
>>> value = 42
153-
>>> f"Value: {value:.2f}"
154-
'Value: 42.00'
224+
:returns: The evaluated value of the interpolation.
225+
:rtype: object
155226

156-
With a t-string :class:`!Interpolation`, the template function is expected to apply this to the value:
227+
>>> t"{1 + 2}".interpolations[0].value
228+
3
157229

158-
>>> value = 42
159-
>>> template = t"Value: {value:.2f}"
160-
>>> template.interpolations[0].value
161-
42
230+
.. property:: expression
162231

163-
.. property:: __match_args__
232+
:returns: The text of a valid Python expression, or an empty string.
233+
:rtype: str
164234

165-
:returns: A tuple of the attributes to use for structural pattern matching.
166-
:rtype: (Literal["value"], Literal["expression"], Literal["conversion"], Literal["format_spec"])
235+
The :attr:`~Interpolation.expression` is the original text of the
236+
interpolation's Python expression, if the interpolation was created
237+
from a t-string literal. Developers creating interpolations manually
238+
should either set this to an empty string or choose a suitable valid
239+
Python expression.
167240

241+
>>> t"{1 + 2}".interpolations[0].expression
242+
'1 + 2'
168243

169-
.. property:: value
244+
.. property:: conversion
170245

171-
:returns: The evaluated value of the interpolation.
172-
:rtype: object
246+
:returns: The conversion to apply to the value, or ``None``
247+
:rtype: Literal["a", "r", "s"] | None
173248

174-
.. property:: expression
249+
The :attr:`!Interpolation.conversion` is the optional conversion to apply
250+
to the value:
251+
252+
>>> t"{1 + 2!a}".interpolations[0].conversion
253+
'a'
254+
255+
.. note::
256+
257+
Unlike f-strings, where conversions are applied automatically,
258+
the expected behavior with t-strings is that code that *processes* the
259+
:class:`!Template` will decide how to interpret and whether to apply
260+
the :attr:`!Interpolation.conversion`.
175261

176-
:returns: The original text of the interpolation's Python expression if the interpolation was created from a t-string literal
262+
.. property:: format_spec
263+
264+
:returns: The format specification to apply to the value.
177265
:rtype: str
178266

179-
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
180-
interpolations manually should either set this to an empty
181-
string or choose a suitable valid python expression.
267+
The :attr:`!Interpolation.format_spec` is an optional, arbitrary string
268+
used as the format specification to present the value:
182269

183-
.. property:: conversion
270+
>>> t"{1 + 2:.2f}".interpolations[0].format_spec
271+
'.2f'
184272

185-
:returns: The conversion to apply to the value, one of "a", "r", or "s", or None.
186-
:rtype: Literal["a", "r", "s"] | None
273+
.. note::
187274

188-
The :attr:`~Interpolation.conversion` is the optional conversion to apply to the value. This is one of "a", "r", or "s", or None if no conversion is specified.
275+
Unlike f-strings, where format specifications are applied automatically
276+
via the :func:`format` protocol, the expected behavior with
277+
t-strings is that code that *processes* the :class:`!Template` will
278+
decide how to interpret and whether to apply the format specification.
279+
As a result, :attr:`!Interpolation.format_spec` values in
280+
:class:`!Template` instances can be arbitrary strings, even those that
281+
do not necessarily conform to the rules of Python's :func:`format`
282+
protocol.
189283

190-
.. property:: format_spec
284+
.. property:: __match_args__
285+
286+
:returns: A tuple of the attributes to use for structural pattern matching.
191287

192-
:returns: The format specification to apply to the value.
193-
:rtype: str
288+
The tuple returned is ``('value', 'expression', 'conversion', 'format_spec')``.
289+
This allows for :ref:`pattern matching <match>` on :class:`!Interpolation` instances.
194290

195-
The :attr:`~Interpolation.format_spec` is an optional, arbitrary string used as the format specification to present the value. This is similar to the format specification used in :ref:`format strings <formatstrings>`, but it is not limited to a specific set of formats.

0 commit comments

Comments
 (0)