From 5d51cc90bc98b6c96d12a86e8c0753bf4ff42c55 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 30 May 2023 18:42:49 +0200 Subject: [PATCH 1/3] gh-62948: IOBase finalizer logs close() exceptions --- Doc/whatsnew/3.13.rst | 9 +++++++++ Lib/test/test_io.py | 14 ++------------ .../2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst | 4 ++++ Modules/_io/iobase.c | 12 ------------ 4 files changed, 15 insertions(+), 24 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 13b9be1c8ee23f..b32d55f1a61289 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -87,6 +87,15 @@ New Modules Improved Modules ================ +io +-- + +The :class:`io.IOBase` finalizer now logs the ``close()`` method errors with +:data:`sys.excepthook`. Previously, errors were ignored silently by default, +and only logged in :ref:`Python Development Mode ` or on :ref:`Python +built on debug mode `. +(Contributed by Victor Stinner in :gh:`62948`.) + pathlib ------- diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index cc16804fe21829..26ae40d93c84eb 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -66,10 +66,6 @@ def byteslike(*pos, **kw): class EmptyStruct(ctypes.Structure): pass -# Does io.IOBase finalizer log the exception if the close() method fails? -# The exception is ignored silently by default in release build. -IOBASE_EMITS_UNRAISABLE = (support.Py_DEBUG or sys.flags.dev_mode) - def _default_chunk_size(): """Get the default TextIOWrapper chunk size""" @@ -1218,10 +1214,7 @@ def test_error_through_destructor(self): 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) + self.assertEqual(cm.unraisable.exc_type, OSError) def test_repr(self): raw = self.MockRawIO() @@ -3022,10 +3015,7 @@ def test_error_through_destructor(self): with self.assertRaises(AttributeError): self.TextIOWrapper(rawio, encoding="utf-8").xyzzy - if not IOBASE_EMITS_UNRAISABLE: - self.assertIsNone(cm.unraisable) - elif cm.unraisable is not None: - self.assertEqual(cm.unraisable.exc_type, OSError) + self.assertEqual(cm.unraisable.exc_type, OSError) # Systematic tests of the text I/O API diff --git a/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst b/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst new file mode 100644 index 00000000000000..fecdc74e69f23f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst @@ -0,0 +1,4 @@ +The :class:`io.IOBase` finalizer now logs the ``close()`` method errors with +:data:`sys.excepthook`. Previously, errors were ignored silently by default, +and only logged in :ref:`Python Development Mode ` or on +:ref:`Python built on debug mode `. Patch by Victor Stinner. diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index bcb498d9c5b5de..f98e75ce2d1ed3 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -319,20 +319,8 @@ iobase_finalize(PyObject *self) if (PyObject_SetAttr(self, &_Py_ID(_finalizing), Py_True)) PyErr_Clear(); res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(close)); - /* 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) { -#ifndef Py_DEBUG - if (_Py_GetConfig()->dev_mode) { - PyErr_WriteUnraisable(self); - } - else { - PyErr_Clear(); - } -#else PyErr_WriteUnraisable(self); -#endif } else { Py_DECREF(res); From b840a379abdc2e624c87da50c111d3fee2ced74c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 30 May 2023 19:26:02 +0200 Subject: [PATCH 2/3] Update _pyio --- Lib/_pyio.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 7f247ff47c9e61..00c9df31031eda 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -33,11 +33,8 @@ # Rebind for compatibility BlockingIOError = BlockingIOError -# Does io.IOBase finalizer log the exception if the close() method fails? -# The exception is ignored silently by default in release build. -_IOBASE_EMITS_UNRAISABLE = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode) # Does open() check its 'errors' argument? -_CHECK_ERRORS = _IOBASE_EMITS_UNRAISABLE +_CHECK_ERRORS = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode) def text_encoding(encoding, stacklevel=2): @@ -416,18 +413,8 @@ def __del__(self): if closed: return - if _IOBASE_EMITS_UNRAISABLE: - self.close() - else: - # The try/except block is in case this is called at program - # exit time, when it's possible that globals have already been - # deleted, and then the close() call might fail. Since - # there's nothing we can do about such failures and they annoy - # the end users, we suppress the traceback. - try: - self.close() - except: - pass + # Calling close() can fail: log an unraisable exception + self.close() ### Inquiries ### From 533d60da5c5d9a8b47d90c0d25860208600d20b3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 31 May 2023 13:12:20 +0200 Subject: [PATCH 3/3] sys.unraisablehook, not sys.excepthook --- Doc/whatsnew/3.13.rst | 2 +- Lib/_pyio.py | 3 ++- .../next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index b32d55f1a61289..7e7186d3ee3177 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -91,7 +91,7 @@ io -- The :class:`io.IOBase` finalizer now logs the ``close()`` method errors with -:data:`sys.excepthook`. Previously, errors were ignored silently by default, +:data:`sys.unraisablehook`. Previously, errors were ignored silently by default, and only logged in :ref:`Python Development Mode ` or on :ref:`Python built on debug mode `. (Contributed by Victor Stinner in :gh:`62948`.) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 00c9df31031eda..32698abac78d25 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -413,7 +413,8 @@ def __del__(self): if closed: return - # Calling close() can fail: log an unraisable exception + # If close() fails, the caller logs the exception with + # sys.unraisablehook. close() must be called at the end at __del__(). self.close() ### Inquiries ### diff --git a/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst b/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst index fecdc74e69f23f..d6ba989329bce0 100644 --- a/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst +++ b/Misc/NEWS.d/next/Library/2023-05-30-18-45-02.gh-issue-62948.1-5wMR.rst @@ -1,4 +1,4 @@ The :class:`io.IOBase` finalizer now logs the ``close()`` method errors with -:data:`sys.excepthook`. Previously, errors were ignored silently by default, +:data:`sys.unraisablehook`. Previously, errors were ignored silently by default, and only logged in :ref:`Python Development Mode ` or on :ref:`Python built on debug mode `. Patch by Victor Stinner. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy