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
31 changes: 15 additions & 16 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,13 +770,15 @@ 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
)
Expand Down
159 changes: 114 additions & 45 deletions Lib/test/test_pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -1517,56 +1517,129 @@ def test_user_string(self):

def test_template(self):
d = t""
self.assertEqual(pprint.pformat(d),
"Template(strings=('',), interpolations=())")
self.assertEqual(pprint.pformat(d), "Template()")
self.assertEqual(pprint.pformat(d), repr(d))
self.assertEqual(pprint.pformat(d, width=1),
"""\
Template(strings=('',),
interpolations=())""")
self.assertEqual(pprint.pformat(d, width=1), "Template()")
name = "World"
d = t"Hello {name}"
self.assertEqual(pprint.pformat(d),
"""\
Template(strings=('Hello ', ''),
interpolations=(Interpolation('World', 'name', None, ''),))""")
Template('Hello ', Interpolation('World', 'name'))""")
d = t"Hello {name!r}"
self.assertEqual(pprint.pformat(d),
"""\
Template('Hello ', Interpolation('World', 'name', 'r'))""")
d = t"Hello {name:0}"
self.assertEqual(pprint.pformat(d),
"""\
Template('Hello ', Interpolation('World', 'name', None, '0'))""")
d = t"Hello {name!r:0}"
self.assertEqual(pprint.pformat(d),
"""\
Template('Hello ', Interpolation('World', 'name', 'r', '0'))""")
d = t"Hello {name}"
self.assertEqual(pprint.pformat(d, width=10),
"""\
Template('Hello ',
Interpolation('World',
'name'))""")
d = t"Hello {name!r}"
self.assertEqual(pprint.pformat(d, width=10),
"""\
Template('Hello ',
Interpolation('World',
'name',
'r'))""")
d = t"Hello {name:0}"
self.assertEqual(pprint.pformat(d, width=10),
"""\
Template('Hello ',
Interpolation('World',
'name',
None,
'0'))""")
d = t"Hello {name!r:0}"
self.assertEqual(pprint.pformat(d, width=10),
"""\
Template('Hello ',
Interpolation('World',
'name',
'r',
'0'))""")
ver = {3.13: False, 3.14: True}
d = t"Hello { {"name": "Python", "version": ver}!s:z}!"
self.assertEqual(pprint.pformat(d, width=1),
"""\
Template(strings=('Hello ',
'!'),
interpolations=(Interpolation({'name': 'Python',
'version': {3.13: False,
3.14: True}},
' '
'{"name": '
'"Python", '
'"version": '
'ver}',
's',
'z'),))""")
Template('Hello ',
Interpolation({'name': 'Python',
'version': {3.13: False,
3.14: True}},
' '
'{"name": '
'"Python", '
'"version": '
'ver}',
's',
'z'),
'!')""")

def test_expand_template(self):
d = t""
self.assertEqual(
pprint.pformat(d, expand=True),
"Template(strings=('',), interpolations=())",
"Template()",
)
name = "World"
d = t"Hello {name}"
self.assertEqual(
pprint.pformat(d, width=40, indent=4, expand=True),
pprint.pformat(d, width=30, indent=4, expand=True),
"""\
Template(
strings=('Hello ', ''),
interpolations=(
Interpolation(
value='World',
expression='name',
conversion=None,
format_spec='',
),
'Hello ',
Interpolation(
value='World',
expression='name',
),
)""",
)
d = t"Hello {name!r}"
self.assertEqual(
pprint.pformat(d, width=30, indent=4, expand=True),
"""\
Template(
'Hello ',
Interpolation(
value='World',
expression='name',
conversion='r',
),
)""",
)
d = t"Hello {name:0}"
self.assertEqual(
pprint.pformat(d, width=30, indent=4, expand=True),
"""\
Template(
'Hello ',
Interpolation(
value='World',
expression='name',
conversion=None,
format_spec='0',
),
)""",
)
d = t"Hello {name!r:0}"
self.assertEqual(
pprint.pformat(d, width=30, indent=4, expand=True),
"""\
Template(
'Hello ',
Interpolation(
value='World',
expression='name',
conversion='r',
format_spec='0',
),
)""",
)
Expand All @@ -1576,22 +1649,18 @@ def test_expand_template(self):
pprint.pformat(d, width=40, indent=4, expand=True),
"""\
Template(
strings=('Hello ', '!'),
interpolations=(
Interpolation(
value={
'name': 'Python',
'version': {
3.13: False,
3.14: True,
},
},
expression=' {"name": "Python", '
'"version": ver}',
conversion='s',
format_spec='z',
),
'Hello ',
Interpolation(
value={
'name': 'Python',
'version': {3.13: False, 3.14: True},
},
expression=' {"name": "Python", '
'"version": ver}',
conversion='s',
format_spec='z',
),
'!',
)""",
)

Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_string/test_templatelib.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ def test_template_values(self):
t = t'Hello, {name}, {age} from {country}'
self.assertEqual(t.values, ("Lys", 0, "GR"))

def test_repr(self):
self.assertEqual(repr(t''), 'Template()')
self.assertEqual(repr(t'foo'), "Template('foo')")

# Test various combination for present/absent conversion and format_spec
x = 42
self.assertEqual(
repr(t'{x}'),
"Template(Interpolation(42, 'x'))")
self.assertEqual(
repr(t'{x!r}'),
"Template(Interpolation(42, 'x', 'r'))")
self.assertEqual(
repr(t'{x:02}'),
"Template(Interpolation(42, 'x', None, '02'))")
self.assertEqual(
repr(t'a{x!r:02}b'),
"Template('a', Interpolation(42, 'x', 'r', '02'), 'b')")

# Test a "recursive" template
x = []
t = t'a{x}b'
x.append(t)
self.assertEqual(
repr(t),
"Template('a', Interpolation([Template(...)], 'x'), 'b')")

def test_pickle_template(self):
user = 'test'
for template in (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Change :func:`repr` output of :class:`string.templatelib.Template` objects
to list the parts of the template in their original source order.
28 changes: 25 additions & 3 deletions Objects/interpolationobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,31 @@ static PyObject *
interpolation_repr(PyObject *op)
{
interpolationobject *self = interpolationobject_CAST(op);
return PyUnicode_FromFormat("%s(%R, %R, %R, %R)",
_PyType_Name(Py_TYPE(self)), self->value, self->expression,
self->conversion, self->format_spec);

/* Only emit trailing arguments that differ from their default values
(conversion=None and format_spec=""). We never use keyword arguments, so
if 'format_spec' is non-default, 'conversion' has to be emitted too even
when it still has its default value. */
int show_format_spec = PyUnicode_GET_LENGTH(self->format_spec) > 0;
int show_conversion = show_format_spec || self->conversion != Py_None;

if (show_format_spec) {
return PyUnicode_FromFormat("%s(%R, %R, %R, %R)",
_PyType_Name(Py_TYPE(self)),
self->value, self->expression,
self->conversion, self->format_spec);
}
else if (show_conversion) {
return PyUnicode_FromFormat("%s(%R, %R, %R)",
_PyType_Name(Py_TYPE(self)),
self->value, self->expression,
self->conversion);
}
else {
return PyUnicode_FromFormat("%s(%R, %R)",
_PyType_Name(Py_TYPE(self)),
self->value, self->expression);
}
}

static PyMemberDef interpolation_members[] = {
Expand Down
Loading
Loading