Commit cafa2efe authored by Victor Stinner's avatar Victor Stinner

logging: don't define QueueListener if Python has no thread support

parent b912c5a0
...@@ -27,7 +27,10 @@ To use, simply 'import logging.handlers' and log away! ...@@ -27,7 +27,10 @@ To use, simply 'import logging.handlers' and log away!
import logging, socket, os, pickle, struct, time, re import logging, socket, os, pickle, struct, time, re
from stat import ST_DEV, ST_INO, ST_MTIME from stat import ST_DEV, ST_INO, ST_MTIME
import queue import queue
import threading try:
import threading
except ImportError:
threading = None
try: try:
import codecs import codecs
...@@ -1218,106 +1221,107 @@ class QueueHandler(logging.Handler): ...@@ -1218,106 +1221,107 @@ class QueueHandler(logging.Handler):
except: except:
self.handleError(record) self.handleError(record)
class QueueListener(object): if threading:
""" class QueueListener(object):
This class implements an internal threaded listener which watches for """
LogRecords being added to a queue, removes them and passes them to a This class implements an internal threaded listener which watches for
list of handlers for processing. LogRecords being added to a queue, removes them and passes them to a
""" list of handlers for processing.
_sentinel = None """
_sentinel = None
def __init__(self, queue, *handlers):
""" def __init__(self, queue, *handlers):
Initialise an instance with the specified queue and """
handlers. Initialise an instance with the specified queue and
""" handlers.
self.queue = queue """
self.handlers = handlers self.queue = queue
self._stop = threading.Event() self.handlers = handlers
self._thread = None self._stop = threading.Event()
self._thread = None
def dequeue(self, block):
""" def dequeue(self, block):
Dequeue a record and return it, optionally blocking. """
Dequeue a record and return it, optionally blocking.
The base implementation uses get. You may want to override this method
if you want to use timeouts or work with custom queue implementations. The base implementation uses get. You may want to override this method
""" if you want to use timeouts or work with custom queue implementations.
return self.queue.get(block) """
return self.queue.get(block)
def start(self):
""" def start(self):
Start the listener. """
Start the listener.
This starts up a background thread to monitor the queue for
LogRecords to process. This starts up a background thread to monitor the queue for
""" LogRecords to process.
self._thread = t = threading.Thread(target=self._monitor) """
t.setDaemon(True) self._thread = t = threading.Thread(target=self._monitor)
t.start() t.setDaemon(True)
t.start()
def prepare(self , record):
""" def prepare(self , record):
Prepare a record for handling. """
Prepare a record for handling.
This method just returns the passed-in record. You may want to
override this method if you need to do any custom marshalling or This method just returns the passed-in record. You may want to
manipulation of the record before passing it to the handlers. override this method if you need to do any custom marshalling or
""" manipulation of the record before passing it to the handlers.
return record """
return record
def handle(self, record):
""" def handle(self, record):
Handle a record. """
Handle a record.
This just loops through the handlers offering them the record
to handle. This just loops through the handlers offering them the record
""" to handle.
record = self.prepare(record) """
for handler in self.handlers: record = self.prepare(record)
handler.handle(record) for handler in self.handlers:
handler.handle(record)
def _monitor(self):
""" def _monitor(self):
Monitor the queue for records, and ask the handler """
to deal with them. Monitor the queue for records, and ask the handler
to deal with them.
This method runs on a separate, internal thread.
The thread will terminate if it sees a sentinel object in the queue. This method runs on a separate, internal thread.
""" The thread will terminate if it sees a sentinel object in the queue.
q = self.queue """
has_task_done = hasattr(q, 'task_done') q = self.queue
while not self._stop.isSet(): has_task_done = hasattr(q, 'task_done')
try: while not self._stop.isSet():
record = self.dequeue(True) try:
if record is self._sentinel: record = self.dequeue(True)
break if record is self._sentinel:
self.handle(record) break
if has_task_done: self.handle(record)
q.task_done() if has_task_done:
except queue.Empty: q.task_done()
pass except queue.Empty:
# There might still be records in the queue. pass
while True: # There might still be records in the queue.
try: while True:
record = self.dequeue(False) try:
if record is self._sentinel: record = self.dequeue(False)
if record is self._sentinel:
break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
break break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
break
def stop(self):
"""
Stop the listener.
This asks the thread to terminate, and then waits for it to do so. def stop(self):
Note that if you don't call this before your application exits, there """
may be some records still left on the queue, which won't be processed. Stop the listener.
"""
self._stop.set() This asks the thread to terminate, and then waits for it to do so.
self.queue.put_nowait(self._sentinel) Note that if you don't call this before your application exits, there
self._thread.join() may be some records still left on the queue, which won't be processed.
self._thread = None """
self._stop.set()
self.queue.put_nowait(self._sentinel)
self._thread.join()
self._thread = None
...@@ -2093,6 +2093,8 @@ class QueueHandlerTest(BaseTest): ...@@ -2093,6 +2093,8 @@ class QueueHandlerTest(BaseTest):
self.assertEqual(data.name, self.que_logger.name) self.assertEqual(data.name, self.que_logger.name)
self.assertEqual((data.msg, data.args), (msg, None)) self.assertEqual((data.msg, data.args), (msg, None))
@unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
'logging.handlers.QueueListener required for this test')
def test_queue_listener(self): def test_queue_listener(self):
handler = TestHandler(Matcher()) handler = TestHandler(Matcher())
listener = logging.handlers.QueueListener(self.queue, handler) listener = logging.handlers.QueueListener(self.queue, handler)
......
...@@ -79,6 +79,8 @@ Core and Builtins ...@@ -79,6 +79,8 @@ Core and Builtins
Library Library
------- -------
- logging: don't define QueueListener if Python has no thread support.
- Issue #11277: mmap.mmap() calls fcntl(fd, F_FULLFSYNC) on Mac OS X to get - Issue #11277: mmap.mmap() calls fcntl(fd, F_FULLFSYNC) on Mac OS X to get
around a mmap bug with sparse files. Patch written by Steffen Daode Nurpmeso. around a mmap bug with sparse files. Patch written by Steffen Daode Nurpmeso.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment