diff options
Diffstat (limited to 'Lib/test/support/__init__.py')
-rw-r--r-- | Lib/test/support/__init__.py | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index d6ed2215f3..174e0456dc 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3040,6 +3040,18 @@ class catch_unraisable_exception: """ Context manager catching unraisable exception using sys.unraisablehook. + If the *object* attribute of the unraisable hook is set and the object is + being finalized, the object is resurrected because the context manager + stores a strong reference to it (cm.unraisable.object). + + Storing the exception value (cm.unraisable.exc_value) creates a reference + cycle. The reference cycle is broken explicitly when the context manager + exits. + + Exiting the context manager clears the stored unraisable exception. It can + trigger a new unraisable exception (ex: the resurrected object is finalized + again and raises the same exception): it is silently ignored in this case. + Usage: with support.catch_unraisable_exception() as cm: @@ -3058,6 +3070,8 @@ class catch_unraisable_exception: self._old_hook = None def _hook(self, unraisable): + # Storing unraisable.object can resurrect an object which is being + # finalized. Storing unraisable.exc_value creates a reference cycle. self.unraisable = unraisable def __enter__(self): @@ -3066,6 +3080,10 @@ class catch_unraisable_exception: return self def __exit__(self, *exc_info): - # Clear the unraisable exception to explicitly break a reference cycle - del self.unraisable + # Clear the unraisable exception to explicitly break a reference cycle. + # It can call _hook() again: ignore the new unraisable exception in + # this case. + self.unraisable = None + sys.unraisablehook = self._old_hook + del self.unraisable |