diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 3a48628d55f..306cf866983 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -27,7 +27,10 @@ To use, simply 'import logging.handlers' and log away! import logging, socket, os, pickle, struct, time, re from stat import ST_DEV, ST_INO, ST_MTIME import queue -import threading +try: + import threading +except ImportError: + threading = None try: import codecs @@ -1218,106 +1221,107 @@ class QueueHandler(logging.Handler): except: self.handleError(record) -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 - list of handlers for processing. - """ - _sentinel = None +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 + list of handlers for processing. + """ + _sentinel = None - def __init__(self, queue, *handlers): - """ - Initialise an instance with the specified queue and - handlers. - """ - self.queue = queue - self.handlers = handlers - self._stop = threading.Event() - self._thread = None + def __init__(self, queue, *handlers): + """ + Initialise an instance with the specified queue and + handlers. + """ + self.queue = queue + self.handlers = handlers + self._stop = threading.Event() + self._thread = None - def dequeue(self, block): - """ - Dequeue a record and return it, optionally blocking. + def dequeue(self, block): + """ + 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. - """ - return self.queue.get(block) + 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) - def start(self): - """ - Start the listener. + def start(self): + """ + Start the listener. - 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) - t.start() + 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) + t.start() - def prepare(self , record): - """ - Prepare a record for handling. + def prepare(self , record): + """ + 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 - manipulation of the record before passing it to the handlers. - """ - return record + This method just returns the passed-in record. You may want to + override this method if you need to do any custom marshalling or + manipulation of the record before passing it to the handlers. + """ + return record - def handle(self, record): - """ - Handle a record. + def handle(self, record): + """ + Handle a record. - This just loops through the handlers offering them the record - to handle. - """ - record = self.prepare(record) - for handler in self.handlers: - handler.handle(record) + This just loops through the handlers offering them the record + to handle. + """ + record = self.prepare(record) + for handler in self.handlers: + handler.handle(record) - def _monitor(self): - """ - Monitor the queue for records, and ask the handler - to deal with them. + def _monitor(self): + """ + 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. - """ - q = self.queue - has_task_done = hasattr(q, 'task_done') - while not self._stop.isSet(): - try: - record = self.dequeue(True) - if record is self._sentinel: + 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') + while not self._stop.isSet(): + try: + record = self.dequeue(True) + if record is self._sentinel: + break + self.handle(record) + if has_task_done: + q.task_done() + except queue.Empty: + pass + # There might still be records in the queue. + while True: + try: + record = self.dequeue(False) + if record is self._sentinel: + break + self.handle(record) + if has_task_done: + q.task_done() + except queue.Empty: break - self.handle(record) - if has_task_done: - q.task_done() - except queue.Empty: - pass - # There might still be records in the queue. - while True: - try: - record = self.dequeue(False) - if record is self._sentinel: - break - self.handle(record) - if has_task_done: - q.task_done() - except queue.Empty: - break - def stop(self): - """ - Stop the listener. + def stop(self): + """ + Stop the listener. - This asks the thread to terminate, and then waits for it to do so. - 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. - """ - self._stop.set() - self.queue.put_nowait(self._sentinel) - self._thread.join() - self._thread = None + This asks the thread to terminate, and then waits for it to do so. + 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. + """ + self._stop.set() + self.queue.put_nowait(self._sentinel) + self._thread.join() + self._thread = None diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 6623a0f5322..90d293e3abe 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2093,6 +2093,8 @@ class QueueHandlerTest(BaseTest): self.assertEqual(data.name, self.que_logger.name) 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): handler = TestHandler(Matcher()) listener = logging.handlers.QueueListener(self.queue, handler) diff --git a/Misc/NEWS b/Misc/NEWS index 2a500949bc1..8ca2f02bfc0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -79,6 +79,8 @@ Core and Builtins 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 around a mmap bug with sparse files. Patch written by Steffen Daode Nurpmeso.