@@ -28,8 +28,9 @@ def test_inheritance(self):
2828 except TypeError :
2929 pass
3030
31- inheritance_tree = open (os .path .join (os .path .split (__file__ )[0 ],
32- 'exception_hierarchy.txt' ))
31+ inheritance_tree = open (
32+ os .path .join (os .path .split (__file__ )[0 ], 'exception_hierarchy.txt' ),
33+ encoding = "utf-8" )
3334 try :
3435 superclass_name = inheritance_tree .readline ().rstrip ()
3536 try :
@@ -43,7 +44,7 @@ def test_inheritance(self):
4344 last_depth = 0
4445 for exc_line in inheritance_tree :
4546 exc_line = exc_line .rstrip ()
46- depth = exc_line .rindex ('- ' )
47+ depth = exc_line .rindex ('─ ' )
4748 exc_name = exc_line [depth + 2 :] # Slice past space
4849 if '(' in exc_name :
4950 paren_index = exc_name .index ('(' )
@@ -117,6 +118,31 @@ def test_interface_no_arg(self):
117118 [repr (exc ), exc .__class__ .__name__ + '()' ])
118119 self .interface_test_driver (results )
119120
121+ def test_setstate_refcount_no_crash (self ):
122+ # gh-97591: Acquire strong reference before calling tp_hash slot
123+ # in PyObject_SetAttr.
124+ import gc
125+ d = {}
126+ class HashThisKeyWillClearTheDict (str ):
127+ def __hash__ (self ) -> int :
128+ d .clear ()
129+ return super ().__hash__ ()
130+ class Value (str ):
131+ pass
132+ exc = Exception ()
133+
134+ d [HashThisKeyWillClearTheDict ()] = Value () # refcount of Value() is 1 now
135+
136+ # Exception.__setstate__ should aquire a strong reference of key and
137+ # value in the dict. Otherwise, Value()'s refcount would go below
138+ # zero in the tp_hash call in PyObject_SetAttr(), and it would cause
139+ # crash in GC.
140+ exc .__setstate__ (d ) # __hash__() is called again here, clearing the dict.
141+
142+ # This GC would crash if the refcount of Value() goes below zero.
143+ gc .collect ()
144+
145+
120146class UsageTests (unittest .TestCase ):
121147
122148 """Test usage of exceptions"""
0 commit comments