From 6dbbe748e101a173b4cff8aada41e9313e287e0f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 25 May 2019 00:09:38 +0200 Subject: bpo-36829: Document test.support.catch_unraisable_exception() (GH-13554) catch_unraisable_exception() now also removes its 'unraisable' attribute at the context manager exit. --- Doc/library/test.rst | 20 ++++++++++++++++++++ Lib/test/support/__init__.py | 8 +++++--- Lib/test/test_io.py | 20 ++++++-------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 054521d..b7a2595 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1081,6 +1081,26 @@ The :mod:`test.support` module defines the following functions: :exc:`PermissionError` is raised. +.. function:: catch_unraisable_exception() + + Context manager catching unraisable exception using + :func:`sys.unraisablehook`. + + Usage:: + + with support.catch_unraisable_exception() as cm: + # code creating an "unraisable exception" + ... + + # check the unraisable exception: use cm.unraisable + ... + + # cm.unraisable attribute no longer exists at this point + # (to break a reference cycle) + + .. versionadded:: 3.8 + + .. function:: find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM) Returns an unused port that should be suitable for binding. This is diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2fe9d9d..d6ed221 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3043,12 +3043,14 @@ class catch_unraisable_exception: Usage: with support.catch_unraisable_exception() as cm: + # code creating an "unraisable exception" ... - # check the expected unraisable exception: use cm.unraisable + # check the unraisable exception: use cm.unraisable ... - # cm.unraisable is None here (to break a reference cycle) + # cm.unraisable attribute no longer exists at this point + # (to break a reference cycle) """ def __init__(self): @@ -3065,5 +3067,5 @@ class catch_unraisable_exception: def __exit__(self, *exc_info): # Clear the unraisable exception to explicitly break a reference cycle - self.unraisable = None + del self.unraisable sys.unraisablehook = self._old_hook diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 6f22b35..3a1f5ba 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1098,18 +1098,14 @@ class CommonBufferedTests: # Test that the exception state is not modified by a destructor, # even if close() fails. rawio = self.CloseFailureIO() - try: - with support.catch_unraisable_exception() as cm: - with self.assertRaises(AttributeError): - self.tp(rawio).xyzzy + with support.catch_unraisable_exception() as cm: + with self.assertRaises(AttributeError): + self.tp(rawio).xyzzy if not IOBASE_EMITS_UNRAISABLE: self.assertIsNone(cm.unraisable) elif cm.unraisable is not None: self.assertEqual(cm.unraisable.exc_type, OSError) - finally: - # Explicitly break reference cycle - cm = None def test_repr(self): raw = self.MockRawIO() @@ -2854,18 +2850,14 @@ class TextIOWrapperTest(unittest.TestCase): # Test that the exception state is not modified by a destructor, # even if close() fails. rawio = self.CloseFailureIO() - try: - with support.catch_unraisable_exception() as cm: - with self.assertRaises(AttributeError): - self.TextIOWrapper(rawio).xyzzy + with support.catch_unraisable_exception() as cm: + with self.assertRaises(AttributeError): + self.TextIOWrapper(rawio).xyzzy if not IOBASE_EMITS_UNRAISABLE: self.assertIsNone(cm.unraisable) elif cm.unraisable is not None: self.assertEqual(cm.unraisable.exc_type, OSError) - finally: - # Explicitly break reference cycle - cm = None # Systematic tests of the text I/O API -- cgit v0.12