55import unittest
66import pickle , cPickle
77
8- from test .test_support import (TESTFN , unlink , run_unittest , captured_output ,
8+ from test .test_support import (TESTFN , unlink , run_unittest , captured_stderr ,
99 check_warnings , cpython_only )
1010from test .test_pep352 import ignore_deprecation_warnings
1111
12+ class BrokenStrException (Exception ):
13+ def __str__ (self ):
14+ raise Exception ("str() is broken" )
15+ __repr__ = __str__ # Python 2's PyErr_WriteUnraisable() uses repr()
16+
1217# XXX This is not really enough, each *operation* should be tested!
1318
1419class ExceptionTests (unittest .TestCase ):
@@ -375,7 +380,7 @@ def g():
375380 # The test prints an unraisable recursion error when
376381 # doing "except ValueError", this is because subclass
377382 # checking has recursion checking too.
378- with captured_output ( "stderr" ):
383+ with captured_stderr ( ):
379384 try :
380385 g ()
381386 except RuntimeError :
@@ -448,7 +453,7 @@ class MyException(Exception):
448453 __metaclass__ = Meta
449454 pass
450455
451- with captured_output ( "stderr" ) as stderr :
456+ with captured_stderr ( ) as stderr :
452457 try :
453458 raise KeyError ()
454459 except MyException , e :
@@ -460,7 +465,7 @@ class MyException(Exception):
460465 else :
461466 self .fail ("Should have raised KeyError" )
462467
463- with captured_output ( "stderr" ) as stderr :
468+ with captured_stderr ( ) as stderr :
464469 def g ():
465470 try :
466471 return g ()
@@ -644,6 +649,62 @@ class C(object):
644649 self .assertEqual (error5 .a , 1 )
645650 self .assertEqual (error5 .__doc__ , "" )
646651
652+ def test_unraisable (self ):
653+ # Issue #22836: PyErr_WriteUnraisable() should give sensible reports
654+ class BrokenDel :
655+ def __del__ (self ):
656+ exc = ValueError ("del is broken" )
657+ # In Python 3, the following line would be in the report:
658+ raise exc
659+
660+ class BrokenRepr (BrokenDel ):
661+ def __repr__ (self ):
662+ raise AttributeError ("repr() is broken" )
663+
664+ class BrokenExceptionDel :
665+ def __del__ (self ):
666+ exc = BrokenStrException ()
667+ # In Python 3, the following line would be in the report:
668+ raise exc
669+
670+ for test_class in (BrokenDel , BrokenRepr , BrokenExceptionDel ):
671+ obj = test_class ()
672+ with captured_stderr () as stderr :
673+ del obj
674+ report = stderr .getvalue ()
675+ self .assertRegexpMatches (report , "Exception.* ignored" )
676+ if test_class is BrokenRepr :
677+ self .assertIn ("<object repr() failed>" , report )
678+ else :
679+ self .assertIn ("__del__" , report )
680+ if test_class is BrokenExceptionDel :
681+ self .assertIn ("BrokenStrException" , report )
682+ self .assertIn ("<exception repr() failed>" , report )
683+ else :
684+ self .assertIn ("ValueError" , report )
685+ self .assertIn ("del is broken" , report )
686+ self .assertTrue (report .endswith ("\n " ))
687+
688+ def test_unhandled (self ):
689+ # Check for sensible reporting of unhandled exceptions
690+ for exc_type in (ValueError , BrokenStrException ):
691+ try :
692+ exc = exc_type ("test message" )
693+ # The following line is included in the traceback report:
694+ raise exc
695+ except exc_type :
696+ with captured_stderr () as stderr :
697+ sys .__excepthook__ (* sys .exc_info ())
698+ report = stderr .getvalue ()
699+ self .assertIn ("test_exceptions.py" , report )
700+ self .assertIn ("raise exc" , report )
701+ self .assertIn (exc_type .__name__ , report )
702+ if exc_type is BrokenStrException :
703+ self .assertIn ("<exception str() failed>" , report )
704+ else :
705+ self .assertIn ("test message" , report )
706+ self .assertTrue (report .endswith ("\n " ))
707+
647708
648709def test_main ():
649710 run_unittest (ExceptionTests , TestSameStrAndUnicodeMsg )
0 commit comments