diff -r 36af3566b67a Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py Thu Nov 03 15:38:58 2016 +0200 +++ b/Lib/test/test_traceback.py Thu Nov 03 21:36:10 2016 +0100 @@ -594,6 +594,12 @@ err = self.get_report(Exception('')) self.assertIn('Exception\n', err) + def test_unhashable_exc(self): + class UnhashableException(Exception): + __hash__ = None + err = self.get_report(UnhashableException()) + self.assertIn('UnhashableException', err) + class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # diff -r 36af3566b67a Lib/traceback.py --- a/Lib/traceback.py Thu Nov 03 15:38:58 2016 +0200 +++ b/Lib/traceback.py Thu Nov 03 21:36:10 2016 +0100 @@ -459,11 +459,22 @@ # Handle loops in __cause__ or __context__. if _seen is None: _seen = set() - _seen.add(exc_value) + try: + _seen.add(exc_value) + except TypeError: + pass + try: + cause_already_seen = exc_value.__cause__ in _seen + except TypeError: + cause_already_seen = True + try: + context_already_seen = exc_value.__context__ in _seen + except TypeError: + context_already_seen = True # Gracefully handle (the way Python 2.4 and earlier did) the case of # being called with no type or value (None, None, None). if (exc_value and exc_value.__cause__ is not None - and exc_value.__cause__ not in _seen): + and not cause_already_seen): cause = TracebackException( type(exc_value.__cause__), exc_value.__cause__, @@ -475,7 +486,7 @@ else: cause = None if (exc_value and exc_value.__context__ is not None - and exc_value.__context__ not in _seen): + and not context_already_seen): context = TracebackException( type(exc_value.__context__), exc_value.__context__,