-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
traceback module can't format/print unhashable exceptions #72789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The traceback module tries to handle loops caused by an exception's __cause__ or __context__ attributes when printing tracebacks. To do so, it adds already seen exceptions to a set. Unfortunately, it doesn't handle unhashable exceptions: >>> class E(Exception): __hash__ = None
...
>>> traceback.print_exception(E, E(), None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/traceback.py", line 100, in print_exception
type(value), value, tb, limit=limit).format(chain=chain):
File "/usr/lib/python3.5/traceback.py", line 439, in __init__
_seen.add(exc_value)
TypeError: unhashable type: 'E' CPython's internal exception printing pretty much does the same, except it ignores any exception while operating on the seen set (see https://hg.python.org/cpython/file/8ee4ed577c03/Python/pythonrun.c#l813 ff). Attached is a patch that makes the traceback module ignore TypeErrors while operating on the seen set. It also adds a (minimal) test. |
Thanks for the patch. Out of curiosity, how did you get an unhashable exception? Is there a way to reproduce this without creating a custom exception and set __hash__ to None? In other words, what's your use case? |
It was reported as bug to a project that uses the traceback module (bpython/bpython#651). Parsley (https://pypi.python.org/pypi/Parsley) is at least one library that uses unhashable exceptions, although I guess it's by accident: it defines __eq__, but not __hash__ and that makes the exception unhashable under Python 3. |
I ran into this bug through Thrift-generated exception classes (also reported there as https://issues.apache.org/jira/browse/THRIFT-4002). I've added a few potential solutions:
I prefer bpo-28603-list.patch. |
There is no bpo-28603-listset.patch. |
There are now several patches for this problem, which also affects me. What are the next steps to get this resolved? |
I also encountered this issue, and I'd like to further note that it is extraordinarily difficult to debug without access to stderr. If your logging facility uses the traceback module then it will be unable to record both the original (unhashable) exception, and the TypeError that it triggers (since the original exception is its __context__). The effect is that execution of a thread appears to simply stop without rhyme or reason (in fact it is still executing, but that fact is not apparent from the logs). I'm attaching a short reproducer that demonstrates this effect. I believe the trade-offs inherent in the attached patches (either stopping the chain at an unhashable exception, or using a less-efficient data structure) can be avoided by comparing for identity rather than equality. The purpose of the check is to avoid an infinite loop; whether the objects compare as equal is something that is up to the author of the class, and has no relevance here except insofar as that objects ought to compare equal to themselves. I submitted a pull request (bpo-4014) with an implementation of that. |
As I understand it, the patch amounts to ignoring any custom __eq__ and __hash__ on an Exception class when printing tracebacks and, in effect, using the default id-based versions inherited from object, as is being assumed. This seems right to me in this context. |
Thank you for your contribution Zane. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: