tests for anything relating to exception objects themselves e g inheritance hierarchy def testbuiltinsnewstyleself self asserttrueissubclassexception object def verifyinstanceinterfaceself ins for attr in args str repr self asserttruehasattrins attr s missing s attribute ins class name attr def testinheritanceself make sure the inheritance hierarchy matches the documentation excset set for object in builtins dict values try if issubclassobject baseexception excset addobject name except typeerror pass inheritancetree open os path joinos path splitfile0 exceptionhierarchy txt encodingutf8 try superclassname inheritancetree readline rstrip try lastexc getattrbuiltins superclassname except attributeerror self failbase class s not a builtin superclassname self assertinsuperclassname excset s not found superclassname excset discardsuperclassname superclasses loop will insert base exception lastdepth 0 for excline in inheritancetree excline excline rstrip depth excline rindex excname exclinedepth2 slice past space if in excname parenindex excname index platformname excnameparenindex1 1 excname excname parenindex1 slice off space if platformsystem platformname excset discardexcname continue if in excname leftbracket excname index excname excname leftbracket1 cover space try exc getattrbuiltins excname except attributeerror self fails not a builtin exception excname if lastdepth depth superclasses appendlastdepth lastexc elif lastdepth depth while superclasses10 depth superclasses pop self asserttrueissubclassexc superclasses11 s is not a subclass of s exc name superclasses11 name try some exceptions require arguments just skip them self verifyinstanceinterfaceexc except typeerror pass self assertinexcname excset excset discardexcname lastexc exc lastdepth depth finally inheritancetree close self assertequallenexcset 0 s not accounted for excset interfacetests length args str repr def interfacetestdriverself results for testname given expected in zipself interfacetests results self assertequalgiven expected s s s testname given expected def testinterfacesingleargself make sure interface works properly when given a single argument arg spam exc exceptionarg results lenexc args 1 exc args0 arg strexc strarg reprexc sr exc class name arg self interfacetestdriverresults def testinterfacemultiargself make sure interface correct when multiple arguments given argcount 3 args tuplerangeargcount exc exceptionargs results lenexc args argcount exc args args strexc strargs reprexc exc class name reprexc args self interfacetestdriverresults def testinterfacenoargself make sure that with no args that interface is correct exc exception results lenexc args 0 exc args tuple strexc reprexc exc class name self interfacetestdriverresults def testsetstaterefcountnocrashself gh97591 acquire strong reference before calling tphash slot in pyobjectsetattr import gc d class hashthiskeywillclearthedictstr def hashself int d clear return super hash class valuestr pass exc exception dhashthiskeywillclearthedict value refcount of value is 1 now exception setstate should aquire a strong reference of key and value in the dict otherwise value s refcount would go below zero in the tphash call in pyobjectsetattr and it would cause crash in gc exc setstated hash is called again here clearing the dict this gc would crash if the refcount of value goes below zero gc collect class usagetestsunittest testcase make sure that raising object triggers a typeerror try raise object except typeerror return what is expected self failtypeerror expected for raising s typeobject def catchfailsself object you cannot raise a newstyle class that does not inherit from baseexception the ability was not possible until baseexception s introduction so no need to support newstyle objects that do not inherit from it raising a string raises typeerror trying to catch an object that does not inherit from baseexception is not allowed catching an instance of a baseexception subclass won t work catching a string is bad tests for anything relating to exception objects themselves e g inheritance hierarchy make sure the inheritance hierarchy matches the documentation loop will insert base exception slice past space slice off space cover space some exceptions require arguments just skip them make sure interface works properly when given a single argument make sure interface correct when multiple arguments given make sure that with no args that interface is correct gh 97591 acquire strong reference before calling tp_hash slot in pyobject_setattr refcount of value is 1 now exception __setstate__ should aquire a strong reference of key and value in the dict otherwise value s refcount would go below zero in the tp_hash call in pyobject_setattr and it would cause crash in gc __hash__ is called again here clearing the dict this gc would crash if the refcount of value goes below zero test usage of exceptions make sure that raising object_ triggers a typeerror what is expected catching object_ should raise a typeerror you cannot raise a new style class that does not inherit from baseexception the ability was not possible until baseexception s introduction so no need to support new style objects that do not inherit from it raising a string raises typeerror trying to catch an object that does not inherit from baseexception is not allowed catching an instance of a baseexception subclass won t work catching a string is bad
import unittest
import builtins
import os
from platform import system as platform_system


