Issue 14998: pprint._safe_key is not always safe enough (original) (raw)

This is related to resolved issue 3976 and, to a lesser extent, issue 10017.

I've run across another instance where pprint throws an exception (but works fine in 2.7 and earlier):

Python 3.2 (r32:88445, Mar 25 2011, 19:28:28) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information.

from pprint import pprint pprint({(0,): 1, (None,): 2}) Traceback (most recent call last): File "", line 1, in File "/usr/lib/python3.2/pprint.py", line 55, in pprint printer.pprint(object) File "/usr/lib/python3.2/pprint.py", line 132, in pprint self._format(object, self._stream, 0, 0, {}, 0) File "/usr/lib/python3.2/pprint.py", line 155, in _format rep = self._repr(object, context, level - 1) File "/usr/lib/python3.2/pprint.py", line 245, in _repr self._depth, level) File "/usr/lib/python3.2/pprint.py", line 257, in format return _safe_repr(object, context, maxlevels, level) File "/usr/lib/python3.2/pprint.py", line 299, in _safe_repr items = sorted(object.items(), key=_safe_tuple) File "/usr/lib/python3.2/pprint.py", line 89, in lt rv = self.obj.lt(other.obj) TypeError: unorderable types: int() < NoneType()

The above example might seem contrived but I stumbled across the issue quite naturally. Honest!

In working with multiple lists and computing results using combinations of these lists' values. I could organize the results as a dictionary of dictionaries of dictionaries but that would get confusing very quickly. Instead, I'm using a single dictionary with a composite key ("flat is better than nested"). So I've got code like this...

combinations = itertools.product(lst_x, lst_y, lst_z) results = {(x,y,z): compute(x,y,z) for x,y,z in combinations}

... and it is not uncommon for one or more of the values to be None -- resulting in the above exception should anyone (including unittest) attempt to pprint the dictionary.

Currently, I'm monkey patching _safe_key (adding a try/except) as follows:

import pprint

class _safe_key(pprint._safe_key): def lt(self, other): try: rv = self.obj.lt(other.obj) except TypeError: # Exception instead of TypeError? rv = NotImplemented

    if rv is NotImplemented:
        rv = (str(type(self.obj)), id(self.obj)) < \
             (str(type(other.obj)), id(other.obj))
    return rv
    

pprint._safe_key = _safe_key

pprint.pprint({(0,): 1, (None,): 2}) {(None,): 2, (0,): 1}