bpo-26789: Fix logging.FileHandler._open() at exit (GH-23053)

The logging.FileHandler class now keeps a reference to the builtin
open() function to be able to open or reopen the file during Python
finalization.

Fix errors like:

    Exception ignored in: (...)
    Traceback (most recent call last):
      (...)
      File ".../logging/__init__.py", line 1463, in error
      File ".../logging/__init__.py", line 1577, in _log
      File ".../logging/__init__.py", line 1587, in handle
      File ".../logging/__init__.py", line 1649, in callHandlers
      File ".../logging/__init__.py", line 948, in handle
      File ".../logging/__init__.py", line 1182, in emit
      File ".../logging/__init__.py", line 1171, in _open
    NameError: name 'open' is not defined
This commit is contained in:
Victor Stinner 2020-11-02 23:17:46 +01:00 committed by GitHub
parent 5cf4782a26
commit 45df61fd2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 8 deletions

View File

@ -1148,6 +1148,10 @@ class FileHandler(StreamHandler):
self.encoding = encoding self.encoding = encoding
self.errors = errors self.errors = errors
self.delay = delay self.delay = delay
# bpo-26789: FileHandler keeps a reference to the builtin open()
# function to be able to open or reopen the file during Python
# finalization.
self._builtin_open = open
if delay: if delay:
#We don't open the stream, but we still need to call the #We don't open the stream, but we still need to call the
#Handler constructor to set level, formatter, lock etc. #Handler constructor to set level, formatter, lock etc.
@ -1183,8 +1187,9 @@ class FileHandler(StreamHandler):
Open the current base file with the (original) mode and encoding. Open the current base file with the (original) mode and encoding.
Return the resulting stream. Return the resulting stream.
""" """
return open(self.baseFilename, self.mode, encoding=self.encoding, open_func = self._builtin_open
errors=self.errors) return open_func(self.baseFilename, self.mode,
encoding=self.encoding, errors=self.errors)
def emit(self, record): def emit(self, record):
""" """

View File

@ -4310,8 +4310,8 @@ class ModuleLevelMiscTest(BaseTest):
logging.setLoggerClass(logging.Logger) logging.setLoggerClass(logging.Logger)
def test_logging_at_shutdown(self): def test_logging_at_shutdown(self):
# Issue #20037 # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
code = """if 1: code = textwrap.dedent("""
import logging import logging
class A: class A:
@ -4321,22 +4321,55 @@ class ModuleLevelMiscTest(BaseTest):
except Exception: except Exception:
logging.exception("exception in __del__") logging.exception("exception in __del__")
a = A()""" a = A()
""")
rc, out, err = assert_python_ok("-c", code) rc, out, err = assert_python_ok("-c", code)
err = err.decode() err = err.decode()
self.assertIn("exception in __del__", err) self.assertIn("exception in __del__", err)
self.assertIn("ValueError: some error", err) self.assertIn("ValueError: some error", err)
def test_logging_at_shutdown_open(self):
# bpo-26789: FileHandler keeps a reference to the builtin open()
# function to be able to open or reopen the file during Python
# finalization.
filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, filename)
code = textwrap.dedent(f"""
import builtins
import logging
class A:
def __del__(self):
logging.error("log in __del__")
# basicConfig() opens the file, but logging.shutdown() closes
# it at Python exit. When A.__del__() is called,
# FileHandler._open() must be called again to re-open the file.
logging.basicConfig(filename={filename!r})
a = A()
# Simulate the Python finalization which removes the builtin
# open() function.
del builtins.open
""")
assert_python_ok("-c", code)
with open(filename) as fp:
self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
def test_recursion_error(self): def test_recursion_error(self):
# Issue 36272 # Issue 36272
code = """if 1: code = textwrap.dedent("""
import logging import logging
def rec(): def rec():
logging.error("foo") logging.error("foo")
rec() rec()
rec()""" rec()
""")
rc, out, err = assert_python_failure("-c", code) rc, out, err = assert_python_failure("-c", code)
err = err.decode() err = err.decode()
self.assertNotIn("Cannot recover from stack overflow.", err) self.assertNotIn("Cannot recover from stack overflow.", err)

View File

@ -0,0 +1,4 @@
The :class:`logging.FileHandler` class now keeps a reference to the builtin
:func:`open` function to be able to open or reopen the file during Python
finalization. Fix errors like: ``NameError: name 'open' is not defined``. Patch
by Victor Stinner.

View File

@ -852,7 +852,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
} }
if (f == NULL) { if (f == NULL) {
globals = _PyInterpreterState_GET()->sysdict; globals = tstate->interp->sysdict;
*filename = PyUnicode_FromString("sys"); *filename = PyUnicode_FromString("sys");
*lineno = 1; *lineno = 1;
} }