-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
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
logging.shutdown()
should clear logging._handlerList
if that's what it's called with
#129268
Comments
Would you like to submit a PR? |
Would be good to get a second opinion as to why this is the way it is. @vsajip - you around on here? |
Well, the API documentation for
So, calling
The Since the case you mention involves calling If the handlers throw some other stdlib exception than |
@vsajip - what reason could there be to not |
Similar but not a duplicate: #129143 |
Don't know yet if there is one, but I'm generally not keen on making changes to cover for what might be bad practice in user code. I gave some specific reasons why I thought there might be bad practice, but those haven't been addressed. As for #129143 - it just looks like a documentation inaccuracy, as the docstring matches the behaviour of the code. |
Okay, so I put up the PR, I guess where I'm at:
So, hopefully a quick approval on the PR and we can all move on 😅 |
|
Well, one of us core developers will have to deal with any fallout, and I'm quite happy to commit to doing so for any impact of this change...
Ramifications in the test suite would, I hope, be covered by the runs carried out on the pull request?
The concrete case I encountered was running code that called
What are your concrete concerns around emptying a list of resources that have just been freed, versus letting that list grow in an unbound way? I appreciate that in non-test code, this is unlikely to be a practical problem, but still feels like something that's worth fixing, given the small nature of the fix. |
I was curious about this, so I wrote a short script to see what might be happening: import logging
import tempfile
import unittest
logger = logging.getLogger(__name__)
class TestCase(unittest.TestCase):
def test_handler_leaks(self):
print(f'Initial: {len(logging._handlerList)}: {logging._handlerList}')
for i in range(1000):
stream = tempfile.NamedTemporaryFile(mode='w', prefix=f'test-leak-{i + 1}-', suffix='.log')
h = logging.StreamHandler(stream)
logger.addHandler(h)
logger.error('Disaster!')
logger.removeHandler(h)
h.close()
stream.close() # StreamHandler doesn't close its streams
# print(f'{i + 1}: {len(logging._handlerList)}')
print(f'Final : {len(logging._handlerList)}: {logging._handlerList}')
print(f'Last handler was at: 0x{id(h):x} (for {stream.name})')
if __name__ == '__main__':
unittest.main() This loops 1000 times creating, adding, outputting to, removing and closing handlers. Example output: Initial: 1: [<weakref at 0x7f094c9d00e0; to 'logging._StderrHandler' at 0x7f094c96da90>]
Final : 2: [<weakref at 0x7f094c9d00e0; to 'logging._StderrHandler' at 0x7f094c96da90>, <weakref at 0x7f094b3192b0; to 'logging.StreamHandler' at 0x7f094b2f7ad0>]
Last handler was at: 0x7f094b2f7ad0 (for /tmp/test-leak-1000-k3sfej6z.log)
.
----------------------------------------------------------------------
Ran 1 test in 0.183s
OK So, |
Okay, I'm unclear what to do with this bug or the associated PR; it looks like we don't agree but this isn't a big enough thing to do anything more on it. My suggestion would be to close the PR but leave this issue open in case others want to chime in later. |
I'm still curious as to how/why you're experiencing a potentially unbounded growth in |
It's pretty challenging that the logging framework does this "mildly questionable practice" itself; see this example where ipdb is configuring logging during |
I'm not talking about this specific example, but in general, code internal to an API is not subject to the same constraints as the public API. For example, internal code can take advantage of implementation details, but the users of the public API shouldn't (or else risk breakage if the internals change). |
Bug report
Bug description:
In the wild, I've encountered situations where
logging.shutdown
gets called more than once in a process.The current implementation is called with
logging._handlerList
by default, meaning that handlers can have theirflush()
andclose()
methods called more than once, which can cause bad things to happen if those handlers are not amenable to being closed more than once.If
logging.shutdown()
is processinglogging._handlerList
, it should clear that list once it finished to prevent handlers having theirflush()
andclose()
methods called more than once.CPython versions tested on:
3.12, CPython main branch
Operating systems tested on:
Linux, macOS
Linked PRs
The text was updated successfully, but these errors were encountered: