[issue10017] pprint.pprint raises TypeError on dictionaries with user-defined types as keys

Rodrigo Bernardo Pimentel report at bugs.python.org
Fri Oct 8 20:51:40 CEST 2010


Rodrigo Bernardo Pimentel <rbp at isnomore.net> added the comment:

If I'm understanding this correctly, this fails on 3.1 and not (although, actually, it does) on py3k/3.2 because:

* pprint._safe_key.__lt__ checks "rv = self.obj.__lt__(other.obj)" and falls back to id comparison if rv is NotImplemented

* If the object passed to _safe_key is a class, self.obj.__lt__ will expect *self* as well as the other object. Therefore the verification above fails with "TypeError: expected 1 arguments, got 0". You can see that pprint works with an instance:

>>> pprint.pprint({A(): 1, 1: 2})
{<__main__.A object at 0x8594d4c>: 1, 1: 2}

* Finally, this works on py3k *for your example* because, for some reason, on py3k the comparison is being based on the 1 key. That is, the comparison on _safe_key.__lt__ happens to be 1.__lt__(A), instead of A.__lt__(1). Perhaps hashing changed after the 3.1 release?

Anyway, py3k still fails when you force the comparison to happen on the class:

>>> class B(object): pass
... 
>>> pprint.pprint({A: 1, B: 2})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 55, in pprint
    printer.pprint(object)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 132, in pprint
    self._format(object, self._stream, 0, 0, {}, 0)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 155, in _format
    rep = self._repr(object, context, level - 1)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 245, in _repr
    self._depth, level)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 257, in format
    return _safe_repr(object, context, maxlevels, level)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 299, in _safe_repr
    items = sorted(object.items(), key=_safe_tuple)
  File "/home/rbp/python/dev/py3k/Lib/pprint.py", line 89, in __lt__
    rv = self.obj.__lt__(other.obj)
TypeError: expected 1 arguments, got 0
>>> 

So, basically, the fix on issue 3976 does't (always) work when there are classes as dict keys. I think switching from

rv = self.obj.__lt__(other.obj)
if rv is NotImplemented:

to something like

try:
    rv = self.obj < other.obj
except TypeError:
    rv = (str(type(self.obj)), id(self.obj)) < \
    (str(type(other.obj)), id(other.obj))

solves this. Or we can check first whether self.obj is a 'type', but I think it gets convoluted.

If anyone can confirm that that's the way to go, I can produce one (though it's a trivial one). Raymond?

----------

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue10017>
_______________________________________


More information about the Python-bugs-list mailing list