@@ -34,22 +34,6 @@ printing space-separated values. There are several ways to format output.
3434 >>> f'Results of the {year} {event}'
3535 'Results of the 2016 Referendum'
3636
37- * When greater control is needed, :ref: `template string literals <tut-t-strings >`
38- can be useful. T-strings -- which begin with ``t `` or ``T `` -- share the
39- same syntax as f-strings but, unlike f-strings, produce a
40- :class: `~string.templatelib.Template ` instance rather than a simple ``str ``.
41- Templates give you access to the static and interpolated (in curly braces)
42- parts of the string *before * they are combined into a final string.
43-
44- ::
45-
46- >>> name = "World"
47- >>> template = t"Hello {name}!"
48- >>> template.strings
49- ('Hello ', '!')
50- >>> template.values
51- ('World',)
52-
5337* The :meth: `str.format ` method of strings requires more manual
5438 effort. You'll still use ``{ `` and ``} `` to mark where a variable
5539 will be substituted and can provide detailed formatting directives,
@@ -176,185 +160,6 @@ See :ref:`self-documenting expressions <bpo-36817-whatsnew>` for more informatio
176160on the ``= `` specifier. For a reference on these format specifications, see
177161the reference guide for the :ref: `formatspec `.
178162
179-
180- .. _tut-t-strings :
181-
182- Template String Literals
183- -------------------------
184-
185- :ref: `Template string literals <t-strings >` (also called t-strings for short)
186- are an extension of :ref: `f-strings <tut-f-strings >` that let you access the
187- static and interpolated parts of a string *before * they are combined into a
188- final string. This provides for greater control over how the string is
189- formatted.
190-
191- The most common way to create a :class: `~string.templatelib.Template ` instance
192- is to use the :ref: `t-string literal syntax <t-strings >`. This syntax is
193- identical to that of :ref: `f-strings ` except that it uses a ``t `` instead of
194- an ``f ``:
195-
196- >>> name = " World"
197- >>> template = t" Hello {name} !"
198- >>> template.strings
199- ('Hello ', '!')
200- >>> template.values
201- ('World',)
202-
203- :class: `!Template ` instances are iterable, yielding each
204- string and :class: `~string.templatelib.Interpolation ` in order:
205-
206- .. testsetup ::
207-
208- name = "World"
209- template = t"Hello {name}!"
210-
211- .. doctest ::
212-
213- >>> list (template)
214- ['Hello ', Interpolation('World', 'name', None, ''), '!']
215-
216- Interpolations represent expressions inside a t-string. They contain the
217- evaluated value of the expression (``'World' `` in this example), the text of
218- the original expression (``'name' ``), and optional conversion and format
219- specification attributes.
220-
221- Templates can be processed in a variety of ways. For instance, here's code that
222- converts static strings to lowercase and interpolated values to uppercase:
223-
224- >>> from string.templatelib import Template
225- >>>
226- >>> def lower_upper (template : Template) -> str :
227- ... return ' ' .join(
228- ... part.lower() if isinstance (part, str ) else part.value.upper()
229- ... for part in template
230- ... )
231- ...
232- >>> name = " World"
233- >>> template = t" Hello {name} !"
234- >>> lower_upper(template)
235- 'hello WORLD!'
236-
237- Template strings are particularly useful for sanitizing user input. Imagine
238- we're building a web application that has user profile pages. Perhaps the
239- ``User `` class is defined like this:
240-
241- >>> from dataclasses import dataclass
242- >>>
243- >>> @ dataclass
244- ... class User :
245- ... name: str
246- ...
247-
248- Imagine using f-strings in to generate HTML for the ``User ``:
249-
250- .. testsetup ::
251-
252- class User:
253- name: str
254- def __init__(self, name: str):
255- self.name = name
256-
257-
258- .. doctest ::
259-
260- >>> # Warning: this is dangerous code. Don't do this!
261- >>> def user_html (user : User) -> str :
262- ... return f " <div><h1> { user.name} </h1></div> "
263- ...
264-
265- This code is dangerous because our website lets users type in their own names.
266- If a user types in a name like ``"<script>alert('evil');</script>" ``, the
267- browser will execute that script when someone else visits their profile page.
268- This is called a *cross-site scripting (XSS) vulnerability *, and it is a form
269- of *injection vulnerability *. Injection vulnerabilities occur when user input
270- is included in a program without proper sanitization, allowing malicious code
271- to be executed. The same sorts of vulnerabilities can occur when user input is
272- included in SQL queries, command lines, or other contexts where the input is
273- interpreted as code.
274-
275- To prevent this, instead of using f-strings, we can use t-strings. Let's
276- update our ``user_html() `` function to return a :class: `~string.templatelib.Template `:
277-
278- >>> from string.templatelib import Template
279- >>>
280- >>> def user_html (user : User) -> Template:
281- ... return t" <div><h1>{user.name} </h1></div>"
282-
283- Now let's implement a function that sanitizes *any * HTML :class: `!Template `:
284-
285- >>> from html import escape
286- >>> from string.templatelib import Template
287- >>>
288- >>> def sanitize_html_template (template : Template) -> str :
289- ... return ' ' .join(
290- ... part if isinstance (part, str ) else escape(part.value)
291- ... for part in template
292- ... )
293- ...
294-
295- This function iterates over the parts of the :class: `!Template `, escaping any
296- interpolated values using the :func: `html.escape ` function, which converts
297- special characters like ``< ``, ``> ``, and ``& `` into their HTML-safe
298- equivalents.
299-
300- Now we can tie it all together:
301-
302- .. testsetup ::
303-
304- from dataclasses import dataclass
305- from string.templatelib import Template
306- from html import escape
307- @dataclass
308- class User:
309- name: str
310- def sanitize_html_template(template: Template) -> str:
311- return ''.join(
312- part if isinstance(part, str) else escape(part.value)
313- for part in template
314- )
315- def user_html(user: User) -> Template:
316- return t"<div><h1>{user.name}</h1></div>"
317-
318- .. doctest ::
319-
320- >>> evil_user = User(name = " <script>alert('evil');</script>" )
321- >>> template = user_html(evil_user)
322- >>> safe = sanitize_html_template(template)
323- >>> print (safe)
324- <div><h1><script>alert('evil');</script></h1></div>
325-
326- We are no longer vulnerable to XSS attacks because we are escaping the
327- interpolated values before they are included in the rendered HTML.
328-
329- Of course, there's no need for code that processes :class: `!Template ` instances
330- to be limited to returning a simple string. For instance, we could imagine
331- defining a more complex ``html() `` function that returns a structured
332- representation of the HTML:
333-
334- >>> from dataclasses import dataclass
335- >>> from string.templatelib import Template
336- >>> from html.parser import HTMLParser
337- >>>
338- >>> @ dataclass
339- ... class Element :
340- ... tag: str
341- ... attributes: dict[str , str ]
342- ... children: list[str | Element]
343- ...
344- >>> def parse_html (template : Template) -> Element:
345- ... """
346- ... Uses Python' s built-in HTMLParser to parse the template,
347- ... handle any interpolated values, and return a tree of
348- ... Element instances.
349- ... """
350- ... ...
351- ...
352-
353- A full implementation of this function would be quite complex and is not
354- provided here. That said, the fact that it is possible to implement a method
355- like ``parse_html() `` showcases the flexibility and power of t-strings.
356-
357-
358163.. _tut-string-format :
359164
360165The String format() Method
0 commit comments