Commit 212646ca authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

bpo-37261: Document sys.unraisablehook corner cases (GH-14059)

Document reference cycle and resurrected objects issues in
sys.unraisablehook() and threading.excepthook() documentation.

Fix test.support.catch_unraisable_exception(): __exit__() no longer
ignores unraisable exceptions.

Fix test_io test_writer_close_error_on_close(): use a second
catch_unraisable_exception() to catch the BufferedWriter unraisable
exception.
parent 9765efcb
...@@ -1514,13 +1514,21 @@ always available. ...@@ -1514,13 +1514,21 @@ always available.
* *err_msg*: Error message, can be ``None``. * *err_msg*: Error message, can be ``None``.
* *object*: Object causing the exception, can be ``None``. * *object*: Object causing the exception, can be ``None``.
:func:`sys.unraisablehook` can be overridden to control how unraisable
exceptions are handled.
The default hook formats *err_msg* and *object* as: The default hook formats *err_msg* and *object* as:
``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message ``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message
if *err_msg* is ``None``. if *err_msg* is ``None``.
:func:`sys.unraisablehook` can be overridden to control how unraisable
exceptions are handled.
Storing *exc_value* using a custom hook can create a reference cycle. It
should be cleared explicitly to break the reference cycle when the
exception is no longer needed.
Storing *object* using a custom hook can resurrect it if it is set to an
object which is being finalized. Avoid storing *object* after the custom
hook completes to avoid resurrecting objects.
See also :func:`excepthook` which handles uncaught exceptions. See also :func:`excepthook` which handles uncaught exceptions.
.. versionadded:: 3.8 .. versionadded:: 3.8
......
...@@ -1086,17 +1086,13 @@ The :mod:`test.support` module defines the following functions: ...@@ -1086,17 +1086,13 @@ The :mod:`test.support` module defines the following functions:
Context manager catching unraisable exception using Context manager catching unraisable exception using
:func:`sys.unraisablehook`. :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 Storing the exception value (``cm.unraisable.exc_value``) creates a
reference cycle. The reference cycle is broken explicitly when the context reference cycle. The reference cycle is broken explicitly when the context
manager exits. manager exits.
Exiting the context manager clears the stored unraisable exception. It can Storing the object (``cm.unraisable.object``) can resurrect it if it is set
trigger a new unraisable exception (ex: the resurrected object is finalized to an object which is being finalized. Exiting the context manager clears
again and raises the same exception): it is silently ignored in this case. the stored object.
Usage:: Usage::
......
...@@ -58,6 +58,14 @@ This module defines the following functions: ...@@ -58,6 +58,14 @@ This module defines the following functions:
:func:`threading.excepthook` can be overridden to control how uncaught :func:`threading.excepthook` can be overridden to control how uncaught
exceptions raised by :func:`Thread.run` are handled. exceptions raised by :func:`Thread.run` are handled.
Storing *exc_value* using a custom hook can create a reference cycle. It
should be cleared explicitly to break the reference cycle when the
exception is no longer needed.
Storing *object* using a custom hook can resurrect it if it is set to an
object which is being finalized. Avoid storing *object* after the custom
hook completes to avoid resurrecting objects.
.. seealso:: .. seealso::
:func:`sys.excepthook` handles uncaught exceptions. :func:`sys.excepthook` handles uncaught exceptions.
......
...@@ -3040,17 +3040,13 @@ class catch_unraisable_exception: ...@@ -3040,17 +3040,13 @@ class catch_unraisable_exception:
""" """
Context manager catching unraisable exception using sys.unraisablehook. 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 Storing the exception value (cm.unraisable.exc_value) creates a reference
cycle. The reference cycle is broken explicitly when the context manager cycle. The reference cycle is broken explicitly when the context manager
exits. exits.
Exiting the context manager clears the stored unraisable exception. It can Storing the object (cm.unraisable.object) can resurrect it if it is set to
trigger a new unraisable exception (ex: the resurrected object is finalized an object which is being finalized. Exiting the context manager clears the
again and raises the same exception): it is silently ignored in this case. stored object.
Usage: Usage:
...@@ -3080,10 +3076,5 @@ class catch_unraisable_exception: ...@@ -3080,10 +3076,5 @@ class catch_unraisable_exception:
return self return self
def __exit__(self, *exc_info): def __exit__(self, *exc_info):
# 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 sys.unraisablehook = self._old_hook
del self.unraisable del self.unraisable
...@@ -2072,9 +2072,13 @@ class BufferedRWPairTest(unittest.TestCase): ...@@ -2072,9 +2072,13 @@ class BufferedRWPairTest(unittest.TestCase):
writer.close = lambda: None writer.close = lambda: None
writer = None writer = None
# Ignore BufferedWriter (of the BufferedRWPair) unraisable exception
with support.catch_unraisable_exception():
# Ignore BufferedRWPair unraisable exception
with support.catch_unraisable_exception(): with support.catch_unraisable_exception():
pair = None pair = None
support.gc_collect() support.gc_collect()
support.gc_collect()
def test_reader_writer_close_error_on_close(self): def test_reader_writer_close_error_on_close(self):
def reader_close(): def reader_close():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment