diff options
Diffstat (limited to 'Lib/pprint.py')
-rw-r--r-- | Lib/pprint.py | 457 |
1 files changed, 316 insertions, 141 deletions
diff --git a/Lib/pprint.py b/Lib/pprint.py index 2cbffed5d8..87649b48cd 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -34,9 +34,10 @@ saferepr() """ +import collections as _collections import re import sys as _sys -from collections import OrderedDict as _OrderedDict +import types as _types from io import StringIO as _StringIO __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr", @@ -85,14 +86,10 @@ class _safe_key: def __lt__(self, other): try: - rv = self.obj.__lt__(other.obj) + return self.obj < other.obj except TypeError: - rv = NotImplemented - - if rv is NotImplemented: - rv = (str(type(self.obj)), id(self.obj)) < \ - (str(type(other.obj)), id(other.obj)) - return rv + return ((str(type(self.obj)), id(self.obj)) < \ + (str(type(other.obj)), id(other.obj))) def _safe_tuple(t): "Helper function for comparing 2-tuples" @@ -123,9 +120,12 @@ class PrettyPrinter: """ indent = int(indent) width = int(width) - assert indent >= 0, "indent must be >= 0" - assert depth is None or depth > 0, "depth must be > 0" - assert width, "width must be != 0" + if indent < 0: + raise ValueError('indent must be >= 0') + if depth is not None and depth <= 0: + raise ValueError('depth must be > 0') + if not width: + raise ValueError('width must be != 0') self._depth = depth self._indent_per_level = indent self._width = width @@ -152,133 +152,223 @@ class PrettyPrinter: return readable and not recursive def _format(self, object, stream, indent, allowance, context, level): - level = level + 1 objid = id(object) if objid in context: stream.write(_recursion(object)) self._recursive = True self._readable = False return - rep = self._repr(object, context, level - 1) - typ = type(object) - max_width = self._width - 1 - indent - allowance - sepLines = len(rep) > max_width - write = stream.write - - if sepLines: - r = getattr(typ, "__repr__", None) - if issubclass(typ, dict): - write('{') - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') - length = len(object) - if length: - context[objid] = 1 - indent = indent + self._indent_per_level - if issubclass(typ, _OrderedDict): - items = list(object.items()) - else: - items = sorted(object.items(), key=_safe_tuple) - key, ent = items[0] - rep = self._repr(key, context, level) - write(rep) - write(': ') - self._format(ent, stream, indent + len(rep) + 2, - allowance + 1, context, level) - if length > 1: - for key, ent in items[1:]: - rep = self._repr(key, context, level) - write(',\n%s%s: ' % (' '*indent, rep)) - self._format(ent, stream, indent + len(rep) + 2, - allowance + 1, context, level) - indent = indent - self._indent_per_level - del context[objid] - write('}') + rep = self._repr(object, context, level) + max_width = self._width - indent - allowance + if len(rep) > max_width: + p = self._dispatch.get(type(object).__repr__, None) + if p is not None: + context[objid] = 1 + p(self, object, stream, indent, allowance, context, level + 1) + del context[objid] return - - if ((issubclass(typ, list) and r is list.__repr__) or - (issubclass(typ, tuple) and r is tuple.__repr__) or - (issubclass(typ, set) and r is set.__repr__) or - (issubclass(typ, frozenset) and r is frozenset.__repr__) - ): - length = len(object) - if issubclass(typ, list): - write('[') - endchar = ']' - elif issubclass(typ, tuple): - write('(') - endchar = ')' - else: - if not length: - write(rep) - return - if typ is set: - write('{') - endchar = '}' - else: - write(typ.__name__) - write('({') - endchar = '})' - indent += len(typ.__name__) + 1 - object = sorted(object, key=_safe_key) - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') - if length: - context[objid] = 1 - self._format_items(object, stream, - indent + self._indent_per_level, - allowance + 1, context, level) - del context[objid] - if issubclass(typ, tuple) and length == 1: - write(',') - write(endchar) + elif isinstance(object, dict): + context[objid] = 1 + self._pprint_dict(object, stream, indent, allowance, + context, level + 1) + del context[objid] return + stream.write(rep) - if issubclass(typ, str) and len(object) > 0 and r is str.__repr__: - chunks = [] - lines = object.splitlines(True) - if level == 1: - indent += 1 - max_width -= 2 - for i, line in enumerate(lines): - rep = repr(line) - if len(rep) <= max_width: - chunks.append(rep) - else: - # A list of alternating (non-space, space) strings - parts = re.split(r'(\s+)', line) + [''] - current = '' - for i in range(0, len(parts), 2): - part = parts[i] + parts[i+1] - candidate = current + part - if len(repr(candidate)) > max_width: - if current: - chunks.append(repr(current)) - current = part - else: - current = candidate + _dispatch = {} + + def _pprint_dict(self, object, stream, indent, allowance, context, level): + write = stream.write + write('{') + if self._indent_per_level > 1: + write((self._indent_per_level - 1) * ' ') + length = len(object) + if length: + items = sorted(object.items(), key=_safe_tuple) + self._format_dict_items(items, stream, indent, allowance + 1, + context, level) + write('}') + + _dispatch[dict.__repr__] = _pprint_dict + + def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level): + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + '(') + self._format(list(object.items()), stream, + indent + len(cls.__name__) + 1, 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('[') + self._format_items(object, stream, indent, allowance + 1, + context, level) + stream.write(']') + + _dispatch[list.__repr__] = _pprint_list + + def _pprint_tuple(self, object, stream, indent, allowance, context, level): + stream.write('(') + endchar = ',)' if len(object) == 1 else ')' + self._format_items(object, stream, indent, allowance + len(endchar), + context, level) + stream.write(endchar) + + _dispatch[tuple.__repr__] = _pprint_tuple + + def _pprint_set(self, object, stream, indent, allowance, context, level): + if not len(object): + stream.write(repr(object)) + return + typ = object.__class__ + if typ is set: + stream.write('{') + endchar = '}' + else: + stream.write(typ.__name__ + '({') + endchar = '})' + indent += len(typ.__name__) + 1 + object = sorted(object, key=_safe_key) + self._format_items(object, stream, indent, allowance + len(endchar), + context, level) + stream.write(endchar) + + _dispatch[set.__repr__] = _pprint_set + _dispatch[frozenset.__repr__] = _pprint_set + + def _pprint_str(self, object, stream, indent, allowance, context, level): + write = stream.write + if not len(object): + write(repr(object)) + return + chunks = [] + lines = object.splitlines(True) + if level == 1: + indent += 1 + allowance += 1 + max_width1 = max_width = self._width - indent + for i, line in enumerate(lines): + rep = repr(line) + if i == len(lines) - 1: + max_width1 -= allowance + if len(rep) <= max_width1: + chunks.append(rep) + else: + # A list of alternating (non-space, space) strings + parts = re.findall(r'\S*\s*', line) + assert parts + assert not parts[-1] + parts.pop() # drop empty last part + max_width2 = max_width + current = '' + for j, part in enumerate(parts): + candidate = current + part + if j == len(parts) - 1 and i == len(lines) - 1: + max_width2 -= allowance + if len(repr(candidate)) > max_width2: if current: chunks.append(repr(current)) - if len(chunks) == 1: - write(rep) - return - if level == 1: - write('(') - for i, rep in enumerate(chunks): - if i > 0: - write('\n' + ' '*indent) - write(rep) - if level == 1: - write(')') - return - write(rep) + current = part + else: + current = candidate + if current: + chunks.append(repr(current)) + if len(chunks) == 1: + write(rep) + return + if level == 1: + write('(') + for i, rep in enumerate(chunks): + if i > 0: + write('\n' + ' '*indent) + write(rep) + if level == 1: + write(')') + + _dispatch[str.__repr__] = _pprint_str + + def _pprint_bytes(self, object, stream, indent, allowance, context, level): + write = stream.write + if len(object) <= 4: + write(repr(object)) + return + parens = level == 1 + if parens: + indent += 1 + allowance += 1 + write('(') + delim = '' + for rep in _wrap_bytes_repr(object, self._width - indent, allowance): + write(delim) + write(rep) + if not delim: + delim = '\n' + ' '*indent + if parens: + write(')') + + _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, + allowance + 1, context, level + 1) + write(')') + + _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, + context, level) + stream.write(')') + + _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy + + def _format_dict_items(self, items, stream, indent, allowance, context, + level): + write = stream.write + indent += self._indent_per_level + delimnl = ',\n' + ' ' * indent + last_index = len(items) - 1 + for i, (key, ent) in enumerate(items): + last = i == last_index + rep = self._repr(key, context, level) + write(rep) + write(': ') + self._format(ent, stream, indent + len(rep) + 2, + allowance if last else 1, + context, level) + if not last: + write(delimnl) 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) * ' ') delimnl = ',\n' + ' ' * indent delim = '' - width = max_width = self._width - indent - allowance + 2 - for ent in items: + width = max_width = self._width - indent + 1 + it = iter(items) + try: + next_ent = next(it) + except StopIteration: + return + last = False + while not last: + ent = next_ent + try: + next_ent = next(it) + except StopIteration: + last = True + max_width -= allowance + width -= allowance if self._compact: rep = self._repr(ent, context, level) w = len(rep) + 2 @@ -294,7 +384,9 @@ class PrettyPrinter: continue write(delim) delim = delimnl - self._format(ent, stream, indent, allowance, context, level) + self._format(ent, stream, indent, + allowance if last else 1, + context, level) def _repr(self, object, context, level): repr, readable, recursive = self.format(object, context.copy(), @@ -312,29 +404,93 @@ class PrettyPrinter: """ return _safe_repr(object, context, maxlevels, level) + def _pprint_default_dict(self, object, stream, indent, allowance, context, level): + if not len(object): + stream.write(repr(object)) + 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)) + self._pprint_dict(object, stream, indent, allowance + 1, context, level) + stream.write(')') + + _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict + + def _pprint_counter(self, object, stream, indent, allowance, context, level): + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + '({') + if self._indent_per_level > 1: + stream.write((self._indent_per_level - 1) * ' ') + items = object.most_common() + self._format_dict_items(items, stream, + indent + len(cls.__name__) + 1, allowance + 2, + context, level) + stream.write('})') + + _dispatch[_collections.Counter.__repr__] = _pprint_counter + + def _pprint_chain_map(self, object, stream, indent, allowance, context, level): + if not len(object.maps): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + '(') + indent += len(cls.__name__) + 1 + for i, m in enumerate(object.maps): + if i == len(object.maps) - 1: + self._format(m, stream, indent, allowance + 1, context, level) + stream.write(')') + else: + self._format(m, stream, indent, 1, context, level) + stream.write(',\n' + ' ' * indent) + + _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map + + def _pprint_deque(self, object, stream, indent, allowance, context, level): + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + '(') + indent += len(cls.__name__) + 1 + stream.write('[') + if object.maxlen is None: + self._format_items(object, stream, indent, allowance + 2, + context, level) + stream.write('])') + else: + self._format_items(object, stream, indent, 2, + context, level) + rml = self._repr(object.maxlen, context, level) + stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) + + _dispatch[_collections.deque.__repr__] = _pprint_deque + + def _pprint_user_dict(self, object, stream, indent, allowance, context, level): + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict + + def _pprint_user_list(self, object, stream, indent, allowance, context, level): + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserList.__repr__] = _pprint_user_list + + def _pprint_user_string(self, object, stream, indent, allowance, context, level): + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserString.__repr__] = _pprint_user_string # Return triple (repr_string, isreadable, isrecursive). def _safe_repr(object, context, maxlevels, level): typ = type(object) - if typ is str: - if 'locale' not in _sys.modules: - return repr(object), True, False - if "'" in object and '"' not in object: - closure = '"' - quotes = {'"': '\\"'} - else: - closure = "'" - quotes = {"'": "\\'"} - qget = quotes.get - sio = _StringIO() - write = sio.write - for char in object: - if char.isalpha(): - write(char) - else: - write(qget(char, repr(char)[1:-1])) - return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False + if typ in _builtin_scalars: + return repr(object), True, False r = getattr(typ, "__repr__", None) if issubclass(typ, dict) and r is dict.__repr__: @@ -399,6 +555,8 @@ def _safe_repr(object, context, maxlevels, level): rep = repr(object) return rep, (rep and not rep.startswith('<')), False +_builtin_scalars = frozenset({str, bytes, bytearray, int, float, complex, + bool, type(None)}) def _recursion(object): return ("<Recursion on %s with id=%s>" @@ -418,5 +576,22 @@ def _perfcheck(object=None): print("_safe_repr:", t2 - t1) print("pformat:", t3 - t2) +def _wrap_bytes_repr(object, width, allowance): + current = b'' + last = len(object) // 4 * 4 + for i in range(0, len(object), 4): + part = object[i: i+4] + candidate = current + part + if i == last: + width -= allowance + if len(repr(candidate)) > width: + if current: + yield repr(current) + current = part + else: + current = candidate + if current: + yield repr(current) + if __name__ == "__main__": _perfcheck() |