Skip to content

Commit 5d7491c

Browse files
committed
Remove t-string tutorial (for now)
1 parent 79169d1 commit 5d7491c

File tree

2 files changed

+0
-198
lines changed

2 files changed

+0
-198
lines changed

Doc/library/string.templatelib.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
.. seealso::
1313

14-
* :ref:`T-strings tutorial <tut-t-strings>`
1514
* :ref:`Format strings <f-strings>`
1615
* :ref:`T-string literal syntax <t-strings>`
1716

@@ -31,8 +30,6 @@ While f-strings evaluate to ``str``, t-strings create a :class:`Template`
3130
instance that gives you access to the static and interpolated (in curly braces)
3231
parts of a string *before* they are combined.
3332

34-
See the :ref:`t-strings tutorial <tut-t-strings>` for an introduction.
35-
3633

3734
.. _templatelib-template:
3835

Doc/tutorial/inputoutput.rst

Lines changed: 0 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -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
176160
on the ``=`` specifier. For a reference on these format specifications, see
177161
the 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>&lt;script&gt;alert(&#x27;evil&#x27;);&lt;/script&gt;</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

360165
The String format() Method

0 commit comments

Comments
 (0)