New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-26789: Fix logging.FileHandler._open() at exit #23053
Conversation
I fixed https://bugs.python.org/issue42208 which now makes possible to simply fix https://bugs.python.org/issue26789 with this simple change. |
builtin.open
is not the only issue. If the encoding is not builtin, it should be imported, but the import machinery does not work at this stage.
Also, caching open
creates additional reference loops. And if we use the Python implementation of io.open
it can be not working at all.
I think that it would be better to not open file at this stage.
I view this as a bit of a sticking plaster. When shutting down, by definition, some things will get garbage collected/unbound, and some threaded/async code that uses them can fail because some builtin or other disappears. Logging often gets fingered as the culprit, but IMO any code might try to invoke
If by "this stage" you mean interpreter shutdown, how does any code know we're in that stage? The code that does the |
There is no More radical approach: register an exit function and shutdown logging at interpreter shutdown. |
The logging module registers its logging.shutdown() function in the atexit module. Extract of the unit test of my PR:
|
I consider that the logging module is not the general case. Many applications rely on the logging module for "reliable" logs. I consider that it's important to be able to log as late as possible during the Python shutdown. I'm fine with less important modules failing with "NameError: open". So far, nobody found a solution to make asyncio finalization more reliable. So far, nobody found a solution to finalize all Python objects in the "perfect" order. There are so many corner cases. My PR is a practical solution to a practical issue. In https://bugs.python.org/issue42208 I made the Python finalization a little bit more reliable and deterministic, but it remains full of corner cases obviously. I modified _io.open() (710e826) to not longer call _bootlocale.getpreferredencoding(False) but a new _Py_GetLocaleEncoding() function to fix bpo-26789, but also implementing _Py_GetLocaleEncoding() is C makes is more consistent with Py_DecodeLocale/Py_EncodeLocale, PyUnicode_DecodeFSDefault/PyUnicode_EncodeFSDefault, etc. For example, using I'm aware that calling open() late during the Python finalization is not reliable, but it seems to work in common cases, which is better than the Python 3.9 state. |
I agree, and this is why I approved the PR. I said I view it as a sticking plaster - which I think we are all agreed on - and a sticking plaster is a practical solution. I'm OK with merging this, for that reason, even though it isn't a general or perfect solution. |
Lib/test/test_logging.py
Outdated
# Simulate the Python finalization which sets | ||
# modules attributess to None | ||
builtins.open = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open
is not set to None. It is removed.
Also some names are removed from the sys module: path
, path_hooks
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open is not set to None. It is removed.
Oh, you're right! The finalize_modules() function (*) is a complex beast. It sets module attributes to None, but sys and builtins are treated differently. The operation which "removes" builtin symbols happens in finalize_restore_builtins():
PyDict_Update(interp->builtins, interp->builtins_copy)
A copy of the builtins dict is created before "open" is added to the builtins dict, on purpose.
(*) I recently renamed finalize_modules(), it was previously called _PyImport_Cleanup even if it's unrelated to import!
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
FYI the test_asyncio failed on the 64-bit Windows job of Azure. I'm not interested to investigate this issue. |
I merged my change. It cannot be backported because I didn't backport https://bugs.python.org/issue42208 (and I don't want to backport these finalization enhancements). Since it's a borderline fix, I prefer to only change the master branch. |
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
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:
https://bugs.python.org/issue26789