]> granicus.if.org Git - python/commitdiff
bpo-37261: Document sys.unraisablehook corner cases (GH-14059)
authorVictor Stinner <vstinner@redhat.com>
Fri, 14 Jun 2019 16:03:22 +0000 (18:03 +0200)
committerGitHub <noreply@github.com>
Fri, 14 Jun 2019 16:03:22 +0000 (18:03 +0200)
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.

Doc/library/sys.rst
Doc/library/test.rst
Doc/library/threading.rst
Lib/test/support/__init__.py
Lib/test/test_io.py

index 5bde6870717c9035fca581d2fec78069c9cb94c5..817c3f1e56f91fde6f5c15b9542da3747cea089c 100644 (file)
@@ -1514,13 +1514,21 @@ always available.
    * *err_msg*: Error message, 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:
    ``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message
    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.
 
    .. versionadded:: 3.8
index 0a98c882465d8536de13d31504847ee727c449cf..920c018084b81ca16a9e2dca762b727212f3c1e8 100644 (file)
@@ -1086,17 +1086,13 @@ 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.
+   Storing the object (``cm.unraisable.object``) can resurrect it if it is set
+   to an object which is being finalized. Exiting the context manager clears
+   the stored object.
 
    Usage::
 
index 2907b65f5bca42ffc7fcf2004a0472173522dc9d..9ffd5cd581793ae65d8f40367d55925a67db2350 100644 (file)
@@ -58,6 +58,14 @@ This module defines the following functions:
    :func:`threading.excepthook` can be overridden to control how uncaught
    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::
       :func:`sys.excepthook` handles uncaught exceptions.
 
index 174e0456dc7104146ecd455d7859d7e359d5a196..7c0efc783edb2fbf24880cd94498b8e30a2dff21 100644 (file)
@@ -3040,17 +3040,13 @@ 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.
+    Storing the object (cm.unraisable.object) can resurrect it if it is set to
+    an object which is being finalized. Exiting the context manager clears the
+    stored object.
 
     Usage:
 
@@ -3080,10 +3076,5 @@ class catch_unraisable_exception:
         return self
 
     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
         del self.unraisable
index 55686d743983556a48e3fe29abe301a6bd06d0d8..fc474c99053df2356cd273a3b28e8016663b21c0 100644 (file)
@@ -2072,8 +2072,12 @@ class BufferedRWPairTest(unittest.TestCase):
         writer.close = lambda: None
         writer = None
 
+        # Ignore BufferedWriter (of the BufferedRWPair) unraisable exception
         with support.catch_unraisable_exception():
-            pair = None
+            # Ignore BufferedRWPair unraisable exception
+            with support.catch_unraisable_exception():
+                pair = None
+                support.gc_collect()
             support.gc_collect()
 
     def test_reader_writer_close_error_on_close(self):