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

Reply via email to