https://github.com/python/cpython/commit/517e96b9ed9f2b808ce499d01f6b5db5635218e3 commit: 517e96b9ed9f2b808ce499d01f6b5db5635218e3 branch: main author: Charles Machalow <csm10...@gmail.com> committer: picnixz <10796600+picn...@users.noreply.github.com> date: 2025-04-12T12:00:04Z summary:
gh-132106: Allow `logging.handlers.QueueListener` to be used as a context manager (#132107) files: A Misc/NEWS.d/next/Library/2025-04-05-02-22-49.gh-issue-132106.XMjhQJ.rst M Doc/howto/logging-cookbook.rst M Doc/library/logging.handlers.rst M Doc/whatsnew/3.14.rst M Lib/logging/handlers.py M Lib/test/test_logging.py diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index f08f45179980f3..661d6c290f6186 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -626,6 +626,19 @@ which, when run, will produce: of each message with the handler's level, and only passes a message to a handler if it's appropriate to do so. +.. versionchanged:: next + The :class:`QueueListener` can be started (and stopped) via the + :keyword:`with` statement. For example: + + .. code-block:: python + + with QueueListener(que, handler) as listener: + # The queue listener automatically starts + # when the 'with' block is entered. + pass + # The queue listener automatically stops once + # the 'with' block is exited. + .. _network-logging: Sending and receiving logging events across a network diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index ffb54591b3563b..b737fe311dfb6e 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -1148,6 +1148,13 @@ possible, while any potentially slow operations (such as sending an email via .. versionchanged:: 3.5 The ``respect_handler_level`` argument was added. + .. versionchanged:: next + :class:`QueueListener` can now be used as a context manager via + :keyword:`with`. When entering the context, the listener is started. When + exiting the context, the listener is stopped. + :meth:`~contextmanager.__enter__` returns the + :class:`QueueListener` object. + .. method:: dequeue(block) Dequeues a record and return it, optionally blocking. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index f7b4cdcda826c0..762d53eeb2df1a 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -812,6 +812,14 @@ linecache (Contributed by Tian Gao in :gh:`131638`.) +logging.handlers +---------------- + +* :class:`logging.handlers.QueueListener` now implements the context + manager protocol, allowing it to be used in a :keyword:`with` statement. + (Contributed by Charles Machalow in :gh:`132106`.) + + mimetypes --------- diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 9abadbf5cdd1df..0571ed2356345a 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1532,6 +1532,19 @@ def __init__(self, queue, *handlers, respect_handler_level=False): self._thread = None self.respect_handler_level = respect_handler_level + def __enter__(self): + """ + For use as a context manager. Starts the listener. + """ + self.start() + return self + + def __exit__(self, *args): + """ + For use as a context manager. Stops the listener. + """ + self.stop() + def dequeue(self, block): """ Dequeue a record and return it, optionally blocking. diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e34fe45fd68e52..11f6b64abe28fb 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4311,8 +4311,6 @@ def test_formatting(self): self.assertEqual(formatted_msg, log_record.msg) self.assertEqual(formatted_msg, log_record.message) - @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'), - 'logging.handlers.QueueListener required for this test') def test_queue_listener(self): handler = TestHandler(support.Matcher()) listener = logging.handlers.QueueListener(self.queue, handler) @@ -4347,8 +4345,17 @@ def test_queue_listener(self): self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6')) handler.close() - @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'), - 'logging.handlers.QueueListener required for this test') + def test_queue_listener_context_manager(self): + handler = TestHandler(support.Matcher()) + with logging.handlers.QueueListener(self.queue, handler) as listener: + self.assertIsInstance(listener, logging.handlers.QueueListener) + self.assertIsNotNone(listener._thread) + self.assertIsNone(listener._thread) + + # doesn't hurt to call stop() more than once. + listener.stop() + self.assertIsNone(listener._thread) + def test_queue_listener_with_StreamHandler(self): # Test that traceback and stack-info only appends once (bpo-34334, bpo-46755). listener = logging.handlers.QueueListener(self.queue, self.root_hdlr) @@ -4363,8 +4370,6 @@ def test_queue_listener_with_StreamHandler(self): self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1) self.assertEqual(self.stream.getvalue().strip().count('Stack'), 1) - @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'), - 'logging.handlers.QueueListener required for this test') def test_queue_listener_with_multiple_handlers(self): # Test that queue handler format doesn't affect other handler formats (bpo-35726). self.que_hdlr.setFormatter(self.root_formatter) diff --git a/Misc/NEWS.d/next/Library/2025-04-05-02-22-49.gh-issue-132106.XMjhQJ.rst b/Misc/NEWS.d/next/Library/2025-04-05-02-22-49.gh-issue-132106.XMjhQJ.rst new file mode 100644 index 00000000000000..376f986adcab83 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-05-02-22-49.gh-issue-132106.XMjhQJ.rst @@ -0,0 +1,2 @@ +:class:`logging.handlers.QueueListener` now implements the context +manager protocol, allowing it to be used in a :keyword:`with` statement. _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com