Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Doc/library/test.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 20 additions & 2 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -3058,6 +3070,8 @@ def __init__(self):
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):
Expand All @@ -3066,6 +3080,10 @@ def __enter__(self):
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`test.support.catch_unraisable_exception`: its __exit__() method
now ignores unraisable exception raised when clearing its ``unraisable``
attribute.