Kaydet (Commit) a750ce33 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka

Issue #19105: pprint now more efficiently uses free space at the right.

üst 8089cd64
...@@ -161,7 +161,7 @@ class PrettyPrinter: ...@@ -161,7 +161,7 @@ class PrettyPrinter:
return return
rep = self._repr(object, context, level - 1) rep = self._repr(object, context, level - 1)
typ = type(object) typ = type(object)
max_width = self._width - 1 - indent - allowance max_width = self._width - indent - allowance
sepLines = len(rep) > max_width sepLines = len(rep) > max_width
write = stream.write write = stream.write
...@@ -174,24 +174,14 @@ class PrettyPrinter: ...@@ -174,24 +174,14 @@ class PrettyPrinter:
length = len(object) length = len(object)
if length: if length:
context[objid] = 1 context[objid] = 1
indent = indent + self._indent_per_level
if issubclass(typ, _OrderedDict): if issubclass(typ, _OrderedDict):
items = list(object.items()) items = list(object.items())
else: else:
items = sorted(object.items(), key=_safe_tuple) items = sorted(object.items(), key=_safe_tuple)
key, ent = items[0] self._format_dict_items(items, stream,
rep = self._repr(key, context, level) indent + self._indent_per_level,
write(rep) allowance + 1,
write(': ') context, level)
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] del context[objid]
write('}') write('}')
return return
...@@ -207,7 +197,10 @@ class PrettyPrinter: ...@@ -207,7 +197,10 @@ class PrettyPrinter:
endchar = ']' endchar = ']'
elif issubclass(typ, tuple): elif issubclass(typ, tuple):
write('(') write('(')
endchar = ')' if length == 1:
endchar = ',)'
else:
endchar = ')'
else: else:
if not length: if not length:
write(rep) write(rep)
...@@ -227,10 +220,9 @@ class PrettyPrinter: ...@@ -227,10 +220,9 @@ class PrettyPrinter:
context[objid] = 1 context[objid] = 1
self._format_items(object, stream, self._format_items(object, stream,
indent + self._indent_per_level, indent + self._indent_per_level,
allowance + 1, context, level) allowance + len(endchar),
context, level)
del context[objid] del context[objid]
if issubclass(typ, tuple) and length == 1:
write(',')
write(endchar) write(endchar)
return return
...@@ -239,19 +231,27 @@ class PrettyPrinter: ...@@ -239,19 +231,27 @@ class PrettyPrinter:
lines = object.splitlines(True) lines = object.splitlines(True)
if level == 1: if level == 1:
indent += 1 indent += 1
max_width -= 2 allowance += 1
max_width1 = max_width = self._width - indent
for i, line in enumerate(lines): for i, line in enumerate(lines):
rep = repr(line) rep = repr(line)
if len(rep) <= max_width: if i == len(lines) - 1:
max_width1 -= allowance
if len(rep) <= max_width1:
chunks.append(rep) chunks.append(rep)
else: else:
# A list of alternating (non-space, space) strings # A list of alternating (non-space, space) strings
parts = re.split(r'(\s+)', line) + [''] parts = re.findall(r'\S*\s*', line)
assert parts
assert not parts[-1]
parts.pop() # drop empty last part
max_width2 = max_width
current = '' current = ''
for i in range(0, len(parts), 2): for j, part in enumerate(parts):
part = parts[i] + parts[i+1]
candidate = current + part candidate = current + part
if len(repr(candidate)) > max_width: if j == len(parts) - 1 and i == len(lines) - 1:
max_width2 -= allowance
if len(repr(candidate)) > max_width2:
if current: if current:
chunks.append(repr(current)) chunks.append(repr(current))
current = part current = part
...@@ -273,12 +273,41 @@ class PrettyPrinter: ...@@ -273,12 +273,41 @@ class PrettyPrinter:
return return
write(rep) write(rep)
def _format_dict_items(self, items, stream, indent, allowance, context,
level):
write = stream.write
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): def _format_items(self, items, stream, indent, allowance, context, level):
write = stream.write write = stream.write
delimnl = ',\n' + ' ' * indent delimnl = ',\n' + ' ' * indent
delim = '' delim = ''
width = max_width = self._width - indent - allowance + 2 width = max_width = self._width - indent + 1
for ent in items: 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: if self._compact:
rep = self._repr(ent, context, level) rep = self._repr(ent, context, level)
w = len(rep) + 2 w = len(rep) + 2
...@@ -294,7 +323,9 @@ class PrettyPrinter: ...@@ -294,7 +323,9 @@ class PrettyPrinter:
continue continue
write(delim) write(delim)
delim = delimnl 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): def _repr(self, object, context, level):
repr, readable, recursive = self.format(object, context.copy(), repr, readable, recursive = self.format(object, context.copy(),
......
...@@ -191,11 +191,53 @@ class QueryTestCase(unittest.TestCase): ...@@ -191,11 +191,53 @@ class QueryTestCase(unittest.TestCase):
o2 = dict(first=1, second=2, third=3) o2 = dict(first=1, second=2, third=3)
o = [o1, o2] o = [o1, o2]
expected = """\ expected = """\
[ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
{'first': 1, 'second': 2, 'third': 3}]"""
self.assertEqual(pprint.pformat(o, indent=4, width=42), expected)
expected = """\
[ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
{ 'first': 1, { 'first': 1,
'second': 2, 'second': 2,
'third': 3}]""" 'third': 3}]"""
self.assertEqual(pprint.pformat(o, indent=4, width=42), expected) self.assertEqual(pprint.pformat(o, indent=4, width=41), expected)
def test_width(self):
expected = """\
[[[[[[1, 2, 3],
'1 2']]]],
{1: [1, 2, 3],
2: [12, 34]},
'abc def ghi',
('ab cd ef',),
set2({1, 23}),
[[[[[1, 2, 3],
'1 2']]]]]"""
o = eval(expected)
self.assertEqual(pprint.pformat(o, width=15), expected)
self.assertEqual(pprint.pformat(o, width=16), expected)
self.assertEqual(pprint.pformat(o, width=25), expected)
self.assertEqual(pprint.pformat(o, width=14), """\
[[[[[[1,
2,
3],
'1 '
'2']]]],
{1: [1,
2,
3],
2: [12,
34]},
'abc def '
'ghi',
('ab cd '
'ef',),
set2({1,
23}),
[[[[[1,
2,
3],
'1 '
'2']]]]]""")
def test_sorted_dict(self): def test_sorted_dict(self):
# Starting in Python 2.5, pprint sorts dict displays by key regardless # Starting in Python 2.5, pprint sorts dict displays by key regardless
...@@ -535,13 +577,12 @@ frozenset2({0, ...@@ -535,13 +577,12 @@ frozenset2({0,
def test_str_wrap(self): def test_str_wrap(self):
# pprint tries to wrap strings intelligently # pprint tries to wrap strings intelligently
fox = 'the quick brown fox jumped over a lazy dog' fox = 'the quick brown fox jumped over a lazy dog'
self.assertEqual(pprint.pformat(fox, width=20), """\ self.assertEqual(pprint.pformat(fox, width=19), """\
('the quick ' ('the quick brown '
'brown fox ' 'fox jumped over '
'jumped over a ' 'a lazy dog')""")
'lazy dog')""")
self.assertEqual(pprint.pformat({'a': 1, 'b': fox, 'c': 2}, self.assertEqual(pprint.pformat({'a': 1, 'b': fox, 'c': 2},
width=26), """\ width=25), """\
{'a': 1, {'a': 1,
'b': 'the quick brown ' 'b': 'the quick brown '
'fox jumped over ' 'fox jumped over '
...@@ -553,12 +594,34 @@ frozenset2({0, ...@@ -553,12 +594,34 @@ frozenset2({0,
# - non-ASCII is allowed # - non-ASCII is allowed
# - an apostrophe doesn't disrupt the pprint # - an apostrophe doesn't disrupt the pprint
special = "Portons dix bons \"whiskys\"\nà l'avocat goujat\t qui fumait au zoo" special = "Portons dix bons \"whiskys\"\nà l'avocat goujat\t qui fumait au zoo"
self.assertEqual(pprint.pformat(special, width=21), """\ self.assertEqual(pprint.pformat(special, width=68), repr(special))
('Portons dix ' self.assertEqual(pprint.pformat(special, width=31), """\
'bons "whiskys"\\n' ('Portons dix bons "whiskys"\\n'
"à l'avocat goujat\\t qui "
'fumait au zoo')""")
self.assertEqual(pprint.pformat(special, width=20), """\
('Portons dix bons '
'"whiskys"\\n'
"à l'avocat " "à l'avocat "
'goujat\\t qui ' 'goujat\\t qui '
'fumait au zoo')""") 'fumait au zoo')""")
self.assertEqual(pprint.pformat([[[[[special]]]]], width=35), """\
[[[[['Portons dix bons "whiskys"\\n'
"à l'avocat goujat\\t qui "
'fumait au zoo']]]]]""")
self.assertEqual(pprint.pformat([[[[[special]]]]], width=25), """\
[[[[['Portons dix bons '
'"whiskys"\\n'
"à l'avocat "
'goujat\\t qui '
'fumait au zoo']]]]]""")
self.assertEqual(pprint.pformat([[[[[special]]]]], width=23), """\
[[[[['Portons dix '
'bons "whiskys"\\n'
"à l'avocat "
'goujat\\t qui '
'fumait au '
'zoo']]]]]""")
# An unwrappable string is formatted as its repr # An unwrappable string is formatted as its repr
unwrappable = "x" * 100 unwrappable = "x" * 100
self.assertEqual(pprint.pformat(unwrappable, width=80), repr(unwrappable)) self.assertEqual(pprint.pformat(unwrappable, width=80), repr(unwrappable))
...@@ -581,7 +644,19 @@ frozenset2({0, ...@@ -581,7 +644,19 @@ frozenset2({0,
14, 15], 14, 15],
[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3],
[0, 1, 2, 3, 4]]""" [0, 1, 2, 3, 4]]"""
self.assertEqual(pprint.pformat(o, width=48, compact=True), expected) self.assertEqual(pprint.pformat(o, width=47, compact=True), expected)
def test_compact_width(self):
levels = 20
number = 10
o = [0] * number
for i in range(levels - 1):
o = [o]
for w in range(levels * 2 + 1, levels + 3 * number - 1):
lines = pprint.pformat(o, width=w, compact=True).splitlines()
maxwidth = max(map(len, lines))
self.assertLessEqual(maxwidth, w)
self.assertGreater(maxwidth, w - 3)
class DottedPrettyPrinter(pprint.PrettyPrinter): class DottedPrettyPrinter(pprint.PrettyPrinter):
......
...@@ -13,6 +13,8 @@ Core and Builtins ...@@ -13,6 +13,8 @@ Core and Builtins
Library Library
------- -------
- Issue #19105: pprint now more efficiently uses free space at the right.
- Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by - Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by
Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson. Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment