pprint's workhorse _safe_repr() function took time quadratic in the # of

elements when crunching a list, dict or tuple.  Now takes linear time
instead -- huge speedup for even moderately large containers, and the
code is notably simpler too.
Added some basic "is the output correct?" tests to test_pprint.
This commit is contained in:
Tim Peters 2001-05-14 18:39:41 +00:00
parent 43913dd27c
commit 95b3f78622
3 changed files with 48 additions and 40 deletions

View File

@ -188,66 +188,55 @@ class PrettyPrinter:
# Return triple (repr_string, isreadable, isrecursive). # Return triple (repr_string, isreadable, isrecursive).
def _safe_repr(object, context, maxlevels=None, level=0): def _safe_repr(object, context, maxlevels=None, level=0):
level = level + 1 level += 1
typ = type(object) typ = type(object)
if not (typ in (DictType, ListType, TupleType) and object): if not (typ in (DictType, ListType, TupleType) and object):
rep = `object` rep = `object`
return rep, (rep and (rep[0] != '<')), 0 return rep, (rep and (rep[0] != '<')), 0
if context.has_key(id(object)): if context.has_key(id(object)):
return `_Recursion(object)`, 0, 1 return `_Recursion(object)`, 0, 1
objid = id(object) objid = id(object)
context[objid] = 1 context[objid] = 1
readable = 1 readable = 1
recursive = 0 recursive = 0
if typ is DictType: startchar, endchar = {ListType: "[]",
if maxlevels and level >= maxlevels: TupleType: "()",
s = "{...}" DictType: "{}"}[typ]
readable = 0 if maxlevels and level > maxlevels:
else: with_commas = "..."
items = object.items() readable = 0
k, v = items[0]
elif typ is DictType:
components = []
for k, v in object.iteritems():
krepr, kreadable, krecur = _safe_repr(k, context, maxlevels, krepr, kreadable, krecur = _safe_repr(k, context, maxlevels,
level) level)
vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels, vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels,
level) level)
components.append("%s: %s" % (krepr, vrepr))
readable = readable and kreadable and vreadable readable = readable and kreadable and vreadable
recursive = recursive or krecur or vrecur recursive = recursive or krecur or vrecur
s = "{%s: %s" % (krepr, vrepr) with_commas = ", ".join(components)
for k, v in items[1:]:
krepr, kreadable, krecur = _safe_repr(k, context, maxlevels, else: # list or tuple
level) assert typ in (ListType, TupleType)
vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels, components = []
level) for element in object:
readable = readable and kreadable and vreadable
recursive = recursive or krecur or vrecur
s = "%s, %s: %s" % (s, krepr, vrepr)
s = s + "}"
else:
s, term = (typ is ListType) and ('[', ']') or ('(', ')')
if maxlevels and level >= maxlevels:
s = s + "..."
readable = 0
else:
subrepr, subreadable, subrecur = _safe_repr( subrepr, subreadable, subrecur = _safe_repr(
object[0], context, maxlevels, level) element, context, maxlevels, level)
components.append(subrepr)
readable = readable and subreadable readable = readable and subreadable
recursive = recursive or subrecur recursive = recursive or subrecur
s = s + subrepr if len(components) == 1 and typ is TupleType:
tail = object[1:] components[0] += ","
if not tail: with_commas = ", ".join(components)
if typ is TupleType:
s = s + ',' s = "%s%s%s" % (startchar, with_commas, endchar)
for ent in tail:
subrepr, subreadable, subrecur = _safe_repr(
ent, context, maxlevels, level)
readable = readable and subreadable
recursive = recursive or subrecur
s = "%s, %s" % (s, subrepr)
s = s + term
del context[objid] del context[objid]
return s, readable and not recursive, recursive return s, readable and not recursive, recursive
class _Recursion: class _Recursion:
# represent a recursive relationship; really only used for the __repr__() # represent a recursive relationship; really only used for the __repr__()
# method... # method...

View File

@ -12,7 +12,7 @@ class QueryTestCase(unittest.TestCase):
self.a[-12] = self.b self.a[-12] = self.b
def test_basic(self): def test_basic(self):
# Verify that .isrecursive() and .isreadable() work. # Verify that .isrecursive() and .isreadable() work w/o recursion.
verify = self.assert_ verify = self.assert_
for safe in (2, 2.0, 2j, "abc", [3], (2,2), {3: 3}, u"yaddayadda", for safe in (2, 2.0, 2j, "abc", [3], (2,2), {3: 3}, u"yaddayadda",
self.a, self.b): self.a, self.b):
@ -22,6 +22,7 @@ class QueryTestCase(unittest.TestCase):
"expected isreadable for " + `safe`) "expected isreadable for " + `safe`)
def test_knotted(self): def test_knotted(self):
# Verify that .isrecursive() and .isreadable() work w/ recursion.
# Tie a knot. # Tie a knot.
self.b[67] = self.a self.b[67] = self.a
# Messy dict. # Messy dict.
@ -54,5 +55,20 @@ class QueryTestCase(unittest.TestCase):
verify(not pprint.isreadable(unreadable), verify(not pprint.isreadable(unreadable),
"expected not isreadable for " + `unreadable`) "expected not isreadable for " + `unreadable`)
def test_same_as_repr(self):
"Simple objects and small containers that should be same as repr()."
verify = self.assert_
for simple in (0, 0L, 0+0j, 0.0, "", u"", (), [], {}, verify, pprint,
-6, -6L, -6-6j, -1.5, "x", u"x", (3,), [3], {3: 6},
(1,2), [3,4], {5: 6, 7: 8},
{"xy\tab\n": (3,), 5: [[]], (): {}},
range(10, -11, -1)
):
native = repr(simple)
for function in "pformat", "saferepr":
f = getattr(pprint, function)
got = f(simple)
verify(native == got, "expected %s got %s from pprint.%s" %
(native, got, function))
test_support.run_unittest(QueryTestCase) test_support.run_unittest(QueryTestCase)

View File

@ -92,6 +92,8 @@ Library
- pprint.isrecursive(object) didn't correctly identify recursive objects. - pprint.isrecursive(object) didn't correctly identify recursive objects.
Now it does. Now it does.
- pprint functions now much faster for large containers (tuple, list, dict).
Tests Tests
- New test_mutants.py runs dict comparisons where the key and value - New test_mutants.py runs dict comparisons where the key and value
@ -100,7 +102,8 @@ Tests
of heart: it can also cause Win9x to freeze or reboot!). of heart: it can also cause Win9x to freeze or reboot!).
- New test_pprint.py verfies that pprint.isrecursive() and - New test_pprint.py verfies that pprint.isrecursive() and
pprint.isreadable() return sensible results. pprint.isreadable() return sensible results. Also verifies that simple
cases produce correct output.
What's New in Python 2.1 (final)? What's New in Python 2.1 (final)?