77import weakref
88import errno
99
10- from test .support import (TESTFN , captured_output , check_impl_detail ,
10+ from test .support import (TESTFN , captured_stderr , check_impl_detail ,
1111 check_warnings , cpython_only , gc_collect , run_unittest ,
1212 no_tracing , unlink , import_module )
1313
@@ -20,6 +20,10 @@ class SlottedNaiveException(Exception):
2020 def __init__ (self , x ):
2121 self .x = x
2222
23+ class BrokenStrException (Exception ):
24+ def __str__ (self ):
25+ raise Exception ("str() is broken" )
26+
2327# XXX This is not really enough, each *operation* should be tested!
2428
2529class ExceptionTests (unittest .TestCase ):
@@ -882,7 +886,7 @@ def __subclasscheck__(cls, subclass):
882886 class MyException (Exception , metaclass = Meta ):
883887 pass
884888
885- with captured_output ( "stderr" ) as stderr :
889+ with captured_stderr ( ) as stderr :
886890 try :
887891 raise KeyError ()
888892 except MyException as e :
@@ -1011,6 +1015,66 @@ def test_errno_ENOTDIR(self):
10111015 os .listdir (__file__ )
10121016 self .assertEqual (cm .exception .errno , errno .ENOTDIR , cm .exception )
10131017
1018+ def test_unraisable (self ):
1019+ # Issue #22836: PyErr_WriteUnraisable() should give sensible reports
1020+ class BrokenDel :
1021+ def __del__ (self ):
1022+ exc = ValueError ("del is broken" )
1023+ # The following line is included in the traceback report:
1024+ raise exc
1025+
1026+ class BrokenRepr (BrokenDel ):
1027+ def __repr__ (self ):
1028+ raise AttributeError ("repr() is broken" )
1029+
1030+ class BrokenExceptionDel :
1031+ def __del__ (self ):
1032+ exc = BrokenStrException ()
1033+ # The following line is included in the traceback report:
1034+ raise exc
1035+
1036+ for test_class in (BrokenDel , BrokenRepr , BrokenExceptionDel ):
1037+ with self .subTest (test_class ):
1038+ obj = test_class ()
1039+ with captured_stderr () as stderr :
1040+ del obj
1041+ report = stderr .getvalue ()
1042+ self .assertIn ("Exception ignored" , report )
1043+ if test_class is BrokenRepr :
1044+ self .assertIn ("<object repr() failed>" , report )
1045+ else :
1046+ self .assertIn (test_class .__del__ .__qualname__ , report )
1047+ self .assertIn ("test_exceptions.py" , report )
1048+ self .assertIn ("raise exc" , report )
1049+ if test_class is BrokenExceptionDel :
1050+ self .assertIn ("BrokenStrException" , report )
1051+ self .assertIn ("<exception str() failed>" , report )
1052+ else :
1053+ self .assertIn ("ValueError" , report )
1054+ self .assertIn ("del is broken" , report )
1055+ self .assertTrue (report .endswith ("\n " ))
1056+
1057+ def test_unhandled (self ):
1058+ # Check for sensible reporting of unhandled exceptions
1059+ for exc_type in (ValueError , BrokenStrException ):
1060+ with self .subTest (exc_type ):
1061+ try :
1062+ exc = exc_type ("test message" )
1063+ # The following line is included in the traceback report:
1064+ raise exc
1065+ except exc_type :
1066+ with captured_stderr () as stderr :
1067+ sys .__excepthook__ (* sys .exc_info ())
1068+ report = stderr .getvalue ()
1069+ self .assertIn ("test_exceptions.py" , report )
1070+ self .assertIn ("raise exc" , report )
1071+ self .assertIn (exc_type .__name__ , report )
1072+ if exc_type is BrokenStrException :
1073+ self .assertIn ("<exception str() failed>" , report )
1074+ else :
1075+ self .assertIn ("test message" , report )
1076+ self .assertTrue (report .endswith ("\n " ))
1077+
10141078
10151079class ImportErrorTests (unittest .TestCase ):
10161080
0 commit comments