Skip to content

Commit 6d22cc8

Browse files
authored
bpo-37261: Fix support.catch_unraisable_exception() (GH-14052)
The __exit__() method of test.support.catch_unraisable_exception context manager now ignores unraisable exception raised when clearing self.unraisable attribute.
1 parent 63ab4ba commit 6d22cc8

File tree

3 files changed

+35
-2
lines changed

3 files changed

+35
-2
lines changed

‎Doc/library/test.rst‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,18 @@ The :mod:`test.support` module defines the following functions:
10861086
Context manager catching unraisable exception using
10871087
:func:`sys.unraisablehook`.
10881088

1089+
If the *object* attribute of the unraisable hook is set and the object is
1090+
being finalized, the object is resurrected because the context manager
1091+
stores a strong reference to it (``cm.unraisable.object``).
1092+
1093+
Storing the exception value (``cm.unraisable.exc_value``) creates a
1094+
reference cycle. The reference cycle is broken explicitly when the context
1095+
manager exits.
1096+
1097+
Exiting the context manager clears the stored unraisable exception. It can
1098+
trigger a new unraisable exception (ex: the resurrected object is finalized
1099+
again and raises the same exception): it is silently ignored in this case.
1100+
10891101
Usage::
10901102

10911103
with support.catch_unraisable_exception() as cm:

‎Lib/test/support/__init__.py‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3040,6 +3040,18 @@ class catch_unraisable_exception:
30403040
"""
30413041
Context manager catching unraisable exception using sys.unraisablehook.
30423042
3043+
If the *object* attribute of the unraisable hook is set and the object is
3044+
being finalized, the object is resurrected because the context manager
3045+
stores a strong reference to it (cm.unraisable.object).
3046+
3047+
Storing the exception value (cm.unraisable.exc_value) creates a reference
3048+
cycle. The reference cycle is broken explicitly when the context manager
3049+
exits.
3050+
3051+
Exiting the context manager clears the stored unraisable exception. It can
3052+
trigger a new unraisable exception (ex: the resurrected object is finalized
3053+
again and raises the same exception): it is silently ignored in this case.
3054+
30433055
Usage:
30443056
30453057
with support.catch_unraisable_exception() as cm:
@@ -3058,6 +3070,8 @@ def __init__(self):
30583070
self._old_hook = None
30593071

30603072
def _hook(self, unraisable):
3073+
# Storing unraisable.object can resurrect an object which is being
3074+
# finalized. Storing unraisable.exc_value creates a reference cycle.
30613075
self.unraisable = unraisable
30623076

30633077
def __enter__(self):
@@ -3066,6 +3080,10 @@ def __enter__(self):
30663080
return self
30673081

30683082
def __exit__(self, *exc_info):
3069-
# Clear the unraisable exception to explicitly break a reference cycle
3070-
del self.unraisable
3083+
# Clear the unraisable exception to explicitly break a reference cycle.
3084+
# It can call _hook() again: ignore the new unraisable exception in
3085+
# this case.
3086+
self.unraisable = None
3087+
30713088
sys.unraisablehook = self._old_hook
3089+
del self.unraisable
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`test.support.catch_unraisable_exception`: its __exit__() method
2+
now ignores unraisable exception raised when clearing its ``unraisable``
3+
attribute.

0 commit comments

Comments
 (0)