diff options
author | Victor Stinner <vstinner@redhat.com> | 2019-04-12 15:06:47 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-12 15:06:47 (GMT) |
commit | 44235041f3b957abd36d3792450c3540aa09e120 (patch) | |
tree | f5a07219352053068a742e62bd75447c22e50aa5 | |
parent | 9d949f7796da612f1b588d18c6f041376992a9fc (diff) | |
download | cpython-44235041f3b957abd36d3792450c3540aa09e120.zip cpython-44235041f3b957abd36d3792450c3540aa09e120.tar.gz cpython-44235041f3b957abd36d3792450c3540aa09e120.tar.bz2 |
bpo-18748: io.IOBase destructor now logs close() errors in dev mode (GH-12786)
In development mode (-X dev) and in debug build, the io.IOBase
destructor now logs close() exceptions. These exceptions are silent
by default in release mode.
-rw-r--r-- | Doc/using/cmdline.rst | 4 | ||||
-rw-r--r-- | Lib/test/test_io.py | 31 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2019-04-11-16-09-42.bpo-18748.QW7upB.rst | 3 | ||||
-rw-r--r-- | Modules/_io/iobase.c | 18 |
4 files changed, 46 insertions, 10 deletions
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index bd3cdef..0574336 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -437,6 +437,7 @@ Miscellaneous options * Enable :ref:`asyncio debug mode <asyncio-debug-mode>`. * Set the :attr:`~sys.flags.dev_mode` attribute of :attr:`sys.flags` to ``True`` + * :class:`io.IOBase` destructor logs ``close()`` exceptions. * ``-X utf8`` enables UTF-8 mode for operating system interfaces, overriding the default locale-aware mode. ``-X utf8=0`` explicitly disables UTF-8 @@ -465,7 +466,8 @@ Miscellaneous options The ``-X importtime``, ``-X dev`` and ``-X utf8`` options. .. versionadded:: 3.8 - The ``-X pycache_prefix`` option. + The ``-X pycache_prefix`` option. The ``-X dev`` option now logs + ``close()`` exceptions in :class:`io.IOBase` destructor. Options you shouldn't use diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index d245c5d..811a446 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -67,6 +67,11 @@ MEMORY_SANITIZER = ( '--with-memory-sanitizer' in _config_args ) +# Does io.IOBase logs unhandled exceptions on calling close()? +# They are silenced by default in release build. +DESTRUCTOR_LOG_ERRORS = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode) + + def _default_chunk_size(): """Get the default TextIOWrapper chunk size""" with open(__file__, "r", encoding="latin-1") as f: @@ -1097,9 +1102,16 @@ class CommonBufferedTests: s = s.getvalue().strip() if s: # The destructor *may* have printed an unraisable error, check it - self.assertEqual(len(s.splitlines()), 1) - self.assertTrue(s.startswith("Exception OSError: "), s) - self.assertTrue(s.endswith(" ignored"), s) + lines = s.splitlines() + if DESTRUCTOR_LOG_ERRORS: + self.assertEqual(len(lines), 5) + self.assertTrue(lines[0].startswith("Exception ignored in: "), lines) + self.assertEqual(lines[1], "Traceback (most recent call last):", lines) + self.assertEqual(lines[4], 'OSError:', lines) + else: + self.assertEqual(len(lines), 1) + self.assertTrue(lines[-1].startswith("Exception OSError: "), lines) + self.assertTrue(lines[-1].endswith(" ignored"), lines) def test_repr(self): raw = self.MockRawIO() @@ -2833,9 +2845,16 @@ class TextIOWrapperTest(unittest.TestCase): s = s.getvalue().strip() if s: # The destructor *may* have printed an unraisable error, check it - self.assertEqual(len(s.splitlines()), 1) - self.assertTrue(s.startswith("Exception OSError: "), s) - self.assertTrue(s.endswith(" ignored"), s) + lines = s.splitlines() + if DESTRUCTOR_LOG_ERRORS: + self.assertEqual(len(lines), 5) + self.assertTrue(lines[0].startswith("Exception ignored in: "), lines) + self.assertEqual(lines[1], "Traceback (most recent call last):", lines) + self.assertEqual(lines[4], 'OSError:', lines) + else: + self.assertEqual(len(lines), 1) + self.assertTrue(lines[-1].startswith("Exception OSError: "), lines) + self.assertTrue(lines[-1].endswith(" ignored"), lines) # Systematic tests of the text I/O API diff --git a/Misc/NEWS.d/next/Library/2019-04-11-16-09-42.bpo-18748.QW7upB.rst b/Misc/NEWS.d/next/Library/2019-04-11-16-09-42.bpo-18748.QW7upB.rst new file mode 100644 index 0000000..2e0cef8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-11-16-09-42.bpo-18748.QW7upB.rst @@ -0,0 +1,3 @@ +In development mode (:option:`-X` ``dev``) and in debug build, the +:class:`io.IOBase` destructor now logs ``close()`` exceptions. These exceptions +are silent by default in release mode. diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 9b063cd..3a8f16a 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -286,10 +286,22 @@ iobase_finalize(PyObject *self) /* Silencing I/O errors is bad, but printing spurious tracebacks is equally as bad, and potentially more frequent (because of shutdown issues). */ - if (res == NULL) - PyErr_Clear(); - else + if (res == NULL) { +#ifndef Py_DEBUG + const _PyCoreConfig *config = &_PyInterpreterState_GET_UNSAFE()->core_config; + if (config->dev_mode) { + PyErr_WriteUnraisable(self); + } + else { + PyErr_Clear(); + } +#else + PyErr_WriteUnraisable(self); +#endif + } + else { Py_DECREF(res); + } } /* Restore the saved exception. */ |