https://github.com/python/cpython/commit/517e96b9ed9f2b808ce499d01f6b5db5635218e3
commit: 517e96b9ed9f2b808ce499d01f6b5db5635218e3
branch: main
author: Charles Machalow <[email protected]>
committer: picnixz <[email protected]>
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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]