summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2019-06-13 12:44:54 (GMT)
committerGitHub <noreply@github.com>2019-06-13 12:44:54 (GMT)
commit6d22cc8e90ccb1e1965b1a4bc79456e2cc1e5a3e (patch)
tree5cabdc22f81daacb1ce82a3975580dc3f4261a3c
parent63ab4ba07b492448844940c347787ba30735b7f2 (diff)
downloadcpython-6d22cc8e90ccb1e1965b1a4bc79456e2cc1e5a3e.zip
cpython-6d22cc8e90ccb1e1965b1a4bc79456e2cc1e5a3e.tar.gz
cpython-6d22cc8e90ccb1e1965b1a4bc79456e2cc1e5a3e.tar.bz2
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.
-rw-r--r--Doc/library/test.rst12
-rw-r--r--Lib/test/support/__init__.py22
-rw-r--r--Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst3
3 files changed, 35 insertions, 2 deletions
diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index b7a2595..0a98c88 100644
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -1086,6 +1086,18 @@ The :mod:`test.support` module defines the following functions:
Context manager catching unraisable exception using
:func:`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:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index d6ed221..174e045 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
diff --git a/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst b/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst
new file mode 100644
index 0000000..27ce78a
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2019-06-13-12-19-56.bpo-37261.NuKFVo.rst
@@ -0,0 +1,3 @@
+Fix :func:`test.support.catch_unraisable_exception`: its __exit__() method
+now ignores unraisable exception raised when clearing its ``unraisable``
+attribute.