Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions Doc/library/string.templatelib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ To write a t-string, use a ``'t'`` prefix instead of an ``'f'``, like so:

>>> 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', ''),)
)
<Template t't-strings are new in Python {pi!s=3.14}!' at 0x...>

Types
-----
Expand Down Expand Up @@ -118,7 +115,7 @@ Types
>>> cheese = 'Camembert'
>>> template = t'Ah! We do have {cheese}.'
>>> template.interpolations
(Interpolation('Camembert', 'cheese', None, ''),)
(Interpolation('Camembert', 'cheese'),)

The ``interpolations`` tuple may be empty and always contains one fewer
values than the ``strings`` tuple:
Expand Down Expand Up @@ -153,7 +150,7 @@ Types
... 'Ah! We do have ', Interpolation(cheese, 'cheese'), '.'
... )
>>> list(template)
['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
['Ah! We do have ', Interpolation('Camembert', 'cheese'), '.']

If multiple strings are passed consecutively, they will be concatenated
into a single value in the :attr:`~Template.strings` attribute. For example,
Expand Down Expand Up @@ -184,7 +181,7 @@ Types

>>> cheese = 'Camembert'
>>> list(t'Ah! We do have {cheese}.')
['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
['Ah! We do have ', Interpolation('Camembert', 'cheese'), '.']

.. caution::

Expand All @@ -194,8 +191,8 @@ Types
>>> cheese = 'Camembert'
>>> list(t'Ah! {response}{cheese}.') # doctest: +NORMALIZE_WHITESPACE
['Ah! ',
Interpolation('We do have ', 'response', None, ''),
Interpolation('Camembert', 'cheese', None, ''),
Interpolation('We do have ', 'response'),
Interpolation('Camembert', 'cheese'),
'.']

.. describe:: template + other
Expand All @@ -206,7 +203,7 @@ Types

>>> cheese = 'Camembert'
>>> list(t'Ah! ' + t'We do have {cheese}.')
['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.']
['Ah! We do have ', Interpolation('Camembert', 'cheese'), '.']

Concatenating a :class:`!Template` and a ``str`` is **not** supported.
This is because it is unclear whether the string should be treated as
Expand All @@ -224,7 +221,7 @@ Types
>>> cheese = 'Camembert'
>>> template += Template(Interpolation(cheese, 'cheese'))
>>> list(template)
['Ah! We do have ', Interpolation('Camembert', 'cheese', None, '')]
['Ah! We do have ', Interpolation('Camembert', 'cheese')]


.. class:: Interpolation
Expand Down
2 changes: 1 addition & 1 deletion Doc/reference/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ Also, template string literals may only be combined with other template
string literals::

>>> t"Hello" t"{name}!"
Template(strings=('Hello', '!'), interpolations=(...))
<Template t'Hello{name='Blaise'}!' at 0x...>

Formally:

Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_interpolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) _PyInterpolation_Build(PyObject *value, PyObject *str,

extern PyStatus _PyInterpolation_InitTypes(PyInterpreterState *interp);
extern PyObject *_PyInterpolation_GetValueRef(PyObject *interpolation);
extern int _PyInterpolation_WriteSource(PyUnicodeWriter *writer,
PyObject *interpolation);

#ifdef __cplusplus
}
Expand Down
74 changes: 56 additions & 18 deletions Lib/pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,18 +737,13 @@ def _pprint_user_string(self, object, stream, indent, allowance, context, level)

def _pprint_template(self, object, stream, indent, allowance, context, level):
cls_name = object.__class__.__name__
if self._expand:
indent += self._indent_per_level
else:
indent += len(cls_name) + 1

items = (
("strings", object.strings),
("interpolations", object.interpolations),
)
if not self._expand:
indent += len(cls_name)

stream.write(self._format_block_start(cls_name + "(", indent))
self._format_namespace_items(
items, stream, indent, allowance, context, level
self._format_items(
object, stream, indent, allowance, context, level
)
stream.write(
self._format_block_end(")", indent - self._indent_per_level)
Expand All @@ -758,13 +753,15 @@ def _pprint_interpolation(self, object, stream, indent, allowance, context, leve
cls_name = object.__class__.__name__
if self._expand:
indent += self._indent_per_level
stream.write(self._format_block_start(cls_name + "(", indent))
items = (
("value", object.value),
("expression", object.expression),
("conversion", object.conversion),
("format_spec", object.format_spec),
)
stream.write(self._format_block_start(cls_name + "(", indent))
if object.conversion is not None or object.format_spec:
items += (("conversion", object.conversion),)
if object.format_spec:
items += (("format_spec", object.format_spec),)
self._format_namespace_items(
items, stream, indent, allowance, context, level
)
Expand All @@ -773,21 +770,26 @@ def _pprint_interpolation(self, object, stream, indent, allowance, context, leve
)
else:
indent += len(cls_name)
stream.write(cls_name + "(")
items = (
object.value,
object.expression,
object.conversion,
object.format_spec,
)
stream.write(cls_name + "(")
if object.conversion is not None or object.format_spec:
items += (object.conversion,)
if object.format_spec:
items += (object.format_spec,)
self._format_items(
items, stream, indent, allowance, context, level
)
stream.write(")")

t = t"{0}"
_dispatch[type(t).__repr__] = _pprint_template
_dispatch[type(t.interpolations[0]).__repr__] = _pprint_interpolation
_template = type(t)
_dispatch[_template.__repr__] = _pprint_template

_interpolation = type(t.interpolations[0])
_dispatch[_interpolation.__repr__] = _pprint_interpolation
del t

def _safe_repr(self, object, context, maxlevels, level):
Expand Down Expand Up @@ -916,6 +918,42 @@ def _safe_repr(self, object, context, maxlevels, level):
del context[objid]
return typ.__name__ + '([%s])' % ", ".join(components), readable, recursive

if (issubclass(typ, self._template) and r is self._template.__repr__) or \
(issubclass(typ, self._interpolation) and r is self._interpolation.__repr__):
# Don't use the repr of templates and interpolations, since the repr
# of a template resembles its source code (and includes the id of
# the template). Use constructor syntax instead, which is what
# _pprint_template and _pprint_interpolation produce as well.
objid = id(object)
if maxlevels and level >= maxlevels:
return f"{typ.__name__}(...)", False, objid in context
if objid in context:
return _recursion(object), False, True
if typ is self._template:
items = object
else:
items = (object.value, object.expression)
if object.conversion is not None or object.format_spec:
items += (object.conversion,)
if object.format_spec:
items += (object.format_spec,)
context[objid] = 1
readable = True
recursive = False
components = []
append = components.append
level += 1
for item in items:
irepr, ireadable, irecur = self.format(
item, context, maxlevels, level)
append(irepr)
readable = readable and ireadable
if irecur:
recursive = True
del context[objid]
rep = "%s(%s)" % (typ.__name__, ", ".join(components))
return rep, readable, recursive

rep = repr(object)
return rep, (rep and not rep.startswith('<')), False

Expand Down
Loading
Loading