From b9baf941e498ae82146e14d4b1ce8e89aea66706 Mon Sep 17 00:00:00 2001 From: stodoran Date: Fri, 24 Jan 2025 18:19:12 -0800 Subject: [PATCH 01/31] Added an option for block formatting to pprint --- Lib/pprint.py | 128 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 46 deletions(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index dc0953cec67a58..1d1c207bf1ca54 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -44,21 +44,21 @@ def pprint(object, stream=None, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): """Pretty-print a Python object to a stream [default is sys.stdout].""" printer = PrettyPrinter( stream=stream, indent=indent, width=width, depth=depth, compact=compact, sort_dicts=sort_dicts, - underscore_numbers=underscore_numbers) + underscore_numbers=underscore_numbers, block_style=block_style) printer.pprint(object) def pformat(object, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): """Format a Python object into a pretty-printed representation.""" return PrettyPrinter(indent=indent, width=width, depth=depth, compact=compact, sort_dicts=sort_dicts, - underscore_numbers=underscore_numbers).pformat(object) + underscore_numbers=underscore_numbers, block_style=block_style).pformat(object) def pp(object, *args, sort_dicts=False, **kwargs): @@ -111,7 +111,7 @@ def _safe_tuple(t): class PrettyPrinter: def __init__(self, indent=1, width=80, depth=None, stream=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -137,6 +137,11 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, underscore_numbers If true, digit groups are separated with underscores. + block_style + If true, the output will be formatted in a block style similar to + pretty-printed json.dumps() when `indent` is supplied. Incompatible + with compact mode. + """ indent = int(indent) width = int(width) @@ -146,6 +151,8 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, raise ValueError('depth must be > 0') if not width: raise ValueError('width must be != 0') + if compact and block_style: + raise ValueError('compact and block_style are incompatible') self._depth = depth self._indent_per_level = indent self._width = width @@ -156,6 +163,7 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, self._compact = bool(compact) self._sort_dicts = sort_dicts self._underscore_numbers = underscore_numbers + self._block_style = bool(block_style) def pprint(self, object): if self._stream is not None: @@ -205,24 +213,39 @@ def _format(self, object, stream, indent, allowance, context, level): return stream.write(rep) + def _format_block_start(self, start_str, indent): + if self._block_style: + return start_str +'\n' + ' ' * indent + else: + return start_str + + def _format_block_end(self, end_str, indent): + if self._block_style: + return '\n' + ' ' * indent + end_str + else: + return end_str + def _pprint_dataclass(self, object, stream, indent, allowance, context, level): # Lazy import to improve module import time from dataclasses import fields as dataclass_fields cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + indent += len(cls_name) + 1 if not self._block_style else self._indent_per_level items = [(f.name, getattr(object, f.name)) for f in dataclass_fields(object) if f.repr] - stream.write(cls_name + '(') + stream.write(self._format_block_start(cls_name + '(', indent)) self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(')') + if self._block_style: + stream.write('\n') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch = {} def _pprint_dict(self, object, stream, indent, allowance, context, level): write = stream.write - write('{') + write(self._format_block_start('{', indent)) if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') + indent_adjust = 0 if self._block_style else -1 + write((self._indent_per_level + indent_adjust) * ' ') length = len(object) if length: if self._sort_dicts: @@ -231,7 +254,7 @@ def _pprint_dict(self, object, stream, indent, allowance, context, level): items = object.items() self._format_dict_items(items, stream, indent, allowance + 1, context, level) - write('}') + write(self._format_block_end('}', indent)) _dispatch[dict.__repr__] = _pprint_dict @@ -241,27 +264,28 @@ def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level return cls = object.__class__ stream.write(cls.__name__ + '(') + recursive_indent = indent + len(cls.__name__) + 1 if not self._block_style else indent self._format(list(object.items()), stream, - indent + len(cls.__name__) + 1, allowance + 1, + recursive_indent, allowance + 1, context, level) stream.write(')') _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict def _pprint_list(self, object, stream, indent, allowance, context, level): - stream.write('[') + stream.write(self._format_block_start('[', indent)) self._format_items(object, stream, indent, allowance + 1, context, level) - stream.write(']') + stream.write(self._format_block_end(']', indent)) _dispatch[list.__repr__] = _pprint_list def _pprint_tuple(self, object, stream, indent, allowance, context, level): - stream.write('(') + stream.write(self._format_block_start('(', indent)) endchar = ',)' if len(object) == 1 else ')' self._format_items(object, stream, indent, allowance + len(endchar), context, level) - stream.write(endchar) + stream.write(self._format_block_end(endchar, indent)) _dispatch[tuple.__repr__] = _pprint_tuple @@ -271,16 +295,16 @@ def _pprint_set(self, object, stream, indent, allowance, context, level): return typ = object.__class__ if typ is set: - stream.write('{') + stream.write(self._format_block_start('{', indent)) endchar = '}' else: - stream.write(typ.__name__ + '({') + stream.write(self._format_block_start(typ.__name__ + '({', indent)) endchar = '})' - indent += len(typ.__name__) + 1 + indent += len(typ.__name__) + 1 if not self._block_style else 0 object = sorted(object, key=_safe_key) self._format_items(object, stream, indent, allowance + len(endchar), context, level) - stream.write(endchar) + stream.write(self._format_block_end(endchar, indent)) _dispatch[set.__repr__] = _pprint_set _dispatch[frozenset.__repr__] = _pprint_set @@ -346,9 +370,9 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): return parens = level == 1 if parens: - indent += 1 + indent += 1 if not self._block_style else self._indent_per_level allowance += 1 - write('(') + write(self._format_block_start('(', indent)) delim = '' for rep in _wrap_bytes_repr(object, self._width - indent, allowance): write(delim) @@ -356,22 +380,25 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): if not delim: delim = '\n' + ' '*indent if parens: - write(')') + write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch[bytes.__repr__] = _pprint_bytes def _pprint_bytearray(self, object, stream, indent, allowance, context, level): write = stream.write - write('bytearray(') - self._pprint_bytes(bytes(object), stream, indent + 10, + write(self._format_block_start('bytearray(', indent)) + write(' ' * self._indent_per_level) + recursive_indent = indent + 10 if not self._block_style else indent + self._indent_per_level + self._pprint_bytes(bytes(object), stream, recursive_indent, allowance + 1, context, level + 1) - write(')') + write(self._format_block_end(')', indent)) _dispatch[bytearray.__repr__] = _pprint_bytearray def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level): stream.write('mappingproxy(') - self._format(object.copy(), stream, indent + 13, allowance + 1, + recursive_indent = indent + 13 if not self._block_style else indent + self._format(object.copy(), stream, recursive_indent, allowance + 1, context, level) stream.write(')') @@ -384,11 +411,11 @@ def _pprint_simplenamespace(self, object, stream, indent, allowance, context, le cls_name = 'namespace' else: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + indent += len(cls_name) + 1 if not self._block_style else self._indent_per_level items = object.__dict__.items() - stream.write(cls_name + '(') + stream.write(self._format_block_start(cls_name + '(', indent)) self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(')') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace @@ -403,7 +430,8 @@ def _format_dict_items(self, items, stream, indent, allowance, context, rep = self._repr(key, context, level) write(rep) write(': ') - self._format(ent, stream, indent + len(rep) + 2, + recursive_indent = indent + len(rep) + 2 if not self._block_style else indent + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -422,7 +450,8 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev # recursive dataclass repr. write("...") else: - self._format(ent, stream, indent + len(key) + 1, + recursive_indent = indent + len(key) + 1 if not self._block_style else indent + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -432,7 +461,8 @@ def _format_items(self, items, stream, indent, allowance, context, level): write = stream.write indent += self._indent_per_level if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') + indent_adjust = 0 if self._block_style else -1 + write((self._indent_per_level + indent_adjust) * ' ') delimnl = ',\n' + ' ' * indent delim = '' width = max_width = self._width - indent + 1 @@ -491,8 +521,11 @@ def _pprint_default_dict(self, object, stream, indent, allowance, context, level return rdf = self._repr(object.default_factory, context, level) cls = object.__class__ - indent += len(cls.__name__) + 1 - stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent)) + indent += len(cls.__name__) + 1 if not self._block_style else 0 + if self._block_style: + stream.write('%s(%s, ' % (cls.__name__, rdf)) + else: + stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent)) self._pprint_dict(object, stream, indent, allowance + 1, context, level) stream.write(')') @@ -503,14 +536,15 @@ def _pprint_counter(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '({') + stream.write(self._format_block_start(cls.__name__ + '({', indent)) if self._indent_per_level > 1: stream.write((self._indent_per_level - 1) * ' ') items = object.most_common() + recursive_indent = indent + len(cls.__name__) + 1 if not self._block_style else indent self._format_dict_items(items, stream, - indent + len(cls.__name__) + 1, allowance + 2, + recursive_indent, allowance + 2, context, level) - stream.write('})') + stream.write(self._format_block_end('})', indent)) _dispatch[_collections.Counter.__repr__] = _pprint_counter @@ -519,12 +553,12 @@ def _pprint_chain_map(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '(') - indent += len(cls.__name__) + 1 + stream.write(self._format_block_start(cls.__name__ + '(', indent + self._indent_per_level)) + indent += len(cls.__name__) + 1 if not self._block_style else self._indent_per_level for i, m in enumerate(object.maps): if i == len(object.maps) - 1: self._format(m, stream, indent, allowance + 1, context, level) - stream.write(')') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) else: self._format(m, stream, indent, 1, context, level) stream.write(',\n' + ' ' * indent) @@ -536,18 +570,20 @@ def _pprint_deque(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '(') - indent += len(cls.__name__) + 1 - stream.write('[') + stream.write(self._format_block_start(cls.__name__ + '([', indent)) + indent += len(cls.__name__) + 1 if not self._block_style else 0 if object.maxlen is None: self._format_items(object, stream, indent, allowance + 2, context, level) - stream.write('])') + stream.write(self._format_block_end('])', indent)) else: self._format_items(object, stream, indent, 2, context, level) rml = self._repr(object.maxlen, context, level) - stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) + if self._block_style: + stream.write('%s], maxlen=%s)' % ('\n' + ' ' * indent, rml)) + else: + stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) _dispatch[_collections.deque.__repr__] = _pprint_deque From f5fcbfcad634c54e4f34c8c72643524fdd2f4e1c Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 6 Feb 2025 15:35:38 -0800 Subject: [PATCH 02/31] Small bug fix for indent=1, added to unit test --- Doc/library/pprint.rst | 104 +++++++++++++- Lib/pprint.py | 23 +-- Lib/test/test_pprint.py | 307 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+), 14 deletions(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2985f31bacb47a..1c9503ed54b802 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -36,7 +36,7 @@ Functions --------- .. function:: pp(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=False, underscore_numbers=False) + compact=False, sort_dicts=False, underscore_numbers=False, block_style=False) Prints the formatted representation of *object*, followed by a newline. This function may be used in the interactive interpreter @@ -85,6 +85,12 @@ Functions integers will be formatted with the ``_`` character for a thousands separator, otherwise underscores are not displayed (the default). + :param bool block_style: + If ``True``, + opening parentheses and brackets will be followed by a newline and the + following content will be indented by one level, similar to block style + JSON formatting. This option is not compatible with *compact*. + >>> import pprint >>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni'] >>> stuff.insert(0, stuff) @@ -100,7 +106,7 @@ Functions .. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) Alias for :func:`~pprint.pp` with *sort_dicts* set to ``True`` by default, which would automatically sort the dictionaries' keys, @@ -108,10 +114,10 @@ Functions .. function:: pformat(object, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) Return the formatted representation of *object* as a string. *indent*, - *width*, *depth*, *compact*, *sort_dicts* and *underscore_numbers* are + *width*, *depth*, *compact*, *sort_dicts*, *underscore_numbers* and *block_style* are passed to the :class:`PrettyPrinter` constructor as formatting parameters and their meanings are as described in the documentation above. @@ -155,7 +161,7 @@ PrettyPrinter Objects .. index:: single: ...; placeholder .. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) Construct a :class:`PrettyPrinter` instance. @@ -179,6 +185,22 @@ PrettyPrinter Objects 'knights', 'ni'], 'spam', 'eggs', 'lumberjack', 'knights', 'ni'] + >>> pp = pprint.PrettyPrinter(width=41, block_style=True, indent=3) + >>> pp.pprint(stuff) + [ + [ + 'spam', + 'eggs', + 'lumberjack', + 'knights', + 'ni' + ], + 'spam', + 'eggs', + 'lumberjack', + 'knights', + 'ni' + ] >>> tup = ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', ... ('parrot', ('fresh fruit',)))))))) >>> pp = pprint.PrettyPrinter(depth=6) @@ -198,6 +220,9 @@ PrettyPrinter Objects .. versionchanged:: 3.11 No longer attempts to write to :data:`!sys.stdout` if it is ``None``. + .. versionchanged:: 3.13 + Added the *block_style* parameter. + :class:`PrettyPrinter` instances have the following methods: @@ -420,3 +445,72 @@ cannot be split, the specified width will be exceeded:: 'requires_python': None, 'summary': 'A sample Python project', 'version': '1.2.0'} + +Lastly, we can achieve block style formatting with the *block_style* parameter. Best results +are achieved with a higher *indent* value:: + + >>> pprint.pp(project_info, indent=4, block_style=True) + { + 'author': 'The Python Packaging Authority', + 'author_email': 'pypa-dev@googlegroups.com', + 'bugtrack_url': None, + 'classifiers': [ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Topic :: Software Development :: Build Tools' + ], + 'description': 'A sample Python project\n' + '=======================\n' + '\n' + 'This is the description file for the project.\n' + '\n' + 'The file should use UTF-8 encoding and be written using ReStructured ' + 'Text. It\n' + 'will be used to generate the project webpage on PyPI, and should be ' + 'written for\n' + 'that purpose.\n' + '\n' + 'Typical contents for this file would include an overview of the project, ' + 'basic\n' + 'usage examples, etc. Generally, including the project changelog in here ' + 'is not\n' + 'a good idea, although a simple "What\'s New" section for the most recent ' + 'version\n' + 'may be appropriate.', + 'description_content_type': None, + 'docs_url': None, + 'download_url': 'UNKNOWN', + 'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1}, + 'dynamic': None, + 'home_page': 'https://github.com/pypa/sampleproject', + 'keywords': 'sample setuptools development', + 'license': 'MIT', + 'license_expression': None, + 'license_files': None, + 'maintainer': None, + 'maintainer_email': None, + 'name': 'sampleproject', + 'package_url': 'https://pypi.org/project/sampleproject/', + 'platform': 'UNKNOWN', + 'project_url': 'https://pypi.org/project/sampleproject/', + 'project_urls': { + 'Download': 'UNKNOWN', + 'Homepage': 'https://github.com/pypa/sampleproject' + }, + 'provides_extra': None, + 'release_url': 'https://pypi.org/project/sampleproject/1.2.0/', + 'requires_dist': None, + 'requires_python': None, + 'summary': 'A sample Python project', + 'version': '1.2.0', + 'yanked': False, + 'yanked_reason': None + } diff --git a/Lib/pprint.py b/Lib/pprint.py index 1d1c207bf1ca54..251973f99785e7 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -218,7 +218,7 @@ def _format_block_start(self, start_str, indent): return start_str +'\n' + ' ' * indent else: return start_str - + def _format_block_end(self, end_str, indent): if self._block_style: return '\n' + ' ' * indent + end_str @@ -243,9 +243,10 @@ def _pprint_dataclass(self, object, stream, indent, allowance, context, level): def _pprint_dict(self, object, stream, indent, allowance, context, level): write = stream.write write(self._format_block_start('{', indent)) - if self._indent_per_level > 1: - indent_adjust = 0 if self._block_style else -1 - write((self._indent_per_level + indent_adjust) * ' ') + if self._indent_per_level > 1 and not self._block_style: + write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + write(self._indent_per_level * ' ') length = len(object) if length: if self._sort_dicts: @@ -387,7 +388,8 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): def _pprint_bytearray(self, object, stream, indent, allowance, context, level): write = stream.write write(self._format_block_start('bytearray(', indent)) - write(' ' * self._indent_per_level) + if self._block_style: + write(' ' * self._indent_per_level) recursive_indent = indent + 10 if not self._block_style else indent + self._indent_per_level self._pprint_bytes(bytes(object), stream, recursive_indent, allowance + 1, context, level + 1) @@ -460,9 +462,10 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev def _format_items(self, items, stream, indent, allowance, context, level): write = stream.write indent += self._indent_per_level - if self._indent_per_level > 1: - indent_adjust = 0 if self._block_style else -1 - write((self._indent_per_level + indent_adjust) * ' ') + if self._indent_per_level > 1 and not self._block_style: + write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + write(self._indent_per_level * ' ') delimnl = ',\n' + ' ' * indent delim = '' width = max_width = self._width - indent + 1 @@ -537,8 +540,10 @@ def _pprint_counter(self, object, stream, indent, allowance, context, level): return cls = object.__class__ stream.write(self._format_block_start(cls.__name__ + '({', indent)) - if self._indent_per_level > 1: + if self._indent_per_level > 1 and not self._block_style: stream.write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + stream.write(self._indent_per_level * ' ') items = object.most_common() recursive_indent = indent + len(cls.__name__) + 1 if not self._block_style else indent self._format_dict_items(items, stream, diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index dfbc2a06e7346f..e641badc6039e6 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -10,6 +10,15 @@ import re import types import unittest +from collections import OrderedDict +from collections import defaultdict +from collections import Counter +from collections import ChainMap +from collections import deque +from collections import UserDict, UserList +from dataclasses import dataclass, field +from types import SimpleNamespace, MappingProxyType +from typing import Optional # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): @@ -141,6 +150,7 @@ def test_init(self): self.assertRaises(ValueError, pprint.PrettyPrinter, depth=0) self.assertRaises(ValueError, pprint.PrettyPrinter, depth=-1) self.assertRaises(ValueError, pprint.PrettyPrinter, width=0) + self.assertRaises(ValueError, pprint.PrettyPrinter, compact=True, block_style=True) def test_basic(self): # Verify .isrecursive() and .isreadable() w/o recursion @@ -1123,6 +1133,303 @@ def test_user_string(self): 'jumped over a ' 'lazy dog'}""") + def test_block_style(self): + @dataclass + class DummyDataclass: + foo: str + bar: float + baz: bool + qux: dict = field(default_factory=dict) + quux: list = field(default_factory=list) + corge: int = 1 + grault: set = field(default_factory=set) + garply: tuple = (1, 2, 3, 4) + waldo: Optional["DummyDataclass"] = None + dummy_dataclass = DummyDataclass( + foo="foo", + bar=1.2, + baz=False, + qux={"foo": "bar", "baz": 123}, + quux=["foo", "bar", "baz"], + corge=7, + grault={"foo", "bar", "baz", "baz"}, + garply=(1, 2, 3, 4), + waldo=None, + ) + + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + + dummy_ordered_dict = OrderedDict( + [ + ("foo", 1), + ("bar", 12), + ("baz", 123), + ] + ) + + dummy_list = [ + "foo", + "bar", + "baz", + ] + + dummy_tuple = ( + "foo", + "bar", + "baz", + 4, + 5, + 6, + dummy_list, + ) + + dummy_set = { + "foo", + "bar", + "baz", + (1, 2, 3), + } + + dummy_frozenset = frozenset( + { + "foo", + "bar", + "baz", + (1, 2, 3), + frozenset(dummy_set), + } + ) + + dummy_bytes = b"Hello world! foo bar baz 123 456 789" + dummy_byte_array = bytearray(dummy_bytes) + + dummy_mappingproxy = MappingProxyType(dummy_dict) + + dummy_namespace = SimpleNamespace( + foo="bar", + bar=42, + baz=SimpleNamespace( + x=321, + y="string", + d={"foo": True, "bar": "baz"}, + ), + ) + + dummy_defaultdict = defaultdict(list) + dummy_defaultdict["foo"].append("bar") + dummy_defaultdict["foo"].append("baz") + dummy_defaultdict["foo"].append("qux") + dummy_defaultdict["bar"] = {"foo": "bar", "baz": None} + + dummy_counter = Counter() + dummy_counter.update("foo") + dummy_counter.update( + { + "bar": 5, + "baz": 3, + "qux": 10, + } + ) + + dummy_chainmap = ChainMap( + {"foo": "bar"}, + {"baz": "qux"}, + {"corge": dummy_dict}, + ) + dummy_chainmap.maps.append({"garply": "waldo"}) + + dummy_deque = deque(maxlen=10) + dummy_deque.append("foo") + dummy_deque.append(123) + dummy_deque.append(dummy_dict) + dummy_deque.extend(dummy_list) + dummy_deque.appendleft(dummy_set) + + class DummyUserDict(UserDict): + """A custom UserDict with some extra attributes""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.access_count = 0 + dummy_userdict = DummyUserDict( + { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + ) + dummy_userdict.access_count = 5 + + class DummyUserList(UserList): + """A custom UserList with some extra attributes""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.description = "foo" + dummy_userlist = DummyUserList(["first", 2, {"key": "value"}, [4, 5, 6]]) + + dummy_samples = { + "dummy_dataclass": dummy_dataclass, + "dummy_dict": dummy_dict, + "dummy_ordered_dict": dummy_ordered_dict, + "dummy_list": dummy_list, + "dummy_tuple": dummy_tuple, + "dummy_set": dummy_set, + "dummy_frozenset": dummy_frozenset, + "dummy_bytes": dummy_bytes, + "dummy_byte_array": dummy_byte_array, + "dummy_mappingproxy": dummy_mappingproxy, + "dummy_namespace": dummy_namespace, + "dummy_defaultdict": dummy_defaultdict, + "dummy_counter": dummy_counter, + "dummy_chainmap": dummy_chainmap, + "dummy_deque": dummy_deque, + "dummy_userdict": dummy_userdict, + "dummy_userlist": dummy_userlist, + } + self.assertEqual(pprint.pformat(dummy_samples, width=40, indent=4, block_style=True, sort_dicts=False), +"""\ +{ + 'dummy_dataclass': DummyDataclass( + foo='foo', + bar=1.2, + baz=False, + qux={'foo': 'bar', 'baz': 123}, + quux=['foo', 'bar', 'baz'], + corge=7, + grault={'baz', 'foo', 'bar'}, + garply=(1, 2, 3, 4), + waldo=None + + ), + 'dummy_dict': { + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 + }, + 'dummy_ordered_dict': OrderedDict([ + ('foo', 1), + ('bar', 12), + ('baz', 123) + ]), + 'dummy_list': ['foo', 'bar', 'baz'], + 'dummy_tuple': ( + 'foo', + 'bar', + 'baz', + 4, + 5, + 6, + ['foo', 'bar', 'baz'] + ), + 'dummy_set': {'baz', (1, 2, 3), 'foo', 'bar'}, + 'dummy_frozenset': frozenset({ + frozenset({ + 'bar', + 'baz', + 'foo', + (1, 2, 3) + }), + 'bar', + 'baz', + 'foo', + (1, 2, 3) + }), + 'dummy_bytes': b'Hello world! foo bar baz 123 456' + b' 789', + 'dummy_byte_array': bytearray( + b'Hello world! foo bar baz 123' + b' 456 789' + ), + 'dummy_mappingproxy': mappingproxy({ + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 + }), + 'dummy_namespace': namespace( + foo='bar', + bar=42, + baz=namespace( + x=321, + y='string', + d={'foo': True, 'bar': 'baz'} + ) + ), + 'dummy_defaultdict': defaultdict(, { + 'foo': ['bar', 'baz', 'qux'], + 'bar': {'foo': 'bar', 'baz': None} + }), + 'dummy_counter': Counter({ + 'qux': 10, + 'bar': 5, + 'baz': 3, + 'o': 2, + 'f': 1 + }), + 'dummy_chainmap': ChainMap( + {'foo': 'bar'}, + {'baz': 'qux'}, + { + 'corge': { + 'foo': 'bar', + 'baz': 123, + 'qux': { + 'foo': 'bar', + 'baz': 123 + }, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 + } + }, + {'garply': 'waldo'} + ), + 'dummy_deque': deque([ + { + 'bar', + 'baz', + 'foo', + (1, 2, 3) + }, + 'foo', + 123, + { + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 + }, + 'foo', + 'bar', + 'baz' + ], maxlen=10), + 'dummy_userdict': { + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 + }, + 'dummy_userlist': [ + 'first', + 2, + {'key': 'value'}, + [4, 5, 6] + ] +}""") + class DottedPrettyPrinter(pprint.PrettyPrinter): From a92ef3d623328b7531286a866713ac39220e5028 Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 6 Feb 2025 16:13:16 -0800 Subject: [PATCH 03/31] Remove set from block style unit test (since order kept changing) --- Lib/test/test_pprint.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index e641badc6039e6..6d972f3f03dede 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1142,7 +1142,6 @@ class DummyDataclass: qux: dict = field(default_factory=dict) quux: list = field(default_factory=list) corge: int = 1 - grault: set = field(default_factory=set) garply: tuple = (1, 2, 3, 4) waldo: Optional["DummyDataclass"] = None dummy_dataclass = DummyDataclass( @@ -1152,7 +1151,6 @@ class DummyDataclass: qux={"foo": "bar", "baz": 123}, quux=["foo", "bar", "baz"], corge=7, - grault={"foo", "bar", "baz", "baz"}, garply=(1, 2, 3, 4), waldo=None, ) @@ -1190,9 +1188,6 @@ class DummyDataclass: ) dummy_set = { - "foo", - "bar", - "baz", (1, 2, 3), } @@ -1305,7 +1300,6 @@ def __init__(self, *args, **kwargs): qux={'foo': 'bar', 'baz': 123}, quux=['foo', 'bar', 'baz'], corge=7, - grault={'baz', 'foo', 'bar'}, garply=(1, 2, 3, 4), waldo=None @@ -1332,14 +1326,9 @@ def __init__(self, *args, **kwargs): 6, ['foo', 'bar', 'baz'] ), - 'dummy_set': {'baz', (1, 2, 3), 'foo', 'bar'}, + 'dummy_set': {(1, 2, 3)}, 'dummy_frozenset': frozenset({ - frozenset({ - 'bar', - 'baz', - 'foo', - (1, 2, 3) - }), + frozenset({(1, 2, 3)}), 'bar', 'baz', 'foo', @@ -1396,12 +1385,7 @@ def __init__(self, *args, **kwargs): {'garply': 'waldo'} ), 'dummy_deque': deque([ - { - 'bar', - 'baz', - 'foo', - (1, 2, 3) - }, + {(1, 2, 3)}, 'foo', 123, { From d9a147f20ae8181d8cae96637fde9d71f2bad71c Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 00:48:09 +0000 Subject: [PATCH 04/31] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst diff --git a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst new file mode 100644 index 00000000000000..8978e361ecfafd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst @@ -0,0 +1 @@ +Adds a `block_style` boolean parameter to :func:`pprint.pp` which inserts a newline after opening parentheses and braces of printed objects and indents their contents. In other words, enabling this option results in formatted output looking similar to the block style formatting from :func:`json.dumps` when `indent` is set to a positive integer. Contributed by Stefan Todoran. From 19e4e082f8aea241450ff522c6ed442914db8bdf Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 6 Feb 2025 16:55:27 -0800 Subject: [PATCH 05/31] Fix params in news entry --- .../next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst index 8978e361ecfafd..79b80c977da4cf 100644 --- a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst +++ b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst @@ -1 +1 @@ -Adds a `block_style` boolean parameter to :func:`pprint.pp` which inserts a newline after opening parentheses and braces of printed objects and indents their contents. In other words, enabling this option results in formatted output looking similar to the block style formatting from :func:`json.dumps` when `indent` is set to a positive integer. Contributed by Stefan Todoran. +Adds a ``block_style`` boolean parameter to :func:`pprint.pp` which inserts a newline after opening parentheses and braces of printed objects and indents their contents. In other words, enabling this option results in formatted output looking similar to the block style formatting from :func:`json.dumps` when ``indent`` is set to a positive integer. Contributed by Stefan Todoran. From 8ef085f53271e1b21f16dc460f7ca21364708deb Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 6 Feb 2025 17:06:04 -0800 Subject: [PATCH 06/31] Fix line length for news entry --- .../Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst index 79b80c977da4cf..35e06f47548ef6 100644 --- a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst +++ b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst @@ -1 +1,5 @@ -Adds a ``block_style`` boolean parameter to :func:`pprint.pp` which inserts a newline after opening parentheses and braces of printed objects and indents their contents. In other words, enabling this option results in formatted output looking similar to the block style formatting from :func:`json.dumps` when ``indent`` is set to a positive integer. Contributed by Stefan Todoran. +Add a ``block_style`` boolean parameter to :func:`pprint.pp` which inserts a +newline after opening parentheses/braces of printed objects and indents their +contents. In other words, enabling this option results in formatted output +similar to the block style formatting from :func:`json.dumps` when ``indent`` +is set to a positive integer. Contributed by Stefan Todoran. From c446aa1f6d1c7f0b1d3819905c6355cb7d451bc9 Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 6 Feb 2025 17:58:19 -0800 Subject: [PATCH 07/31] Shorten news entry --- .../2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst index 35e06f47548ef6..b165ce4827cf82 100644 --- a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst +++ b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst @@ -1,5 +1,3 @@ -Add a ``block_style`` boolean parameter to :func:`pprint.pp` which inserts a -newline after opening parentheses/braces of printed objects and indents their -contents. In other words, enabling this option results in formatted output -similar to the block style formatting from :func:`json.dumps` when ``indent`` -is set to a positive integer. Contributed by Stefan Todoran. +Add a *block_style* keyword argument for :func:`pprint.pprint`, +:func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and +:class:`pprint.PrettyPrinter`. Contributed by Stefan Todoran. From 49b11084a8d87493a5bca33bc7f56a42f8278112 Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Wed, 19 Feb 2025 10:23:06 -0800 Subject: [PATCH 08/31] Address documentation related PR comments --- Doc/library/pprint.rst | 2 +- Doc/whatsnew/3.14.rst | 9 +++++++++ Lib/test/test_pprint.py | 3 +-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 1c9503ed54b802..bff1c8b5cab832 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -220,7 +220,7 @@ PrettyPrinter Objects .. versionchanged:: 3.11 No longer attempts to write to :data:`!sys.stdout` if it is ``None``. - .. versionchanged:: 3.13 + .. versionchanged:: next Added the *block_style* parameter. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 23b1f5f2e0c67c..38d0ff9b2fc028 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -673,6 +673,15 @@ platform (Contributed by Bénédikt Tran in :gh:`122549`.) +pprint +----- + +* Add a *block_style* keyword argument for :func:`pprint.pprint`, + :func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and + :class:`pprint.PrettyPrinter`. + (Contributed by Stefan Todoran in :gh:`129274`.) + + pydoc ----- diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 6d972f3f03dede..5dec81b9b6bdf5 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -18,7 +18,6 @@ from collections import UserDict, UserList from dataclasses import dataclass, field from types import SimpleNamespace, MappingProxyType -from typing import Optional # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): @@ -1143,7 +1142,7 @@ class DummyDataclass: quux: list = field(default_factory=list) corge: int = 1 garply: tuple = (1, 2, 3, 4) - waldo: Optional["DummyDataclass"] = None + waldo: "DummyDataclass" | None = None dummy_dataclass = DummyDataclass( foo="foo", bar=1.2, From 0b8fd2b90552068f18169a1d843dfd848ef9b47a Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Wed, 19 Feb 2025 10:36:15 -0800 Subject: [PATCH 09/31] Fix length of title underline :/ --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 1d52f10a8805e8..b724f79c26937f 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -729,7 +729,7 @@ platform pprint ------ +------ * Add a *block_style* keyword argument for :func:`pprint.pprint`, :func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and From 5f08960d4aac038cdec99be3ddb924c29f29e717 Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Wed, 19 Feb 2025 10:49:56 -0800 Subject: [PATCH 10/31] Fix test case, remove unnecessary recursion on dataclass (we aren't testing that in the particular unit test) --- Lib/pprint.py | 2 -- Lib/test/test_pprint.py | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 251973f99785e7..e01bc4d2db5aa5 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -234,8 +234,6 @@ def _pprint_dataclass(self, object, stream, indent, allowance, context, level): items = [(f.name, getattr(object, f.name)) for f in dataclass_fields(object) if f.repr] stream.write(self._format_block_start(cls_name + '(', indent)) self._format_namespace_items(items, stream, indent, allowance, context, level) - if self._block_style: - stream.write('\n') stream.write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch = {} diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 5dec81b9b6bdf5..8c17e35e9a9630 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1142,7 +1142,6 @@ class DummyDataclass: quux: list = field(default_factory=list) corge: int = 1 garply: tuple = (1, 2, 3, 4) - waldo: "DummyDataclass" | None = None dummy_dataclass = DummyDataclass( foo="foo", bar=1.2, @@ -1151,7 +1150,6 @@ class DummyDataclass: quux=["foo", "bar", "baz"], corge=7, garply=(1, 2, 3, 4), - waldo=None, ) dummy_dict = { @@ -1299,9 +1297,7 @@ def __init__(self, *args, **kwargs): qux={'foo': 'bar', 'baz': 123}, quux=['foo', 'bar', 'baz'], corge=7, - garply=(1, 2, 3, 4), - waldo=None - + garply=(1, 2, 3, 4) ), 'dummy_dict': { 'foo': 'bar', From 59ed5fd7acbddc02a6e3a88e98bf70feefee0cea Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:45:06 -0800 Subject: [PATCH 11/31] Update Doc/library/pprint.rst Co-authored-by: donBarbos --- Doc/library/pprint.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index bff1c8b5cab832..e6854defc49ec2 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -36,7 +36,8 @@ Functions --------- .. function:: pp(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=False, underscore_numbers=False, block_style=False) + compact=False, sort_dicts=False, underscore_numbers=False, \ + block_style=False) Prints the formatted representation of *object*, followed by a newline. This function may be used in the interactive interpreter From cbc392d49576efb191d5a7f9263d5bd250d0c95f Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:45:43 -0800 Subject: [PATCH 12/31] Update Doc/library/pprint.rst Co-authored-by: donBarbos --- Doc/library/pprint.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index e6854defc49ec2..af2025c8cd09a7 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -107,7 +107,8 @@ Functions .. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=False) Alias for :func:`~pprint.pp` with *sort_dicts* set to ``True`` by default, which would automatically sort the dictionaries' keys, From d1af62c4d5d82f3a92ecea97149c2f457e849a9c Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:45:59 -0800 Subject: [PATCH 13/31] Update Doc/library/pprint.rst Co-authored-by: donBarbos --- Doc/library/pprint.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index af2025c8cd09a7..a22e710a89a3a8 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -116,7 +116,8 @@ Functions .. function:: pformat(object, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=False) Return the formatted representation of *object* as a string. *indent*, *width*, *depth*, *compact*, *sort_dicts*, *underscore_numbers* and *block_style* are From 283ed3834ed09f5eddd7aa8ff64d0068aeadb1ca Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:46:19 -0800 Subject: [PATCH 14/31] Update Doc/library/pprint.rst Co-authored-by: donBarbos --- Doc/library/pprint.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index a22e710a89a3a8..5e1a411b71cf4d 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -164,7 +164,8 @@ PrettyPrinter Objects .. index:: single: ...; placeholder .. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=False) Construct a :class:`PrettyPrinter` instance. From 142b92b9d9a8d71252fb31477155d0c782f194a4 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:47:10 -0800 Subject: [PATCH 15/31] Update Doc/library/pprint.rst Co-authored-by: donBarbos --- Doc/library/pprint.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 5e1a411b71cf4d..918b9c01b4e3f1 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -450,8 +450,8 @@ cannot be split, the specified width will be exceeded:: 'summary': 'A sample Python project', 'version': '1.2.0'} -Lastly, we can achieve block style formatting with the *block_style* parameter. Best results -are achieved with a higher *indent* value:: +Lastly, we can achieve block style formatting with the *block_style* parameter. +Best results are achieved with a higher *indent* value:: >>> pprint.pp(project_info, indent=4, block_style=True) { From 2273be297bc1781b9dc90d0f7f8ecb0fd0822ea8 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:47:37 -0800 Subject: [PATCH 16/31] Update Doc/whatsnew/3.14.rst Co-authored-by: donBarbos --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 79bfe541dd1c33..b3d4dc4730b084 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -800,7 +800,7 @@ pprint * Add a *block_style* keyword argument for :func:`pprint.pprint`, :func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and :class:`pprint.PrettyPrinter`. - (Contributed by Stefan Todoran in :gh:`129274`.) + (Contributed by Stefan Todoran in :gh:`112632`.) pydoc From d8b594275eddacffdc1ad7c792d989777d2cf9b9 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:47:55 -0800 Subject: [PATCH 17/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index e01bc4d2db5aa5..6b49a7010ea6f2 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -44,7 +44,8 @@ def pprint(object, stream=None, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=False): """Pretty-print a Python object to a stream [default is sys.stdout].""" printer = PrettyPrinter( stream=stream, indent=indent, width=width, depth=depth, From 8c3ed5c4d9a744c21392d6bf86d2e200e53c815d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:48:17 -0800 Subject: [PATCH 18/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 6b49a7010ea6f2..67d30325b7122d 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -55,7 +55,8 @@ def pprint(object, stream=None, indent=1, width=80, depth=None, *, def pformat(object, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=False): """Format a Python object into a pretty-printed representation.""" return PrettyPrinter(indent=indent, width=width, depth=depth, compact=compact, sort_dicts=sort_dicts, From 9c7afd5bd52449f9e3abff2511204c92b43b651d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:48:37 -0800 Subject: [PATCH 19/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 67d30325b7122d..d443b1c2d9e091 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -60,7 +60,8 @@ def pformat(object, indent=1, width=80, depth=None, *, """Format a Python object into a pretty-printed representation.""" return PrettyPrinter(indent=indent, width=width, depth=depth, compact=compact, sort_dicts=sort_dicts, - underscore_numbers=underscore_numbers, block_style=block_style).pformat(object) + underscore_numbers=underscore_numbers, + block_style=block_style).pformat(object) def pp(object, *args, sort_dicts=False, **kwargs): From 4e7fbfffa9f2c153e0e243950975e400392bcf1d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:48:52 -0800 Subject: [PATCH 20/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index d443b1c2d9e091..7c77ef67a27d3b 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -114,7 +114,8 @@ def _safe_tuple(t): class PrettyPrinter: def __init__(self, indent=1, width=80, depth=None, stream=None, *, - compact=False, sort_dicts=True, underscore_numbers=False, block_style=False): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=False): """Handle pretty printing operations onto a stream using a set of configured parameters. From 5abab858660b040269b1a35bc9e45b74c84a5283 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:49:20 -0800 Subject: [PATCH 21/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 7c77ef67a27d3b..9b748ee114213f 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -219,7 +219,7 @@ def _format(self, object, stream, indent, allowance, context, level): def _format_block_start(self, start_str, indent): if self._block_style: - return start_str +'\n' + ' ' * indent + return f"{start_str}\n{' ' * indent}" else: return start_str From 5e8a624d52e190c22f3dd86fe802e7701ec2beec Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:49:39 -0800 Subject: [PATCH 22/31] Update Lib/pprint.py Co-authored-by: donBarbos --- Lib/pprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pprint.py b/Lib/pprint.py index 9b748ee114213f..a3503939260300 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -225,7 +225,7 @@ def _format_block_start(self, start_str, indent): def _format_block_end(self, end_str, indent): if self._block_style: - return '\n' + ' ' * indent + end_str + return f"\n{' ' * indent}{end_str}" else: return end_str From 5ffbd0db68e487853326c67e60852d62ecc48df8 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:49:58 -0800 Subject: [PATCH 23/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 8c17e35e9a9630..ccda286287715e 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -10,14 +10,6 @@ import re import types import unittest -from collections import OrderedDict -from collections import defaultdict -from collections import Counter -from collections import ChainMap -from collections import deque -from collections import UserDict, UserList -from dataclasses import dataclass, field -from types import SimpleNamespace, MappingProxyType # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): From fa4cdacb11e10e88f6f5f7097e619b11787c79b9 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:50:13 -0800 Subject: [PATCH 24/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index ccda286287715e..b20efe6d700f14 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1125,7 +1125,7 @@ def test_user_string(self): 'lazy dog'}""") def test_block_style(self): - @dataclass + @dataclasses.dataclass class DummyDataclass: foo: str bar: float From 6bffc7eb18560ad410d43c5ce753e7ee1ee9362d Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:50:31 -0800 Subject: [PATCH 25/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index b20efe6d700f14..f0ac16d1dbdd38 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1130,8 +1130,8 @@ class DummyDataclass: foo: str bar: float baz: bool - qux: dict = field(default_factory=dict) - quux: list = field(default_factory=list) + qux: dict = dataclasses.field(default_factory=dict) + quux: list = dataclasses.field(default_factory=list) corge: int = 1 garply: tuple = (1, 2, 3, 4) dummy_dataclass = DummyDataclass( From c8ce4ff5ea46f3821ae1c3da9252959f8996dd7e Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:50:49 -0800 Subject: [PATCH 26/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 112 +++++++++------------------------------- 1 file changed, 25 insertions(+), 87 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index f0ac16d1dbdd38..9b17fbb6e1fe93 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1134,108 +1134,46 @@ class DummyDataclass: quux: list = dataclasses.field(default_factory=list) corge: int = 1 garply: tuple = (1, 2, 3, 4) - dummy_dataclass = DummyDataclass( - foo="foo", - bar=1.2, - baz=False, - qux={"foo": "bar", "baz": 123}, - quux=["foo", "bar", "baz"], - corge=7, - garply=(1, 2, 3, 4), - ) - - dummy_dict = { - "foo": "bar", - "baz": 123, - "qux": {"foo": "bar", "baz": 123}, - "quux": ["foo", "bar", "baz"], - "corge": 7, - } - - dummy_ordered_dict = OrderedDict( - [ - ("foo", 1), - ("bar", 12), - ("baz", 123), - ] - ) - - dummy_list = [ - "foo", - "bar", - "baz", - ] - - dummy_tuple = ( - "foo", - "bar", - "baz", - 4, - 5, - 6, - dummy_list, - ) - - dummy_set = { - (1, 2, 3), - } - - dummy_frozenset = frozenset( - { - "foo", - "bar", - "baz", - (1, 2, 3), - frozenset(dummy_set), - } - ) + dummy_dataclass = DummyDataclass(foo="foo", bar=1.2, baz=False, + qux={"foo": "bar", "baz": 123}, + quux=["foo", "bar", "baz"], corge=7, + garply=(1, 2, 3, 4)) + dummy_dict = {"foo": "bar", "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], "corge": 7 } + dummy_ordered_dict = collections.OrderedDict([("foo", 1), ("bar", 12), + ("baz", 123)]) + dummy_list = ["foo", "bar", "baz"] + dummy_tuple = ("foo", "bar", "baz", 4, 5, 6, dummy_list) + dummy_set = { 1, 2, 3 } + dummy_frozenset = frozenset(("foo", "bar", "baz", (1, 2, 3), + frozenset(dummy_set))) dummy_bytes = b"Hello world! foo bar baz 123 456 789" dummy_byte_array = bytearray(dummy_bytes) - - dummy_mappingproxy = MappingProxyType(dummy_dict) - - dummy_namespace = SimpleNamespace( - foo="bar", - bar=42, - baz=SimpleNamespace( - x=321, - y="string", - d={"foo": True, "bar": "baz"}, - ), - ) - - dummy_defaultdict = defaultdict(list) + dummy_mappingproxy = types.MappingProxyType(dummy_dict) + baz = types.SimpleNamespace(x=321, y="string", + d={"foo": True, "bar": "baz"}) + dummy_namespace = types.SimpleNamespace(foo="bar", bar=42, baz=baz) + dummy_defaultdict = collections.defaultdict(list) dummy_defaultdict["foo"].append("bar") dummy_defaultdict["foo"].append("baz") dummy_defaultdict["foo"].append("qux") dummy_defaultdict["bar"] = {"foo": "bar", "baz": None} - - dummy_counter = Counter() + dummy_counter = collections.Counter() dummy_counter.update("foo") - dummy_counter.update( - { - "bar": 5, - "baz": 3, - "qux": 10, - } - ) - - dummy_chainmap = ChainMap( - {"foo": "bar"}, - {"baz": "qux"}, - {"corge": dummy_dict}, - ) + dummy_counter.update({ "bar": 5, "baz": 3, "qux": 10 }) + dummy_chainmap = collections.ChainMap({"foo": "bar"}, {"baz": "qux"}, + {"corge": dummy_dict},) dummy_chainmap.maps.append({"garply": "waldo"}) - - dummy_deque = deque(maxlen=10) + dummy_deque = collections.deque(maxlen=10) dummy_deque.append("foo") dummy_deque.append(123) dummy_deque.append(dummy_dict) dummy_deque.extend(dummy_list) dummy_deque.appendleft(dummy_set) - class DummyUserDict(UserDict): + class DummyUserDict(collections.UserDict): """A custom UserDict with some extra attributes""" def __init__(self, *args, **kwargs): From 879f1dae73f4107fd27ccd03c3c4199b5117d255 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:51:01 -0800 Subject: [PATCH 27/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 9b17fbb6e1fe93..d60c68342d0764 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1179,15 +1179,10 @@ class DummyUserDict(collections.UserDict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.access_count = 0 - dummy_userdict = DummyUserDict( - { - "foo": "bar", - "baz": 123, - "qux": {"foo": "bar", "baz": 123}, - "quux": ["foo", "bar", "baz"], - "corge": 7, - } - ) + dummy_userdict = DummyUserDict({ "foo": "bar", "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7 }) dummy_userdict.access_count = 5 class DummyUserList(UserList): From 21c9fa699f381b30e53544130e990c358fa21ed0 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:51:12 -0800 Subject: [PATCH 28/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index d60c68342d0764..8e8e5569649452 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1185,7 +1185,7 @@ def __init__(self, *args, **kwargs): "corge": 7 }) dummy_userdict.access_count = 5 - class DummyUserList(UserList): + class DummyUserList(collections.UserList): """A custom UserList with some extra attributes""" def __init__(self, *args, **kwargs): From 815e1da768c25a3c983fbe5517f61335fdefe576 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 08:51:22 -0800 Subject: [PATCH 29/31] Update Lib/test/test_pprint.py Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 8e8e5569649452..0c046aab1848e1 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1191,7 +1191,8 @@ class DummyUserList(collections.UserList): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.description = "foo" - dummy_userlist = DummyUserList(["first", 2, {"key": "value"}, [4, 5, 6]]) + dummy_userlist = DummyUserList(["first", 2, {"key": "value"}, + [4, 5, 6]]) dummy_samples = { "dummy_dataclass": dummy_dataclass, From a7c26dd6baac22366b1331f4c818913cc56fb174 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 25 Feb 2025 15:06:27 -0800 Subject: [PATCH 30/31] Apply suggestions from code review Co-authored-by: donBarbos --- Lib/test/test_pprint.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 0c046aab1848e1..504a8ec4b2db88 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1146,7 +1146,7 @@ class DummyDataclass: ("baz", 123)]) dummy_list = ["foo", "bar", "baz"] dummy_tuple = ("foo", "bar", "baz", 4, 5, 6, dummy_list) - dummy_set = { 1, 2, 3 } + dummy_set = { (1, 2, 3) } dummy_frozenset = frozenset(("foo", "bar", "baz", (1, 2, 3), frozenset(dummy_set))) dummy_bytes = b"Hello world! foo bar baz 123 456 789" @@ -1213,7 +1213,8 @@ def __init__(self, *args, **kwargs): "dummy_userdict": dummy_userdict, "dummy_userlist": dummy_userlist, } - self.assertEqual(pprint.pformat(dummy_samples, width=40, indent=4, block_style=True, sort_dicts=False), + self.assertEqual(pprint.pformat(dummy_samples, width=40, indent=4, + block_style=True, sort_dicts=False), """\ { 'dummy_dataclass': DummyDataclass( From 37312d943c4877066ec627bd312ae237317bb087 Mon Sep 17 00:00:00 2001 From: StefanTodoran Date: Thu, 27 Mar 2025 18:21:31 -0700 Subject: [PATCH 31/31] Split large test case into multiple smaller ones, update news entry --- Doc/whatsnew/3.14.rst | 5 +- Lib/test/test_pprint.py | 392 ++++++++++++++++++++++++---------------- 2 files changed, 240 insertions(+), 157 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b724f79c26937f..d0b383ccf88b15 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -732,8 +732,9 @@ pprint ------ * Add a *block_style* keyword argument for :func:`pprint.pprint`, - :func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and - :class:`pprint.PrettyPrinter`. + :func:`pprint.pformat`, :func:`pprint.pp`. If true, the output will be + formatted in a block style similar to pretty-printed :func:`json.dumps` when + *indent* is supplied. Incompatible with *compact* mode. (Contributed by Stefan Todoran in :gh:`129274`.) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 8c17e35e9a9630..eb0fe79d847d05 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1132,7 +1132,7 @@ def test_user_string(self): 'jumped over a ' 'lazy dog'}""") - def test_block_style(self): + def test_block_style_dataclass(self): @dataclass class DummyDataclass: foo: str @@ -1151,7 +1151,19 @@ class DummyDataclass: corge=7, garply=(1, 2, 3, 4), ) - + self.assertEqual(pprint.pformat(dummy_dataclass, width=40, indent=4, block_style=True), +"""\ +DummyDataclass( + foo='foo', + bar=1.2, + baz=False, + qux={'baz': 123, 'foo': 'bar'}, + quux=['foo', 'bar', 'baz'], + corge=7, + garply=(1, 2, 3, 4) +)""") + + def test_block_style_dict(self): dummy_dict = { "foo": "bar", "baz": 123, @@ -1159,7 +1171,17 @@ class DummyDataclass: "quux": ["foo", "bar", "baz"], "corge": 7, } + self.assertEqual(pprint.pformat(dummy_dict, width=40, indent=4, block_style=True, sort_dicts=False), +"""\ +{ + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 +}""") + def test_block_style_ordered_dict(self): dummy_ordered_dict = OrderedDict( [ ("foo", 1), @@ -1167,13 +1189,31 @@ class DummyDataclass: ("baz", 123), ] ) + self.assertEqual(pprint.pformat(dummy_ordered_dict, width=20, indent=4, block_style=True), +"""\ +OrderedDict([ + ('foo', 1), + ('bar', 12), + ('baz', 123) +])""") + def test_block_style_list(self): dummy_list = [ "foo", "bar", "baz", + "qux", ] - + self.assertEqual(pprint.pformat(dummy_list, width=20, indent=4, block_style=True), +"""\ +[ + 'foo', + 'bar', + 'baz', + 'qux' +]""") + + def test_block_style_tuple(self): dummy_tuple = ( "foo", "bar", @@ -1181,13 +1221,40 @@ class DummyDataclass: 4, 5, 6, - dummy_list, ) - + self.assertEqual(pprint.pformat(dummy_tuple, width=20, indent=4, block_style=True), +"""\ +( + 'foo', + 'bar', + 'baz', + 4, + 5, + 6 +)""") + + def test_block_style_set(self): dummy_set = { + "foo", + "bar", + "baz", + "qux", (1, 2, 3), } + self.assertEqual(pprint.pformat(dummy_set, width=20, indent=4, block_style=True), +"""\ +{ + 'bar', + 'baz', + 'foo', + 'qux', + (1, 2, 3) +}""") + def test_block_style_frozenset(self): + dummy_set = { + (1, 2, 3), + } dummy_frozenset = frozenset( { "foo", @@ -1197,12 +1264,56 @@ class DummyDataclass: frozenset(dummy_set), } ) + self.assertEqual(pprint.pformat(dummy_frozenset, width=40, indent=4, block_style=True), +"""\ +frozenset({ + frozenset({(1, 2, 3)}), + 'bar', + 'baz', + 'foo', + (1, 2, 3) +})""") + + def test_block_style_bytes(self): + dummy_bytes = b"Hello world! foo bar baz 123 456 789" + self.assertEqual(pprint.pformat(dummy_bytes, width=20, indent=4, block_style=True), +"""\ +( + b'Hello world!' + b' foo bar baz' + b' 123 456 789' +)""") + def test_block_style_bytearray(self): dummy_bytes = b"Hello world! foo bar baz 123 456 789" dummy_byte_array = bytearray(dummy_bytes) + self.assertEqual(pprint.pformat(dummy_byte_array, width=40, indent=4, block_style=True), +"""\ +bytearray( + b'Hello world! foo bar baz 123 456' + b' 789' +)""") + def test_block_style_mappingproxy(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } dummy_mappingproxy = MappingProxyType(dummy_dict) - + self.assertEqual(pprint.pformat(dummy_mappingproxy, width=40, indent=4, block_style=True), +"""\ +mappingproxy({ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'} +})""") + + def test_block_style_namespace(self): dummy_namespace = SimpleNamespace( foo="bar", bar=42, @@ -1213,36 +1324,128 @@ class DummyDataclass: ), ) + self.assertEqual(pprint.pformat(dummy_namespace, width=40, indent=4, block_style=True), +"""\ +namespace( + foo='bar', + bar=42, + baz=namespace( + x=321, + y='string', + d={'bar': 'baz', 'foo': True} + ) +)""") + + def test_block_style_defaultdict(self): dummy_defaultdict = defaultdict(list) dummy_defaultdict["foo"].append("bar") dummy_defaultdict["foo"].append("baz") dummy_defaultdict["foo"].append("qux") dummy_defaultdict["bar"] = {"foo": "bar", "baz": None} + self.assertEqual(pprint.pformat(dummy_defaultdict, width=40, indent=4, block_style=True), +"""\ +defaultdict(, { + 'bar': {'baz': None, 'foo': 'bar'}, + 'foo': ['bar', 'baz', 'qux'] +})""") - dummy_counter = Counter() - dummy_counter.update("foo") - dummy_counter.update( - { - "bar": 5, - "baz": 3, - "qux": 10, - } - ) - + def test_block_style_counter(self): + dummy_counter = Counter("abcdeabcdabcaba") + expected = """\ +Counter({ + 'a': 5, + 'b': 4, + 'c': 3, + 'd': 2, + 'e': 1 +})""" + self.assertEqual(pprint.pformat(dummy_counter, width=40, indent=4, block_style=True), expected) + + expected2 = """\ +Counter({ + 'a': 5, + 'b': 4, + 'c': 3, + 'd': 2, + 'e': 1 +})""" + self.assertEqual(pprint.pformat(dummy_counter, width=20, indent=2, block_style=True), expected2) + + def test_block_style_chainmap(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } dummy_chainmap = ChainMap( {"foo": "bar"}, {"baz": "qux"}, {"corge": dummy_dict}, ) dummy_chainmap.maps.append({"garply": "waldo"}) + self.assertEqual(pprint.pformat(dummy_chainmap, width=40, indent=4, block_style=True), +"""\ +ChainMap( + {'foo': 'bar'}, + {'baz': 'qux'}, + { + 'corge': { + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': { + 'baz': 123, + 'foo': 'bar' + } + } + }, + {'garply': 'waldo'} +)""") + def test_block_style_deque(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + dummy_list = [ + "foo", + "bar", + "baz", + ] + dummy_set = { + (1, 2, 3), + } dummy_deque = deque(maxlen=10) dummy_deque.append("foo") dummy_deque.append(123) dummy_deque.append(dummy_dict) dummy_deque.extend(dummy_list) dummy_deque.appendleft(dummy_set) + self.assertEqual(pprint.pformat(dummy_deque, width=40, indent=4, block_style=True), +"""\ +deque([ + {(1, 2, 3)}, + 'foo', + 123, + { + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'} + }, + 'foo', + 'bar', + 'baz' +], maxlen=10)""") + def test_block_style_userdict(self): class DummyUserDict(UserDict): """A custom UserDict with some extra attributes""" @@ -1260,6 +1463,17 @@ def __init__(self, *args, **kwargs): ) dummy_userdict.access_count = 5 + self.assertEqual(pprint.pformat(dummy_userdict, width=40, indent=4, block_style=True), +"""\ +{ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'} +}""") + + def test_block_style_userlist(self): class DummyUserList(UserList): """A custom UserList with some extra attributes""" @@ -1268,146 +1482,14 @@ def __init__(self, *args, **kwargs): self.description = "foo" dummy_userlist = DummyUserList(["first", 2, {"key": "value"}, [4, 5, 6]]) - dummy_samples = { - "dummy_dataclass": dummy_dataclass, - "dummy_dict": dummy_dict, - "dummy_ordered_dict": dummy_ordered_dict, - "dummy_list": dummy_list, - "dummy_tuple": dummy_tuple, - "dummy_set": dummy_set, - "dummy_frozenset": dummy_frozenset, - "dummy_bytes": dummy_bytes, - "dummy_byte_array": dummy_byte_array, - "dummy_mappingproxy": dummy_mappingproxy, - "dummy_namespace": dummy_namespace, - "dummy_defaultdict": dummy_defaultdict, - "dummy_counter": dummy_counter, - "dummy_chainmap": dummy_chainmap, - "dummy_deque": dummy_deque, - "dummy_userdict": dummy_userdict, - "dummy_userlist": dummy_userlist, - } - self.assertEqual(pprint.pformat(dummy_samples, width=40, indent=4, block_style=True, sort_dicts=False), + self.assertEqual(pprint.pformat(dummy_userlist, width=40, indent=4, block_style=True), """\ -{ - 'dummy_dataclass': DummyDataclass( - foo='foo', - bar=1.2, - baz=False, - qux={'foo': 'bar', 'baz': 123}, - quux=['foo', 'bar', 'baz'], - corge=7, - garply=(1, 2, 3, 4) - ), - 'dummy_dict': { - 'foo': 'bar', - 'baz': 123, - 'qux': {'foo': 'bar', 'baz': 123}, - 'quux': ['foo', 'bar', 'baz'], - 'corge': 7 - }, - 'dummy_ordered_dict': OrderedDict([ - ('foo', 1), - ('bar', 12), - ('baz', 123) - ]), - 'dummy_list': ['foo', 'bar', 'baz'], - 'dummy_tuple': ( - 'foo', - 'bar', - 'baz', - 4, - 5, - 6, - ['foo', 'bar', 'baz'] - ), - 'dummy_set': {(1, 2, 3)}, - 'dummy_frozenset': frozenset({ - frozenset({(1, 2, 3)}), - 'bar', - 'baz', - 'foo', - (1, 2, 3) - }), - 'dummy_bytes': b'Hello world! foo bar baz 123 456' - b' 789', - 'dummy_byte_array': bytearray( - b'Hello world! foo bar baz 123' - b' 456 789' - ), - 'dummy_mappingproxy': mappingproxy({ - 'foo': 'bar', - 'baz': 123, - 'qux': {'foo': 'bar', 'baz': 123}, - 'quux': ['foo', 'bar', 'baz'], - 'corge': 7 - }), - 'dummy_namespace': namespace( - foo='bar', - bar=42, - baz=namespace( - x=321, - y='string', - d={'foo': True, 'bar': 'baz'} - ) - ), - 'dummy_defaultdict': defaultdict(, { - 'foo': ['bar', 'baz', 'qux'], - 'bar': {'foo': 'bar', 'baz': None} - }), - 'dummy_counter': Counter({ - 'qux': 10, - 'bar': 5, - 'baz': 3, - 'o': 2, - 'f': 1 - }), - 'dummy_chainmap': ChainMap( - {'foo': 'bar'}, - {'baz': 'qux'}, - { - 'corge': { - 'foo': 'bar', - 'baz': 123, - 'qux': { - 'foo': 'bar', - 'baz': 123 - }, - 'quux': ['foo', 'bar', 'baz'], - 'corge': 7 - } - }, - {'garply': 'waldo'} - ), - 'dummy_deque': deque([ - {(1, 2, 3)}, - 'foo', - 123, - { - 'foo': 'bar', - 'baz': 123, - 'qux': {'foo': 'bar', 'baz': 123}, - 'quux': ['foo', 'bar', 'baz'], - 'corge': 7 - }, - 'foo', - 'bar', - 'baz' - ], maxlen=10), - 'dummy_userdict': { - 'foo': 'bar', - 'baz': 123, - 'qux': {'foo': 'bar', 'baz': 123}, - 'quux': ['foo', 'bar', 'baz'], - 'corge': 7 - }, - 'dummy_userlist': [ - 'first', - 2, - {'key': 'value'}, - [4, 5, 6] - ] -}""") +[ + 'first', + 2, + {'key': 'value'}, + [4, 5, 6] +]""") class DottedPrettyPrinter(pprint.PrettyPrinter):