[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