class ExceptionClassTests(unittest.TestCase):

    

    def test_builtins_new_style(self):
        self.assertTrue(issubclass(Exception, object))

    def verify_instance_interface(self, ins):
        for attr in ("args", "__str__", "__repr__"):
            self.assertTrue(hasattr(ins, attr),
                    "%s missing %s attribute" %
                        (ins.__class__.__name__, attr))

    def test_inheritance(self):
        
        exc_set = set()
        for object_ in builtins.__dict__.values():
            try:
                if issubclass(object_, BaseException):
                    exc_set.add(object_.__name__)
            except TypeError:
                pass

        inheritance_tree = open(
                os.path.join(os.path.split(__file__)[0], 'exception_hierarchy.txt'),
                encoding="utf-8")
        try:
            superclass_name = inheritance_tree.readline().rstrip()
            try:
                last_exc = getattr(builtins, superclass_name)
            except AttributeError:
                self.fail("base class %s not a built-in" % superclass_name)
            self.assertIn(superclass_name, exc_set,
                          '%s not found' % superclass_name)
            exc_set.discard(superclass_name)
            superclasses = []  
            last_depth = 0
            for exc_line in inheritance_tree:
                exc_line = exc_line.rstrip()
                depth = exc_line.rindex('─')
                exc_name = exc_line[depth+2:]  
                if '(' in exc_name:
                    paren_index = exc_name.index('(')
                    platform_name = exc_name[paren_index+1:-1]
                    exc_name = exc_name[:paren_index-1]  
                    if platform_system() != platform_name:
                        exc_set.discard(exc_name)
                        continue
                if '[' in exc_name:
                    left_bracket = exc_name.index('[')
                    exc_name = exc_name[:left_bracket-1]  
                try:
                    exc = getattr(builtins, exc_name)
                except AttributeError:
                    self.fail("%s not a built-in exception" % exc_name)
                if last_depth < depth:
                    superclasses.append((last_depth, last_exc))
                elif last_depth > depth:
                    while superclasses[-1][0] >= depth:
                        superclasses.pop()
                self.assertTrue(issubclass(exc, superclasses[-1][1]),
                "%s is not a subclass of %s" % (exc.__name__,
                    superclasses[-1][1].__name__))
                try:  
                    self.verify_instance_interface(exc())
                except TypeError:
                    pass
                self.assertIn(exc_name, exc_set)
                exc_set.discard(exc_name)
                last_exc = exc
                last_depth = depth
        finally:
            inheritance_tree.close()
        self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set)

    interface_tests = ("length", "args", "str", "repr")

    def interface_test_driver(self, results):
        for test_name, (given, expected) in zip(self.interface_tests, results):
            self.assertEqual(given, expected, "%s: %s != %s" % (test_name,
                given, expected))

    def test_interface_single_arg(self):
        
        arg = "spam"
        exc = Exception(arg)
        results = ([len(exc.args), 1], [exc.args[0], arg],
                   [str(exc), str(arg)],
            [repr(exc), '%s(%r)' % (exc.__class__.__name__, arg)])
        self.interface_test_driver(results)

    def test_interface_multi_arg(self):
        
        arg_count = 3
        args = tuple(range(arg_count))
        exc = Exception(*args)
        results = ([len(exc.args), arg_count], [exc.args, args],
                [str(exc), str(args)],
                [repr(exc), exc.__class__.__name__ + repr(exc.args)])
        self.interface_test_driver(results)

    def test_interface_no_arg(self):
        
        exc = Exception()
        results = ([len(exc.args), 0], [exc.args, tuple()],
                [str(exc), ''],
                [repr(exc), exc.__class__.__name__ + '()'])
        self.interface_test_driver(results)

    def test_setstate_refcount_no_crash(self):
        
        
        import gc
        d = {}
        class HashThisKeyWillClearTheDict(str):
            def __hash__(self) -> int:
                d.clear()
                return super().__hash__()
        class Value(str):
            pass
        exc = Exception()

        d[HashThisKeyWillClearTheDict()] = Value()  

        
        
        
        
        exc.__setstate__(d)  

        
        gc.collect()


class UsageTests(unittest.TestCase):

    

    def raise_fails(self, object_):
        
        try:
            raise object_
        except TypeError:
            return  
        self.fail("TypeError expected for raising %s" % type(object_))

    def catch_fails(self, object_):
        
        try:
            try:
                raise Exception
            except object_:
                pass
        except TypeError:
            pass
        except Exception:
            self.fail("TypeError expected when catching %s" % type(object_))

        try:
            try:
                raise Exception
            except (object_,):
                pass
        except TypeError:
            return
        except Exception:
            self.fail("TypeError expected when catching %s as specified in a "
                        "tuple" % type(object_))

    def test_raise_new_style_non_exception(self):
        
        
        
        
        class NewStyleClass(object):
            pass
        self.raise_fails(NewStyleClass)
        self.raise_fails(NewStyleClass())

    def test_raise_string(self):
        
        self.raise_fails("spam")

    def test_catch_non_BaseException(self):
        
        
        class NonBaseException(object):
            pass
        self.catch_fails(NonBaseException)
        self.catch_fails(NonBaseException())

    def test_catch_BaseException_instance(self):
        
        self.catch_fails(BaseException())

    def test_catch_string(self):
        
        self.catch_fails("spam")


if __name__ == '__main__':
    unittest.main()