From 628dc27695a15bab284771a608a58e1f6a7bf7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Simon?= Date: Thu, 22 May 2025 23:25:19 +0200 Subject: [PATCH 1/6] Add Template / Interpolation formatting to pprint --- Lib/pprint.py | 23 +++++++++++++++++++++++ Lib/test/test_pprint.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Lib/pprint.py b/Lib/pprint.py index dc0953cec67a58..09e287ee4c9f4a 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -566,6 +566,29 @@ def _pprint_user_string(self, object, stream, indent, allowance, context, level) _dispatch[_collections.UserString.__repr__] = _pprint_user_string + def _pprint_template(self, object, stream, indent, allowance, context, level): + cls_name = object.__class__.__name__ + indent += len(cls_name) + 1 + items = (("strings", object.strings), + ("interpolations", object.interpolations)) + stream.write(cls_name + '(') + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(')') + + def _pprint_interpolation(self, object, stream, indent, allowance, context, level): + cls_name = object.__class__.__name__ + indent += len(cls_name) + items = (object.value, object.expression, + object.conversion, object.format_spec) + stream.write(cls_name + '(') + 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 + del t + def _safe_repr(self, object, context, maxlevels, level): # Return triple (repr_string, isreadable, isrecursive). typ = type(object) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index f68996f72b15b5..be63ce1edc16d4 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1130,6 +1130,38 @@ def test_user_string(self): 'jumped over a ' 'lazy dog'}""") + def test_template(self): + d = t"" + self.assertEqual(pprint.pformat(d), + "Template(strings=('',), interpolations=())") + self.assertEqual(pprint.pformat(d), repr(d)) + self.assertEqual(pprint.pformat(d, width=1), +"""\ +Template(strings=('',), + interpolations=())""") + name = "World" + d = t"Hello {name}" + self.assertEqual(pprint.pformat(d), +"""\ +Template(strings=('Hello ', ''), + interpolations=(Interpolation('World', 'name', None, ''),))""") + 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'),))""") + class DottedPrettyPrinter(pprint.PrettyPrinter): From f14f7ad600d33123739b82e5faf67b4fa142d124 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 10:28:52 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-05-23-10-28-51.gh-issue-134551.0rnq0X.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-05-23-10-28-51.gh-issue-134551.0rnq0X.rst diff --git a/Misc/NEWS.d/next/Library/2025-05-23-10-28-51.gh-issue-134551.0rnq0X.rst b/Misc/NEWS.d/next/Library/2025-05-23-10-28-51.gh-issue-134551.0rnq0X.rst new file mode 100644 index 00000000000000..94e0c1e0b225d6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-23-10-28-51.gh-issue-134551.0rnq0X.rst @@ -0,0 +1 @@ +Add t-strings support to pprint functions From 9a7a523be25784e086da96d846ebc8ff9c9bd351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Simon?= Date: Fri, 23 May 2025 13:53:35 +0200 Subject: [PATCH 3/6] Exclude test_pprint from ruff formatting --- Lib/test/.ruff.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 7aa8a4785d6844..2829485cd73889 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -12,6 +12,7 @@ extend-exclude = [ "test_annotationlib.py", "test_string/test_templatelib.py", "test_tstring.py", + "test_pprint.py", # New grammar constructions may not yet be recognized by Ruff, # and tests re-use the same names as only the grammar is being checked. "test_grammar.py", From 93380b56016ec2f383109d13b719d5fe2165e28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Simon?= Date: Fri, 23 May 2025 17:56:56 +0200 Subject: [PATCH 4/6] Fix names sorting in Lib/test/.ruff.toml Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Lib/test/.ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index 2829485cd73889..1a0fbb3b4ca9c1 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -10,9 +10,9 @@ extend-exclude = [ "encoded_modules/module_koi8_r.py", # SyntaxError because of t-strings "test_annotationlib.py", + "test_pprint.py", "test_string/test_templatelib.py", "test_tstring.py", - "test_pprint.py", # New grammar constructions may not yet be recognized by Ruff, # and tests re-use the same names as only the grammar is being checked. "test_grammar.py", From 507bf307337c6eb78246ab99b90a81555c6f5c8a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:54:17 +0300 Subject: [PATCH 5/6] Add t-string support to pprint with expand=True --- Lib/pprint.py | 56 ++++++++++++++++++++++++++++++++--------- Lib/test/test_pprint.py | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 1128e841e19323..7355021998081d 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -737,21 +737,53 @@ 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__ - indent += len(cls_name) + 1 - items = (("strings", object.strings), - ("interpolations", object.interpolations)) - stream.write(cls_name + '(') - self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(')') + if self._expand: + indent += self._indent_per_level + else: + indent += len(cls_name) + 1 + + items = ( + ("strings", object.strings), + ("interpolations", object.interpolations), + ) + stream.write(self._format_block_start(cls_name + "(", indent)) + self._format_namespace_items( + items, stream, indent, allowance, context, level + ) + stream.write( + self._format_block_end(")", indent - self._indent_per_level) + ) def _pprint_interpolation(self, object, stream, indent, allowance, context, level): cls_name = object.__class__.__name__ - indent += len(cls_name) - items = (object.value, object.expression, - object.conversion, object.format_spec) - stream.write(cls_name + '(') - self._format_items(items, stream, indent, allowance, context, level) - stream.write(')') + if self._expand: + indent += self._indent_per_level + items = ( + ("value", object.value), + ("expression", object.expression), + ("conversion", object.conversion), + ("format_spec", object.format_spec), + ) + stream.write(self._format_block_start(cls_name + "(", indent)) + self._format_namespace_items( + items, stream, indent, allowance, context, level + ) + stream.write( + self._format_block_end(")", indent - self._indent_per_level) + ) + else: + indent += len(cls_name) + items = ( + object.value, + object.expression, + object.conversion, + object.format_spec, + ) + stream.write(cls_name + "(") + self._format_items( + items, stream, indent, allowance, context, level + ) + stream.write(")") t = t"{0}" _dispatch[type(t).__repr__] = _pprint_template diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 6fb755abf25840..041c2072b9e253 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1547,6 +1547,54 @@ def test_template(self): 's', 'z'),))""") + def test_expand_template(self): + d = t"" + self.assertEqual( + pprint.pformat(d, expand=True), + "Template(strings=('',), interpolations=())", + ) + name = "World" + d = t"Hello {name}" + self.assertEqual( + pprint.pformat(d, width=40, indent=4, expand=True), + """\ +Template( + strings=('Hello ', ''), + interpolations=( + Interpolation( + value='World', + expression='name', + conversion=None, + format_spec='', + ), + ), +)""", + ) + ver = {3.13: False, 3.14: True} + d = t"Hello { {"name": "Python", "version": ver}!s:z}!" + self.assertEqual( + 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', + ), + ), +)""", + ) + def test_expand_dataclass(self): @dataclasses.dataclass class DummyDataclass: From a486838d84713fdad430f1ee4f4289f1698f8498 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:57:01 +0300 Subject: [PATCH 6/6] Add to What's New --- Doc/whatsnew/3.15.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index c754b634ecccfa..fe2ddfdcd0e917 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1551,7 +1551,11 @@ pprint :func:`pprint.pformat`, :func:`pprint.pp`. If true, the output will be formatted similar to pretty-printed :func:`json.dumps` when *indent* is supplied. - (Contributed by Stefan Todoran and Semyon Moroz in :gh:`112632`.) + (Contributed by Stefan Todoran, Semyon Moroz and Hugo van Kemenade in + :gh:`112632`.) + +* Add t-string support to :mod:`pprint`. + (Contributed by Loïc Simon and Hugo van Kemenade in :gh:`134551`.) sre_*