blob: 880f4e89fb03b5157ebbbc4d9fd3d64a732b6ee5 [file] [log] [blame]
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001# Copyright 2001-2017 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00002#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17"""Test harness for the logging module. Run all tests.
18
Vinay Sajip1e6499c2017-01-09 16:54:12 +000019Copyright (C) 2001-2017 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000020"""
21
Christian Heimes180510d2008-03-03 19:15:45 +000022import logging
23import logging.handlers
24import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000025
Benjamin Petersonf91df042009-02-13 02:50:59 +000026import codecs
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010027import configparser
Vinay Sajip19ec67a2010-09-17 18:57:36 +000028import datetime
Vinay Sajipdb8f4632016-09-08 01:37:03 +010029import pathlib
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Vinay Sajip66b8b082012-04-24 23:25:30 +010036import random
Christian Heimes180510d2008-03-03 19:15:45 +000037import re
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Christian Heimes180510d2008-03-03 19:15:45 +000039import struct
40import sys
41import tempfile
Berker Peksagce643912015-05-06 06:33:17 +030042from test.support.script_helper import assert_python_ok
Vinay Sajip1feedb42014-05-31 08:19:12 +010043from test import support
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010045import time
Christian Heimes180510d2008-03-03 19:15:45 +000046import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000047import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000048import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000049try:
50 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010051 # The following imports are needed only for tests which
Florent Xicluna5252f9f2011-11-07 19:43:05 +010052 # require threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010053 import asyncore
Vinay Sajipce7c9782011-05-17 07:15:53 +010054 from http.server import HTTPServer, BaseHTTPRequestHandler
55 import smtpd
56 from urllib.parse import urlparse, parse_qs
57 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
Vinay Sajip1feedb42014-05-31 08:19:12 +010058 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000059except ImportError:
60 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010061try:
Vinay Sajip223349c2015-10-01 20:37:54 +010062 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010063except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010064 win32evtlog = win32evtlogutil = pywintypes = None
65
Eric V. Smith851cad72012-03-11 22:46:04 -070066try:
67 import zlib
68except ImportError:
69 pass
Christian Heimes18c66892008-02-17 13:31:39 +000070
Christian Heimes180510d2008-03-03 19:15:45 +000071class BaseTest(unittest.TestCase):
72
73 """Base class for logging tests."""
74
75 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030076 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000077 message_num = 0
78
79 def setUp(self):
80 """Setup the default logging stream to an internal StringIO instance,
81 so that we can examine log output as we want."""
82 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000083 logging._acquireLock()
84 try:
Christian Heimes180510d2008-03-03 19:15:45 +000085 self.saved_handlers = logging._handlers.copy()
86 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000087 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070088 self.saved_name_to_level = logging._nameToLevel.copy()
89 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000090 self.logger_states = logger_states = {}
91 for name in saved_loggers:
92 logger_states[name] = getattr(saved_loggers[name],
93 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000094 finally:
95 logging._releaseLock()
96
Florent Xicluna5252f9f2011-11-07 19:43:05 +010097 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +000098 self.logger1 = logging.getLogger("\xab\xd7\xbb")
99 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000100
Christian Heimes180510d2008-03-03 19:15:45 +0000101 self.root_logger = logging.getLogger("")
102 self.original_logging_level = self.root_logger.getEffectiveLevel()
103
104 self.stream = io.StringIO()
105 self.root_logger.setLevel(logging.DEBUG)
106 self.root_hdlr = logging.StreamHandler(self.stream)
107 self.root_formatter = logging.Formatter(self.log_format)
108 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000109 if self.logger1.hasHandlers():
110 hlist = self.logger1.handlers + self.root_logger.handlers
111 raise AssertionError('Unexpected handlers: %s' % hlist)
112 if self.logger2.hasHandlers():
113 hlist = self.logger2.handlers + self.root_logger.handlers
114 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000115 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000116 self.assertTrue(self.logger1.hasHandlers())
117 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000118
119 def tearDown(self):
120 """Remove our logging stream, and restore the original logging
121 level."""
122 self.stream.close()
123 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000124 while self.root_logger.handlers:
125 h = self.root_logger.handlers[0]
126 self.root_logger.removeHandler(h)
127 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000128 self.root_logger.setLevel(self.original_logging_level)
129 logging._acquireLock()
130 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700131 logging._levelToName.clear()
132 logging._levelToName.update(self.saved_level_to_name)
133 logging._nameToLevel.clear()
134 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000135 logging._handlers.clear()
136 logging._handlers.update(self.saved_handlers)
137 logging._handlerList[:] = self.saved_handler_list
138 loggerDict = logging.getLogger().manager.loggerDict
139 loggerDict.clear()
140 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000141 logger_states = self.logger_states
142 for name in self.logger_states:
143 if logger_states[name] is not None:
144 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000145 finally:
146 logging._releaseLock()
147
Vinay Sajip4ded5512012-10-02 15:56:16 +0100148 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000149 """Match the collected log lines against the regular expression
150 self.expected_log_pat, and compare the extracted group values to
151 the expected_values list of tuples."""
152 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100153 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300154 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100155 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000156 for actual, expected in zip(actual_lines, expected_values):
157 match = pat.search(actual)
158 if not match:
159 self.fail("Log line does not match expected pattern:\n" +
160 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000161 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000162 s = stream.read()
163 if s:
164 self.fail("Remaining output at end of log stream:\n" + s)
165
166 def next_message(self):
167 """Generate a message consisting solely of an auto-incrementing
168 integer."""
169 self.message_num += 1
170 return "%d" % self.message_num
171
172
173class BuiltinLevelsTest(BaseTest):
174 """Test builtin levels and their inheritance."""
175
176 def test_flat(self):
177 #Logging levels in a flat logger namespace.
178 m = self.next_message
179
180 ERR = logging.getLogger("ERR")
181 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000182 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000183 INF.setLevel(logging.INFO)
184 DEB = logging.getLogger("DEB")
185 DEB.setLevel(logging.DEBUG)
186
187 # These should log.
188 ERR.log(logging.CRITICAL, m())
189 ERR.error(m())
190
191 INF.log(logging.CRITICAL, m())
192 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100193 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000194 INF.info(m())
195
196 DEB.log(logging.CRITICAL, m())
197 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100198 DEB.warning(m())
199 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000200 DEB.debug(m())
201
202 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000204 ERR.info(m())
205 ERR.debug(m())
206
207 INF.debug(m())
208
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000209 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000210 ('ERR', 'CRITICAL', '1'),
211 ('ERR', 'ERROR', '2'),
212 ('INF', 'CRITICAL', '3'),
213 ('INF', 'ERROR', '4'),
214 ('INF', 'WARNING', '5'),
215 ('INF', 'INFO', '6'),
216 ('DEB', 'CRITICAL', '7'),
217 ('DEB', 'ERROR', '8'),
218 ('DEB', 'WARNING', '9'),
219 ('DEB', 'INFO', '10'),
220 ('DEB', 'DEBUG', '11'),
221 ])
222
223 def test_nested_explicit(self):
224 # Logging levels in a nested namespace, all explicitly set.
225 m = self.next_message
226
227 INF = logging.getLogger("INF")
228 INF.setLevel(logging.INFO)
229 INF_ERR = logging.getLogger("INF.ERR")
230 INF_ERR.setLevel(logging.ERROR)
231
232 # These should log.
233 INF_ERR.log(logging.CRITICAL, m())
234 INF_ERR.error(m())
235
236 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100237 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000238 INF_ERR.info(m())
239 INF_ERR.debug(m())
240
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000241 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000242 ('INF.ERR', 'CRITICAL', '1'),
243 ('INF.ERR', 'ERROR', '2'),
244 ])
245
246 def test_nested_inherited(self):
247 #Logging levels in a nested namespace, inherited from parent loggers.
248 m = self.next_message
249
250 INF = logging.getLogger("INF")
251 INF.setLevel(logging.INFO)
252 INF_ERR = logging.getLogger("INF.ERR")
253 INF_ERR.setLevel(logging.ERROR)
254 INF_UNDEF = logging.getLogger("INF.UNDEF")
255 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
256 UNDEF = logging.getLogger("UNDEF")
257
258 # These should log.
259 INF_UNDEF.log(logging.CRITICAL, m())
260 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100261 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000262 INF_UNDEF.info(m())
263 INF_ERR_UNDEF.log(logging.CRITICAL, m())
264 INF_ERR_UNDEF.error(m())
265
266 # These should not log.
267 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100268 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000269 INF_ERR_UNDEF.info(m())
270 INF_ERR_UNDEF.debug(m())
271
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000272 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000273 ('INF.UNDEF', 'CRITICAL', '1'),
274 ('INF.UNDEF', 'ERROR', '2'),
275 ('INF.UNDEF', 'WARNING', '3'),
276 ('INF.UNDEF', 'INFO', '4'),
277 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
278 ('INF.ERR.UNDEF', 'ERROR', '6'),
279 ])
280
281 def test_nested_with_virtual_parent(self):
282 # Logging levels when some parent does not exist yet.
283 m = self.next_message
284
285 INF = logging.getLogger("INF")
286 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
287 CHILD = logging.getLogger("INF.BADPARENT")
288 INF.setLevel(logging.INFO)
289
290 # These should log.
291 GRANDCHILD.log(logging.FATAL, m())
292 GRANDCHILD.info(m())
293 CHILD.log(logging.FATAL, m())
294 CHILD.info(m())
295
296 # These should not log.
297 GRANDCHILD.debug(m())
298 CHILD.debug(m())
299
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000300 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000301 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
302 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
303 ('INF.BADPARENT', 'CRITICAL', '3'),
304 ('INF.BADPARENT', 'INFO', '4'),
305 ])
306
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100307 def test_regression_22386(self):
308 """See issue #22386 for more information."""
309 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
310 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000311
Vinay Sajip8b866d52017-01-11 06:57:55 +0000312 def test_regression_29220(self):
313 """See issue #29220 for more information."""
314 logging.addLevelName(logging.INFO, '')
315 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
316 self.assertEqual(logging.getLevelName(logging.INFO), '')
317
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100318 def test_issue27935(self):
319 fatal = logging.getLevelName('FATAL')
320 self.assertEqual(fatal, logging.FATAL)
321
Vinay Sajip924aaae2017-01-11 17:35:36 +0000322 def test_regression_29220(self):
323 """See issue #29220 for more information."""
324 logging.addLevelName(logging.INFO, '')
325 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
326 self.assertEqual(logging.getLevelName(logging.INFO), '')
327 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
328 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
329
Christian Heimes180510d2008-03-03 19:15:45 +0000330class BasicFilterTest(BaseTest):
331
332 """Test the bundled Filter class."""
333
334 def test_filter(self):
335 # Only messages satisfying the specified criteria pass through the
336 # filter.
337 filter_ = logging.Filter("spam.eggs")
338 handler = self.root_logger.handlers[0]
339 try:
340 handler.addFilter(filter_)
341 spam = logging.getLogger("spam")
342 spam_eggs = logging.getLogger("spam.eggs")
343 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
344 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
345
346 spam.info(self.next_message())
347 spam_eggs.info(self.next_message()) # Good.
348 spam_eggs_fish.info(self.next_message()) # Good.
349 spam_bakedbeans.info(self.next_message())
350
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000351 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000352 ('spam.eggs', 'INFO', '2'),
353 ('spam.eggs.fish', 'INFO', '3'),
354 ])
355 finally:
356 handler.removeFilter(filter_)
357
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000358 def test_callable_filter(self):
359 # Only messages satisfying the specified criteria pass through the
360 # filter.
361
362 def filterfunc(record):
363 parts = record.name.split('.')
364 prefix = '.'.join(parts[:2])
365 return prefix == 'spam.eggs'
366
367 handler = self.root_logger.handlers[0]
368 try:
369 handler.addFilter(filterfunc)
370 spam = logging.getLogger("spam")
371 spam_eggs = logging.getLogger("spam.eggs")
372 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
373 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
374
375 spam.info(self.next_message())
376 spam_eggs.info(self.next_message()) # Good.
377 spam_eggs_fish.info(self.next_message()) # Good.
378 spam_bakedbeans.info(self.next_message())
379
380 self.assert_log_lines([
381 ('spam.eggs', 'INFO', '2'),
382 ('spam.eggs.fish', 'INFO', '3'),
383 ])
384 finally:
385 handler.removeFilter(filterfunc)
386
Vinay Sajip985ef872011-04-26 19:34:04 +0100387 def test_empty_filter(self):
388 f = logging.Filter()
389 r = logging.makeLogRecord({'name': 'spam.eggs'})
390 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000391
392#
393# First, we define our levels. There can be as many as you want - the only
394# limitations are that they should be integers, the lowest should be > 0 and
395# larger values mean less information being logged. If you need specific
396# level values which do not fit into these limitations, you can use a
397# mapping dictionary to convert between your application levels and the
398# logging system.
399#
400SILENT = 120
401TACITURN = 119
402TERSE = 118
403EFFUSIVE = 117
404SOCIABLE = 116
405VERBOSE = 115
406TALKATIVE = 114
407GARRULOUS = 113
408CHATTERBOX = 112
409BORING = 111
410
411LEVEL_RANGE = range(BORING, SILENT + 1)
412
413#
414# Next, we define names for our levels. You don't need to do this - in which
415# case the system will use "Level n" to denote the text for the level.
416#
417my_logging_levels = {
418 SILENT : 'Silent',
419 TACITURN : 'Taciturn',
420 TERSE : 'Terse',
421 EFFUSIVE : 'Effusive',
422 SOCIABLE : 'Sociable',
423 VERBOSE : 'Verbose',
424 TALKATIVE : 'Talkative',
425 GARRULOUS : 'Garrulous',
426 CHATTERBOX : 'Chatterbox',
427 BORING : 'Boring',
428}
429
430class GarrulousFilter(logging.Filter):
431
432 """A filter which blocks garrulous messages."""
433
434 def filter(self, record):
435 return record.levelno != GARRULOUS
436
437class VerySpecificFilter(logging.Filter):
438
439 """A filter which blocks sociable and taciturn messages."""
440
441 def filter(self, record):
442 return record.levelno not in [SOCIABLE, TACITURN]
443
444
445class CustomLevelsAndFiltersTest(BaseTest):
446
447 """Test various filtering possibilities with custom logging levels."""
448
449 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300450 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000451
452 def setUp(self):
453 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000454 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000455 logging.addLevelName(k, v)
456
457 def log_at_all_levels(self, logger):
458 for lvl in LEVEL_RANGE:
459 logger.log(lvl, self.next_message())
460
461 def test_logger_filter(self):
462 # Filter at logger level.
463 self.root_logger.setLevel(VERBOSE)
464 # Levels >= 'Verbose' are good.
465 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000466 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000467 ('Verbose', '5'),
468 ('Sociable', '6'),
469 ('Effusive', '7'),
470 ('Terse', '8'),
471 ('Taciturn', '9'),
472 ('Silent', '10'),
473 ])
474
475 def test_handler_filter(self):
476 # Filter at handler level.
477 self.root_logger.handlers[0].setLevel(SOCIABLE)
478 try:
479 # Levels >= 'Sociable' are good.
480 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000481 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000482 ('Sociable', '6'),
483 ('Effusive', '7'),
484 ('Terse', '8'),
485 ('Taciturn', '9'),
486 ('Silent', '10'),
487 ])
488 finally:
489 self.root_logger.handlers[0].setLevel(logging.NOTSET)
490
491 def test_specific_filters(self):
492 # Set a specific filter object on the handler, and then add another
493 # filter object on the logger itself.
494 handler = self.root_logger.handlers[0]
495 specific_filter = None
496 garr = GarrulousFilter()
497 handler.addFilter(garr)
498 try:
499 self.log_at_all_levels(self.root_logger)
500 first_lines = [
501 # Notice how 'Garrulous' is missing
502 ('Boring', '1'),
503 ('Chatterbox', '2'),
504 ('Talkative', '4'),
505 ('Verbose', '5'),
506 ('Sociable', '6'),
507 ('Effusive', '7'),
508 ('Terse', '8'),
509 ('Taciturn', '9'),
510 ('Silent', '10'),
511 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000512 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000513
514 specific_filter = VerySpecificFilter()
515 self.root_logger.addFilter(specific_filter)
516 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000517 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000518 # Not only 'Garrulous' is still missing, but also 'Sociable'
519 # and 'Taciturn'
520 ('Boring', '11'),
521 ('Chatterbox', '12'),
522 ('Talkative', '14'),
523 ('Verbose', '15'),
524 ('Effusive', '17'),
525 ('Terse', '18'),
526 ('Silent', '20'),
527 ])
528 finally:
529 if specific_filter:
530 self.root_logger.removeFilter(specific_filter)
531 handler.removeFilter(garr)
532
533
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100534class HandlerTest(BaseTest):
535 def test_name(self):
536 h = logging.Handler()
537 h.name = 'generic'
538 self.assertEqual(h.name, 'generic')
539 h.name = 'anothergeneric'
540 self.assertEqual(h.name, 'anothergeneric')
541 self.assertRaises(NotImplementedError, h.emit, None)
542
Vinay Sajip5a35b062011-04-27 11:31:14 +0100543 def test_builtin_handlers(self):
544 # We can't actually *use* too many handlers in the tests,
545 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200546 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100547 for existing in (True, False):
548 fd, fn = tempfile.mkstemp()
549 os.close(fd)
550 if not existing:
551 os.unlink(fn)
552 h = logging.handlers.WatchedFileHandler(fn, delay=True)
553 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100554 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100555 self.assertEqual(dev, -1)
556 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100557 r = logging.makeLogRecord({'msg': 'Test'})
558 h.handle(r)
559 # Now remove the file.
560 os.unlink(fn)
561 self.assertFalse(os.path.exists(fn))
562 # The next call should recreate the file.
563 h.handle(r)
564 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100565 else:
566 self.assertEqual(h.dev, -1)
567 self.assertEqual(h.ino, -1)
568 h.close()
569 if existing:
570 os.unlink(fn)
571 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100572 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100573 else:
574 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100575 try:
576 h = logging.handlers.SysLogHandler(sockname)
577 self.assertEqual(h.facility, h.LOG_USER)
578 self.assertTrue(h.unixsocket)
579 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200580 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100581 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100582 for method in ('GET', 'POST', 'PUT'):
583 if method == 'PUT':
584 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
585 'localhost', '/log', method)
586 else:
587 h = logging.handlers.HTTPHandler('localhost', '/log', method)
588 h.close()
589 h = logging.handlers.BufferingHandler(0)
590 r = logging.makeLogRecord({})
591 self.assertTrue(h.shouldFlush(r))
592 h.close()
593 h = logging.handlers.BufferingHandler(1)
594 self.assertFalse(h.shouldFlush(r))
595 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100596
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100597 def test_path_objects(self):
598 """
599 Test that Path objects are accepted as filename arguments to handlers.
600
601 See Issue #27493.
602 """
603 fd, fn = tempfile.mkstemp()
604 os.close(fd)
605 os.unlink(fn)
606 pfn = pathlib.Path(fn)
607 cases = (
608 (logging.FileHandler, (pfn, 'w')),
609 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
610 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
611 )
612 if sys.platform in ('linux', 'darwin'):
613 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
614 for cls, args in cases:
615 h = cls(*args)
616 self.assertTrue(os.path.exists(fn))
617 h.close()
618 os.unlink(fn)
619
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100620 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100621 @unittest.skipUnless(threading, 'Threading required for this test.')
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200622 @support.reap_threads
Vinay Sajipa5798de2012-04-24 23:33:33 +0100623 def test_race(self):
624 # Issue #14632 refers.
625 def remove_loop(fname, tries):
626 for _ in range(tries):
627 try:
628 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000629 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100630 except OSError:
631 pass
632 time.sleep(0.004 * random.randint(0, 4))
633
Vinay Sajipc94871a2012-04-25 10:51:35 +0100634 del_count = 500
635 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100636
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000637 self.handle_time = None
638 self.deletion_time = None
639
Vinay Sajipa5798de2012-04-24 23:33:33 +0100640 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100641 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
642 os.close(fd)
643 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
644 remover.daemon = True
645 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100646 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100647 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
648 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100649 try:
650 for _ in range(log_count):
651 time.sleep(0.005)
652 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000653 try:
654 self.handle_time = time.time()
655 h.handle(r)
656 except Exception:
657 print('Deleted at %s, '
658 'opened at %s' % (self.deletion_time,
659 self.handle_time))
660 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100661 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100662 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100663 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100664 if os.path.exists(fn):
665 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100666
667
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100668class BadStream(object):
669 def write(self, data):
670 raise RuntimeError('deliberate mistake')
671
672class TestStreamHandler(logging.StreamHandler):
673 def handleError(self, record):
674 self.error_record = record
675
676class StreamHandlerTest(BaseTest):
677 def test_error_handling(self):
678 h = TestStreamHandler(BadStream())
679 r = logging.makeLogRecord({})
680 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100681
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100682 try:
683 h.handle(r)
684 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100685
Vinay Sajip985ef872011-04-26 19:34:04 +0100686 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100687 with support.captured_stderr() as stderr:
688 h.handle(r)
689 msg = '\nRuntimeError: deliberate mistake\n'
690 self.assertIn(msg, stderr.getvalue())
691
Vinay Sajip985ef872011-04-26 19:34:04 +0100692 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100693 with support.captured_stderr() as stderr:
694 h.handle(r)
695 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100696 finally:
697 logging.raiseExceptions = old_raise
698
Vinay Sajip7367d082011-05-02 13:17:27 +0100699# -- The following section could be moved into a server_helper.py module
700# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100701
Vinay Sajipce7c9782011-05-17 07:15:53 +0100702if threading:
Vinay Sajipce7c9782011-05-17 07:15:53 +0100703 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100704 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100705 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100706
Vinay Sajipce7c9782011-05-17 07:15:53 +0100707 :param addr: A (host, port) tuple which the server listens on.
708 You can specify a port value of zero: the server's
709 *port* attribute will hold the actual port number
710 used, which can be used in client connections.
711 :param handler: A callable which will be called to process
712 incoming messages. The handler will be passed
713 the client address tuple, who the message is from,
714 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100715 :param poll_interval: The interval, in seconds, used in the underlying
716 :func:`select` or :func:`poll` call by
717 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100718 :param sockmap: A dictionary which will be used to hold
719 :class:`asyncore.dispatcher` instances used by
720 :func:`asyncore.loop`. This avoids changing the
721 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100722 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100723
724 def __init__(self, addr, handler, poll_interval, sockmap):
R David Murray1144da52014-06-11 12:27:40 -0400725 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
726 decode_data=True)
Vinay Sajip30298b42013-06-07 15:21:41 +0100727 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +0100728 self._handler = handler
729 self._thread = None
730 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100731
Vinay Sajipce7c9782011-05-17 07:15:53 +0100732 def process_message(self, peer, mailfrom, rcpttos, data):
733 """
734 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100735
Vinay Sajipce7c9782011-05-17 07:15:53 +0100736 Typically, this will be a test case method.
737 :param peer: The client (host, port) tuple.
738 :param mailfrom: The address of the sender.
739 :param rcpttos: The addresses of the recipients.
740 :param data: The message.
741 """
742 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100743
Vinay Sajipce7c9782011-05-17 07:15:53 +0100744 def start(self):
745 """
746 Start the server running on a separate daemon thread.
747 """
748 self._thread = t = threading.Thread(target=self.serve_forever,
749 args=(self.poll_interval,))
750 t.setDaemon(True)
751 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100752
Vinay Sajipce7c9782011-05-17 07:15:53 +0100753 def serve_forever(self, poll_interval):
754 """
755 Run the :mod:`asyncore` loop until normal termination
756 conditions arise.
757 :param poll_interval: The interval, in seconds, used in the underlying
758 :func:`select` or :func:`poll` call by
759 :func:`asyncore.loop`.
760 """
761 try:
Vinay Sajip30298b42013-06-07 15:21:41 +0100762 asyncore.loop(poll_interval, map=self._map)
Andrew Svetlov6d8a1222012-12-17 22:23:46 +0200763 except OSError:
Vinay Sajipce7c9782011-05-17 07:15:53 +0100764 # On FreeBSD 8, closing the server repeatably
765 # raises this error. We swallow it if the
766 # server has been closed.
767 if self.connected or self.accepting:
768 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100769
Vinay Sajipce7c9782011-05-17 07:15:53 +0100770 def stop(self, timeout=None):
771 """
772 Stop the thread by closing the server instance.
773 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100774
Vinay Sajipce7c9782011-05-17 07:15:53 +0100775 :param timeout: How long to wait for the server thread
776 to terminate.
777 """
778 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100779 self._thread.join(timeout)
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200780 alive = self._thread.is_alive()
Vinay Sajip7367d082011-05-02 13:17:27 +0100781 self._thread = None
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200782 if alive:
783 self.fail("join() timed out")
Vinay Sajip7367d082011-05-02 13:17:27 +0100784
Vinay Sajipce7c9782011-05-17 07:15:53 +0100785 class ControlMixin(object):
786 """
787 This mixin is used to start a server on a separate thread, and
788 shut it down programmatically. Request handling is simplified - instead
789 of needing to derive a suitable RequestHandler subclass, you just
790 provide a callable which will be passed each received request to be
791 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100792
Vinay Sajipce7c9782011-05-17 07:15:53 +0100793 :param handler: A handler callable which will be called with a
794 single parameter - the request - in order to
795 process the request. This handler is called on the
796 server thread, effectively meaning that requests are
797 processed serially. While not quite Web scale ;-),
798 this should be fine for testing applications.
799 :param poll_interval: The polling interval in seconds.
800 """
801 def __init__(self, handler, poll_interval):
802 self._thread = None
803 self.poll_interval = poll_interval
804 self._handler = handler
805 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100806
Vinay Sajipce7c9782011-05-17 07:15:53 +0100807 def start(self):
808 """
809 Create a daemon thread to run the server, and start it.
810 """
811 self._thread = t = threading.Thread(target=self.serve_forever,
812 args=(self.poll_interval,))
813 t.setDaemon(True)
814 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100815
Vinay Sajipce7c9782011-05-17 07:15:53 +0100816 def serve_forever(self, poll_interval):
817 """
818 Run the server. Set the ready flag before entering the
819 service loop.
820 """
821 self.ready.set()
822 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100823
Vinay Sajipce7c9782011-05-17 07:15:53 +0100824 def stop(self, timeout=None):
825 """
826 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100827
Vinay Sajipce7c9782011-05-17 07:15:53 +0100828 :param timeout: How long to wait for the server thread
829 to terminate.
830 """
831 self.shutdown()
832 if self._thread is not None:
833 self._thread.join(timeout)
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200834 alive = self._thread.is_alive()
Vinay Sajipce7c9782011-05-17 07:15:53 +0100835 self._thread = None
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200836 if alive:
837 self.fail("join() timed out")
Vinay Sajipce7c9782011-05-17 07:15:53 +0100838 self.server_close()
839 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100840
Vinay Sajipce7c9782011-05-17 07:15:53 +0100841 class TestHTTPServer(ControlMixin, HTTPServer):
842 """
843 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100844
Vinay Sajipce7c9782011-05-17 07:15:53 +0100845 :param addr: A tuple with the IP address and port to listen on.
846 :param handler: A handler callable which will be called with a
847 single parameter - the request - in order to
848 process the request.
849 :param poll_interval: The polling interval in seconds.
850 :param log: Pass ``True`` to enable log messages.
851 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100852 def __init__(self, addr, handler, poll_interval=0.5,
853 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100854 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
855 def __getattr__(self, name, default=None):
856 if name.startswith('do_'):
857 return self.process_request
858 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100859
Vinay Sajipce7c9782011-05-17 07:15:53 +0100860 def process_request(self):
861 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100862
Vinay Sajipce7c9782011-05-17 07:15:53 +0100863 def log_message(self, format, *args):
864 if log:
865 super(DelegatingHTTPRequestHandler,
866 self).log_message(format, *args)
867 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
868 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100869 self.sslctx = sslctx
870
871 def get_request(self):
872 try:
873 sock, addr = self.socket.accept()
874 if self.sslctx:
875 sock = self.sslctx.wrap_socket(sock, server_side=True)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200876 except OSError as e:
Vinay Sajip32565b62011-05-21 00:34:51 +0100877 # socket errors are silenced by the caller, print them here
878 sys.stderr.write("Got an error:\n%s\n" % e)
879 raise
880 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100881
Vinay Sajipce7c9782011-05-17 07:15:53 +0100882 class TestTCPServer(ControlMixin, ThreadingTCPServer):
883 """
884 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100885
Vinay Sajipce7c9782011-05-17 07:15:53 +0100886 :param addr: A tuple with the IP address and port to listen on.
887 :param handler: A handler callable which will be called with a single
888 parameter - the request - in order to process the request.
889 :param poll_interval: The polling interval in seconds.
890 :bind_and_activate: If True (the default), binds the server and starts it
891 listening. If False, you need to call
892 :meth:`server_bind` and :meth:`server_activate` at
893 some later time before calling :meth:`start`, so that
894 the server will set up the socket and listen on it.
895 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100896
Vinay Sajipce7c9782011-05-17 07:15:53 +0100897 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100898
Vinay Sajipce7c9782011-05-17 07:15:53 +0100899 def __init__(self, addr, handler, poll_interval=0.5,
900 bind_and_activate=True):
901 class DelegatingTCPRequestHandler(StreamRequestHandler):
902
903 def handle(self):
904 self.server._handler(self)
905 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
906 bind_and_activate)
907 ControlMixin.__init__(self, handler, poll_interval)
908
909 def server_bind(self):
910 super(TestTCPServer, self).server_bind()
911 self.port = self.socket.getsockname()[1]
912
913 class TestUDPServer(ControlMixin, ThreadingUDPServer):
914 """
915 A UDP server which is controllable using :class:`ControlMixin`.
916
917 :param addr: A tuple with the IP address and port to listen on.
918 :param handler: A handler callable which will be called with a
919 single parameter - the request - in order to
920 process the request.
921 :param poll_interval: The polling interval for shutdown requests,
922 in seconds.
923 :bind_and_activate: If True (the default), binds the server and
924 starts it listening. If False, you need to
925 call :meth:`server_bind` and
926 :meth:`server_activate` at some later time
927 before calling :meth:`start`, so that the server will
928 set up the socket and listen on it.
929 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100930 def __init__(self, addr, handler, poll_interval=0.5,
931 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100932 class DelegatingUDPRequestHandler(DatagramRequestHandler):
933
934 def handle(self):
935 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100936
937 def finish(self):
938 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100939 if data:
940 try:
941 super(DelegatingUDPRequestHandler, self).finish()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200942 except OSError:
Vinay Sajip3ef12292011-05-23 23:00:42 +0100943 if not self.server._closed:
944 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100945
Vinay Sajip3ef12292011-05-23 23:00:42 +0100946 ThreadingUDPServer.__init__(self, addr,
947 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100948 bind_and_activate)
949 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100950 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100951
952 def server_bind(self):
953 super(TestUDPServer, self).server_bind()
954 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100955
Vinay Sajipba980db2011-05-23 21:37:54 +0100956 def server_close(self):
957 super(TestUDPServer, self).server_close()
958 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100959
Victor Stinnerec5a8602014-06-02 14:41:51 +0200960 if hasattr(socket, "AF_UNIX"):
961 class TestUnixStreamServer(TestTCPServer):
962 address_family = socket.AF_UNIX
963
964 class TestUnixDatagramServer(TestUDPServer):
965 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +0100966
Vinay Sajip7367d082011-05-02 13:17:27 +0100967# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100968
Vinay Sajipce7c9782011-05-17 07:15:53 +0100969@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100970class SMTPHandlerTest(BaseTest):
Vinay Sajip827f5d32013-12-03 11:28:55 +0000971 TIMEOUT = 8.0
Victor Stinner4bcfa3a2017-04-25 00:41:36 +0200972
973 @support.reap_threads
Vinay Sajipa463d252011-04-30 21:52:48 +0100974 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100975 sockmap = {}
Ned Deily1e012e62015-01-17 16:57:19 -0800976 server = TestSMTPServer((support.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +0100977 sockmap)
978 server.start()
Ned Deily1e012e62015-01-17 16:57:19 -0800979 addr = (support.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +0000980 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
981 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +0100982 self.assertEqual(h.toaddrs, ['you'])
983 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +0100984 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100985 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100986 h.handle(r)
Vinay Sajip827f5d32013-12-03 11:28:55 +0000987 self.handled.wait(self.TIMEOUT) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100988 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000989 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100990 self.assertEqual(len(self.messages), 1)
991 peer, mailfrom, rcpttos, data = self.messages[0]
992 self.assertEqual(mailfrom, 'me')
993 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100994 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +0100995 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +0100996 h.close()
997
998 def process_message(self, *args):
999 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001000 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001001
Christian Heimes180510d2008-03-03 19:15:45 +00001002class MemoryHandlerTest(BaseTest):
1003
1004 """Tests for the MemoryHandler."""
1005
1006 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001007 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001008
1009 def setUp(self):
1010 BaseTest.setUp(self)
1011 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001012 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001013 self.mem_logger = logging.getLogger('mem')
1014 self.mem_logger.propagate = 0
1015 self.mem_logger.addHandler(self.mem_hdlr)
1016
1017 def tearDown(self):
1018 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001019 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001020
1021 def test_flush(self):
1022 # The memory handler flushes to its target handler based on specific
1023 # criteria (message count and message level).
1024 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001025 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001026 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001027 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001028 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001029 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001030 lines = [
1031 ('DEBUG', '1'),
1032 ('INFO', '2'),
1033 ('WARNING', '3'),
1034 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001035 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001036 for n in (4, 14):
1037 for i in range(9):
1038 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001039 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001040 # This will flush because it's the 10th message since the last
1041 # flush.
1042 self.mem_logger.debug(self.next_message())
1043 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001044 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001045
1046 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001047 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001048
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001049 def test_flush_on_close(self):
1050 """
1051 Test that the flush-on-close configuration works as expected.
1052 """
1053 self.mem_logger.debug(self.next_message())
1054 self.assert_log_lines([])
1055 self.mem_logger.info(self.next_message())
1056 self.assert_log_lines([])
1057 self.mem_logger.removeHandler(self.mem_hdlr)
1058 # Default behaviour is to flush on close. Check that it happens.
1059 self.mem_hdlr.close()
1060 lines = [
1061 ('DEBUG', '1'),
1062 ('INFO', '2'),
1063 ]
1064 self.assert_log_lines(lines)
1065 # Now configure for flushing not to be done on close.
1066 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1067 self.root_hdlr,
1068 False)
1069 self.mem_logger.addHandler(self.mem_hdlr)
1070 self.mem_logger.debug(self.next_message())
1071 self.assert_log_lines(lines) # no change
1072 self.mem_logger.info(self.next_message())
1073 self.assert_log_lines(lines) # no change
1074 self.mem_logger.removeHandler(self.mem_hdlr)
1075 self.mem_hdlr.close()
1076 # assert that no new lines have been added
1077 self.assert_log_lines(lines) # no change
1078
Christian Heimes180510d2008-03-03 19:15:45 +00001079
1080class ExceptionFormatter(logging.Formatter):
1081 """A special exception formatter."""
1082 def formatException(self, ei):
1083 return "Got a [%s]" % ei[0].__name__
1084
1085
1086class ConfigFileTest(BaseTest):
1087
1088 """Reading logging config from a .ini-style config file."""
1089
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001090 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001091
1092 # config0 is a standard configuration.
1093 config0 = """
1094 [loggers]
1095 keys=root
1096
1097 [handlers]
1098 keys=hand1
1099
1100 [formatters]
1101 keys=form1
1102
1103 [logger_root]
1104 level=WARNING
1105 handlers=hand1
1106
1107 [handler_hand1]
1108 class=StreamHandler
1109 level=NOTSET
1110 formatter=form1
1111 args=(sys.stdout,)
1112
1113 [formatter_form1]
1114 format=%(levelname)s ++ %(message)s
1115 datefmt=
1116 """
1117
1118 # config1 adds a little to the standard configuration.
1119 config1 = """
1120 [loggers]
1121 keys=root,parser
1122
1123 [handlers]
1124 keys=hand1
1125
1126 [formatters]
1127 keys=form1
1128
1129 [logger_root]
1130 level=WARNING
1131 handlers=
1132
1133 [logger_parser]
1134 level=DEBUG
1135 handlers=hand1
1136 propagate=1
1137 qualname=compiler.parser
1138
1139 [handler_hand1]
1140 class=StreamHandler
1141 level=NOTSET
1142 formatter=form1
1143 args=(sys.stdout,)
1144
1145 [formatter_form1]
1146 format=%(levelname)s ++ %(message)s
1147 datefmt=
1148 """
1149
Vinay Sajip3f84b072011-03-07 17:49:33 +00001150 # config1a moves the handler to the root.
1151 config1a = """
1152 [loggers]
1153 keys=root,parser
1154
1155 [handlers]
1156 keys=hand1
1157
1158 [formatters]
1159 keys=form1
1160
1161 [logger_root]
1162 level=WARNING
1163 handlers=hand1
1164
1165 [logger_parser]
1166 level=DEBUG
1167 handlers=
1168 propagate=1
1169 qualname=compiler.parser
1170
1171 [handler_hand1]
1172 class=StreamHandler
1173 level=NOTSET
1174 formatter=form1
1175 args=(sys.stdout,)
1176
1177 [formatter_form1]
1178 format=%(levelname)s ++ %(message)s
1179 datefmt=
1180 """
1181
Christian Heimes180510d2008-03-03 19:15:45 +00001182 # config2 has a subtle configuration error that should be reported
1183 config2 = config1.replace("sys.stdout", "sys.stbout")
1184
1185 # config3 has a less subtle configuration error
1186 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1187
1188 # config4 specifies a custom formatter class to be loaded
1189 config4 = """
1190 [loggers]
1191 keys=root
1192
1193 [handlers]
1194 keys=hand1
1195
1196 [formatters]
1197 keys=form1
1198
1199 [logger_root]
1200 level=NOTSET
1201 handlers=hand1
1202
1203 [handler_hand1]
1204 class=StreamHandler
1205 level=NOTSET
1206 formatter=form1
1207 args=(sys.stdout,)
1208
1209 [formatter_form1]
1210 class=""" + __name__ + """.ExceptionFormatter
1211 format=%(levelname)s:%(name)s:%(message)s
1212 datefmt=
1213 """
1214
Georg Brandl3dbca812008-07-23 16:10:53 +00001215 # config5 specifies a custom handler class to be loaded
1216 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1217
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001218 # config6 uses ', ' delimiters in the handlers and formatters sections
1219 config6 = """
1220 [loggers]
1221 keys=root,parser
1222
1223 [handlers]
1224 keys=hand1, hand2
1225
1226 [formatters]
1227 keys=form1, form2
1228
1229 [logger_root]
1230 level=WARNING
1231 handlers=
1232
1233 [logger_parser]
1234 level=DEBUG
1235 handlers=hand1
1236 propagate=1
1237 qualname=compiler.parser
1238
1239 [handler_hand1]
1240 class=StreamHandler
1241 level=NOTSET
1242 formatter=form1
1243 args=(sys.stdout,)
1244
1245 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001246 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001247 level=NOTSET
1248 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001249 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001250
1251 [formatter_form1]
1252 format=%(levelname)s ++ %(message)s
1253 datefmt=
1254
1255 [formatter_form2]
1256 format=%(message)s
1257 datefmt=
1258 """
1259
Vinay Sajip3f84b072011-03-07 17:49:33 +00001260 # config7 adds a compiler logger.
1261 config7 = """
1262 [loggers]
1263 keys=root,parser,compiler
1264
1265 [handlers]
1266 keys=hand1
1267
1268 [formatters]
1269 keys=form1
1270
1271 [logger_root]
1272 level=WARNING
1273 handlers=hand1
1274
1275 [logger_compiler]
1276 level=DEBUG
1277 handlers=
1278 propagate=1
1279 qualname=compiler
1280
1281 [logger_parser]
1282 level=DEBUG
1283 handlers=
1284 propagate=1
1285 qualname=compiler.parser
1286
1287 [handler_hand1]
1288 class=StreamHandler
1289 level=NOTSET
1290 formatter=form1
1291 args=(sys.stdout,)
1292
1293 [formatter_form1]
1294 format=%(levelname)s ++ %(message)s
1295 datefmt=
1296 """
1297
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001298 disable_test = """
1299 [loggers]
1300 keys=root
1301
1302 [handlers]
1303 keys=screen
1304
1305 [formatters]
1306 keys=
1307
1308 [logger_root]
1309 level=DEBUG
1310 handlers=screen
1311
1312 [handler_screen]
1313 level=DEBUG
1314 class=StreamHandler
1315 args=(sys.stdout,)
1316 formatter=
1317 """
1318
1319 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001320 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001321 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001322
1323 def test_config0_ok(self):
1324 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001325 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001326 self.apply_config(self.config0)
1327 logger = logging.getLogger()
1328 # Won't output anything
1329 logger.info(self.next_message())
1330 # Outputs a message
1331 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001332 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001333 ('ERROR', '2'),
1334 ], stream=output)
1335 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001336 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001337
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001338 def test_config0_using_cp_ok(self):
1339 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001340 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001341 file = io.StringIO(textwrap.dedent(self.config0))
1342 cp = configparser.ConfigParser()
1343 cp.read_file(file)
1344 logging.config.fileConfig(cp)
1345 logger = logging.getLogger()
1346 # Won't output anything
1347 logger.info(self.next_message())
1348 # Outputs a message
1349 logger.error(self.next_message())
1350 self.assert_log_lines([
1351 ('ERROR', '2'),
1352 ], stream=output)
1353 # Original logger output is empty.
1354 self.assert_log_lines([])
1355
Georg Brandl3dbca812008-07-23 16:10:53 +00001356 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001357 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001358 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001359 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001360 logger = logging.getLogger("compiler.parser")
1361 # Both will output a message
1362 logger.info(self.next_message())
1363 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001364 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001365 ('INFO', '1'),
1366 ('ERROR', '2'),
1367 ], stream=output)
1368 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001369 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001370
1371 def test_config2_failure(self):
1372 # A simple config file which overrides the default settings.
1373 self.assertRaises(Exception, self.apply_config, self.config2)
1374
1375 def test_config3_failure(self):
1376 # A simple config file which overrides the default settings.
1377 self.assertRaises(Exception, self.apply_config, self.config3)
1378
1379 def test_config4_ok(self):
1380 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001381 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001382 self.apply_config(self.config4)
1383 logger = logging.getLogger()
1384 try:
1385 raise RuntimeError()
1386 except RuntimeError:
1387 logging.exception("just testing")
1388 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001389 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001390 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1391 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001392 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001393
Georg Brandl3dbca812008-07-23 16:10:53 +00001394 def test_config5_ok(self):
1395 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001396
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001397 def test_config6_ok(self):
1398 self.test_config1_ok(config=self.config6)
1399
Vinay Sajip3f84b072011-03-07 17:49:33 +00001400 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001401 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001402 self.apply_config(self.config1a)
1403 logger = logging.getLogger("compiler.parser")
1404 # See issue #11424. compiler-hyphenated sorts
1405 # between compiler and compiler.xyz and this
1406 # was preventing compiler.xyz from being included
1407 # in the child loggers of compiler because of an
1408 # overzealous loop termination condition.
1409 hyphenated = logging.getLogger('compiler-hyphenated')
1410 # All will output a message
1411 logger.info(self.next_message())
1412 logger.error(self.next_message())
1413 hyphenated.critical(self.next_message())
1414 self.assert_log_lines([
1415 ('INFO', '1'),
1416 ('ERROR', '2'),
1417 ('CRITICAL', '3'),
1418 ], stream=output)
1419 # Original logger output is empty.
1420 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001421 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001422 self.apply_config(self.config7)
1423 logger = logging.getLogger("compiler.parser")
1424 self.assertFalse(logger.disabled)
1425 # Both will output a message
1426 logger.info(self.next_message())
1427 logger.error(self.next_message())
1428 logger = logging.getLogger("compiler.lexer")
1429 # Both will output a message
1430 logger.info(self.next_message())
1431 logger.error(self.next_message())
1432 # Will not appear
1433 hyphenated.critical(self.next_message())
1434 self.assert_log_lines([
1435 ('INFO', '4'),
1436 ('ERROR', '5'),
1437 ('INFO', '6'),
1438 ('ERROR', '7'),
1439 ], stream=output)
1440 # Original logger output is empty.
1441 self.assert_log_lines([])
1442
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001443 def test_logger_disabling(self):
1444 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001445 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001446 self.assertFalse(logger.disabled)
1447 self.apply_config(self.disable_test)
1448 self.assertTrue(logger.disabled)
1449 self.apply_config(self.disable_test, disable_existing_loggers=False)
1450 self.assertFalse(logger.disabled)
1451
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001452
Victor Stinner45df8202010-04-28 22:31:17 +00001453@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001454class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001455
Christian Heimes180510d2008-03-03 19:15:45 +00001456 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001457
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001458 if threading:
1459 server_class = TestTCPServer
1460 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001461
Christian Heimes180510d2008-03-03 19:15:45 +00001462 def setUp(self):
1463 """Set up a TCP server to receive log messages, and a SocketHandler
1464 pointing to that server's address and port."""
1465 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001466 # Issue #29177: deal with errors that happen during setup
1467 self.server = self.sock_hdlr = self.server_exception = None
1468 try:
1469 self.server = server = self.server_class(self.address,
1470 self.handle_socket, 0.01)
1471 server.start()
1472 # Uncomment next line to test error recovery in setUp()
1473 # raise OSError('dummy error raised')
1474 except OSError as e:
1475 self.server_exception = e
1476 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001477 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001478 hcls = logging.handlers.SocketHandler
1479 if isinstance(server.server_address, tuple):
1480 self.sock_hdlr = hcls('localhost', server.port)
1481 else:
1482 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001483 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001484 self.root_logger.removeHandler(self.root_logger.handlers[0])
1485 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001486 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001487
Christian Heimes180510d2008-03-03 19:15:45 +00001488 def tearDown(self):
1489 """Shutdown the TCP server."""
1490 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001491 if self.server:
1492 self.server.stop(2.0)
1493 if self.sock_hdlr:
1494 self.root_logger.removeHandler(self.sock_hdlr)
1495 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001496 finally:
1497 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001498
Vinay Sajip7367d082011-05-02 13:17:27 +01001499 def handle_socket(self, request):
1500 conn = request.connection
1501 while True:
1502 chunk = conn.recv(4)
1503 if len(chunk) < 4:
1504 break
1505 slen = struct.unpack(">L", chunk)[0]
1506 chunk = conn.recv(slen)
1507 while len(chunk) < slen:
1508 chunk = chunk + conn.recv(slen - len(chunk))
1509 obj = pickle.loads(chunk)
1510 record = logging.makeLogRecord(obj)
1511 self.log_output += record.msg + '\n'
1512 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001513
Christian Heimes180510d2008-03-03 19:15:45 +00001514 def test_output(self):
1515 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001516 if self.server_exception:
1517 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001518 logger = logging.getLogger("tcp")
1519 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001520 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001521 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001522 self.handled.acquire()
1523 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001524
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001525 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001526 if self.server_exception:
1527 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001528 # Avoid timing-related failures due to SocketHandler's own hard-wired
1529 # one-second timeout on socket.create_connection() (issue #16264).
1530 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001531 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001532 self.server.stop(2.0)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001533 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001534 try:
1535 raise RuntimeError('Deliberate mistake')
1536 except RuntimeError:
1537 self.root_logger.exception('Never sent')
1538 self.root_logger.error('Never sent, either')
1539 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001540 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001541 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1542 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001543
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001544def _get_temp_domain_socket():
1545 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1546 os.close(fd)
1547 # just need a name - file can't be present, or we'll get an
1548 # 'address already in use' error.
1549 os.remove(fn)
1550 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001551
Victor Stinnerec5a8602014-06-02 14:41:51 +02001552@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip7367d082011-05-02 13:17:27 +01001553@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip5421f352013-09-27 18:18:28 +01001554class UnixSocketHandlerTest(SocketHandlerTest):
1555
1556 """Test for SocketHandler with unix sockets."""
1557
Victor Stinnerec5a8602014-06-02 14:41:51 +02001558 if threading and hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001559 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001560
1561 def setUp(self):
1562 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001563 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001564 SocketHandlerTest.setUp(self)
1565
1566 def tearDown(self):
1567 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001568 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001569
1570@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip7367d082011-05-02 13:17:27 +01001571class DatagramHandlerTest(BaseTest):
1572
1573 """Test for DatagramHandler."""
1574
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001575 if threading:
1576 server_class = TestUDPServer
1577 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001578
Vinay Sajip7367d082011-05-02 13:17:27 +01001579 def setUp(self):
1580 """Set up a UDP server to receive log messages, and a DatagramHandler
1581 pointing to that server's address and port."""
1582 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001583 # Issue #29177: deal with errors that happen during setup
1584 self.server = self.sock_hdlr = self.server_exception = None
1585 try:
1586 self.server = server = self.server_class(self.address,
1587 self.handle_datagram, 0.01)
1588 server.start()
1589 # Uncomment next line to test error recovery in setUp()
1590 # raise OSError('dummy error raised')
1591 except OSError as e:
1592 self.server_exception = e
1593 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001594 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001595 hcls = logging.handlers.DatagramHandler
1596 if isinstance(server.server_address, tuple):
1597 self.sock_hdlr = hcls('localhost', server.port)
1598 else:
1599 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001600 self.log_output = ''
1601 self.root_logger.removeHandler(self.root_logger.handlers[0])
1602 self.root_logger.addHandler(self.sock_hdlr)
1603 self.handled = threading.Event()
1604
1605 def tearDown(self):
1606 """Shutdown the UDP server."""
1607 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001608 if self.server:
1609 self.server.stop(2.0)
1610 if self.sock_hdlr:
1611 self.root_logger.removeHandler(self.sock_hdlr)
1612 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001613 finally:
1614 BaseTest.tearDown(self)
1615
1616 def handle_datagram(self, request):
1617 slen = struct.pack('>L', 0) # length of prefix
1618 packet = request.packet[len(slen):]
1619 obj = pickle.loads(packet)
1620 record = logging.makeLogRecord(obj)
1621 self.log_output += record.msg + '\n'
1622 self.handled.set()
1623
1624 def test_output(self):
1625 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001626 if self.server_exception:
1627 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001628 logger = logging.getLogger("udp")
1629 logger.error("spam")
1630 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001631 self.handled.clear()
1632 logger.error("eggs")
1633 self.handled.wait()
1634 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001635
Victor Stinnerec5a8602014-06-02 14:41:51 +02001636@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip7367d082011-05-02 13:17:27 +01001637@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip5421f352013-09-27 18:18:28 +01001638class UnixDatagramHandlerTest(DatagramHandlerTest):
1639
1640 """Test for DatagramHandler using Unix sockets."""
1641
Victor Stinnerec5a8602014-06-02 14:41:51 +02001642 if threading and hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001643 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001644
1645 def setUp(self):
1646 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001647 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001648 DatagramHandlerTest.setUp(self)
1649
1650 def tearDown(self):
1651 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001652 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001653
1654@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip7367d082011-05-02 13:17:27 +01001655class SysLogHandlerTest(BaseTest):
1656
1657 """Test for SysLogHandler using UDP."""
1658
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001659 if threading:
1660 server_class = TestUDPServer
1661 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001662
Vinay Sajip7367d082011-05-02 13:17:27 +01001663 def setUp(self):
1664 """Set up a UDP server to receive log messages, and a SysLogHandler
1665 pointing to that server's address and port."""
1666 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001667 # Issue #29177: deal with errors that happen during setup
1668 self.server = self.sl_hdlr = self.server_exception = None
1669 try:
1670 self.server = server = self.server_class(self.address,
1671 self.handle_datagram, 0.01)
1672 server.start()
1673 # Uncomment next line to test error recovery in setUp()
1674 # raise OSError('dummy error raised')
1675 except OSError as e:
1676 self.server_exception = e
1677 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001678 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001679 hcls = logging.handlers.SysLogHandler
1680 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001681 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001682 else:
1683 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001684 self.log_output = ''
1685 self.root_logger.removeHandler(self.root_logger.handlers[0])
1686 self.root_logger.addHandler(self.sl_hdlr)
1687 self.handled = threading.Event()
1688
1689 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001690 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001691 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001692 if self.server:
1693 self.server.stop(2.0)
1694 if self.sl_hdlr:
1695 self.root_logger.removeHandler(self.sl_hdlr)
1696 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001697 finally:
1698 BaseTest.tearDown(self)
1699
1700 def handle_datagram(self, request):
1701 self.log_output = request.packet
1702 self.handled.set()
1703
1704 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001705 if self.server_exception:
1706 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001707 # The log message sent to the SysLogHandler is properly received.
1708 logger = logging.getLogger("slh")
1709 logger.error("sp\xe4m")
1710 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001711 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001712 self.handled.clear()
1713 self.sl_hdlr.append_nul = False
1714 logger.error("sp\xe4m")
1715 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001716 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001717 self.handled.clear()
1718 self.sl_hdlr.ident = "h\xe4m-"
1719 logger.error("sp\xe4m")
1720 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001721 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001722
Victor Stinnerec5a8602014-06-02 14:41:51 +02001723@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip7367d082011-05-02 13:17:27 +01001724@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip5421f352013-09-27 18:18:28 +01001725class UnixSysLogHandlerTest(SysLogHandlerTest):
1726
1727 """Test for SysLogHandler with Unix sockets."""
1728
Victor Stinnerec5a8602014-06-02 14:41:51 +02001729 if threading and hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001730 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001731
1732 def setUp(self):
1733 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001734 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001735 SysLogHandlerTest.setUp(self)
1736
1737 def tearDown(self):
1738 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001739 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001740
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001741@unittest.skipUnless(support.IPV6_ENABLED,
1742 'IPv6 support required for this test.')
1743@unittest.skipUnless(threading, 'Threading required for this test.')
1744class IPv6SysLogHandlerTest(SysLogHandlerTest):
1745
1746 """Test for SysLogHandler with IPv6 host."""
1747
1748 server_class = TestUDPServer
1749 address = ('::1', 0)
1750
1751 def setUp(self):
1752 self.server_class.address_family = socket.AF_INET6
1753 super(IPv6SysLogHandlerTest, self).setUp()
1754
1755 def tearDown(self):
1756 self.server_class.address_family = socket.AF_INET
1757 super(IPv6SysLogHandlerTest, self).tearDown()
1758
Vinay Sajip5421f352013-09-27 18:18:28 +01001759@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajip7367d082011-05-02 13:17:27 +01001760class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001761 """Test for HTTPHandler."""
1762
1763 def setUp(self):
1764 """Set up an HTTP server to receive log messages, and a HTTPHandler
1765 pointing to that server's address and port."""
1766 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001767 self.handled = threading.Event()
1768
Vinay Sajip7367d082011-05-02 13:17:27 +01001769 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001770 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001771 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001772 if self.command == 'POST':
1773 try:
1774 rlen = int(request.headers['Content-Length'])
1775 self.post_data = request.rfile.read(rlen)
1776 except:
1777 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001778 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001779 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001780 self.handled.set()
1781
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001782 @support.reap_threads
Vinay Sajip7367d082011-05-02 13:17:27 +01001783 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001784 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001785 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001786 root_logger = self.root_logger
1787 root_logger.removeHandler(self.root_logger.handlers[0])
1788 for secure in (False, True):
1789 addr = ('localhost', 0)
1790 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001791 try:
1792 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001793 except ImportError:
1794 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001795 else:
1796 here = os.path.dirname(__file__)
1797 localhost_cert = os.path.join(here, "keycert.pem")
1798 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1799 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06001800
1801 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01001802 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001803 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06001804 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01001805 self.server = server = TestHTTPServer(addr, self.handle_request,
1806 0.01, sslctx=sslctx)
1807 server.start()
1808 server.ready.wait()
1809 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001810 secure_client = secure and sslctx
1811 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06001812 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001813 context=context,
1814 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01001815 self.log_data = None
1816 root_logger.addHandler(self.h_hdlr)
1817
1818 for method in ('GET', 'POST'):
1819 self.h_hdlr.method = method
1820 self.handled.clear()
1821 msg = "sp\xe4m"
1822 logger.error(msg)
1823 self.handled.wait()
1824 self.assertEqual(self.log_data.path, '/frob')
1825 self.assertEqual(self.command, method)
1826 if method == 'GET':
1827 d = parse_qs(self.log_data.query)
1828 else:
1829 d = parse_qs(self.post_data.decode('utf-8'))
1830 self.assertEqual(d['name'], ['http'])
1831 self.assertEqual(d['funcName'], ['test_output'])
1832 self.assertEqual(d['msg'], [msg])
1833
1834 self.server.stop(2.0)
1835 self.root_logger.removeHandler(self.h_hdlr)
1836 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001837
Christian Heimes180510d2008-03-03 19:15:45 +00001838class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001839
Christian Heimes180510d2008-03-03 19:15:45 +00001840 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001841
Christian Heimes180510d2008-03-03 19:15:45 +00001842 def setUp(self):
1843 """Create a dict to remember potentially destroyed objects."""
1844 BaseTest.setUp(self)
1845 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001846
Christian Heimes180510d2008-03-03 19:15:45 +00001847 def _watch_for_survival(self, *args):
1848 """Watch the given objects for survival, by creating weakrefs to
1849 them."""
1850 for obj in args:
1851 key = id(obj), repr(obj)
1852 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001853
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001854 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001855 """Assert that all objects watched for survival have survived."""
1856 # Trigger cycle breaking.
1857 gc.collect()
1858 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001859 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001860 if ref() is None:
1861 dead.append(repr_)
1862 if dead:
1863 self.fail("%d objects should have survived "
1864 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001865
Christian Heimes180510d2008-03-03 19:15:45 +00001866 def test_persistent_loggers(self):
1867 # Logger objects are persistent and retain their configuration, even
1868 # if visible references are destroyed.
1869 self.root_logger.setLevel(logging.INFO)
1870 foo = logging.getLogger("foo")
1871 self._watch_for_survival(foo)
1872 foo.setLevel(logging.DEBUG)
1873 self.root_logger.debug(self.next_message())
1874 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001875 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001876 ('foo', 'DEBUG', '2'),
1877 ])
1878 del foo
1879 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001880 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001881 # foo has retained its settings.
1882 bar = logging.getLogger("foo")
1883 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001884 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001885 ('foo', 'DEBUG', '2'),
1886 ('foo', 'DEBUG', '3'),
1887 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001888
Benjamin Petersonf91df042009-02-13 02:50:59 +00001889
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001890class EncodingTest(BaseTest):
1891 def test_encoding_plain_file(self):
1892 # In Python 2.x, a plain file object is treated as having no encoding.
1893 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001894 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1895 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001896 # the non-ascii data we write to the log.
1897 data = "foo\x80"
1898 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001899 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001900 log.addHandler(handler)
1901 try:
1902 # write non-ascii data to the log.
1903 log.warning(data)
1904 finally:
1905 log.removeHandler(handler)
1906 handler.close()
1907 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001908 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001909 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001910 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001911 finally:
1912 f.close()
1913 finally:
1914 if os.path.isfile(fn):
1915 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001916
Benjamin Petersonf91df042009-02-13 02:50:59 +00001917 def test_encoding_cyrillic_unicode(self):
1918 log = logging.getLogger("test")
1919 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1920 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1921 #Ensure it's written in a Cyrillic encoding
1922 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001923 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001924 stream = io.BytesIO()
1925 writer = writer_class(stream, 'strict')
1926 handler = logging.StreamHandler(writer)
1927 log.addHandler(handler)
1928 try:
1929 log.warning(message)
1930 finally:
1931 log.removeHandler(handler)
1932 handler.close()
1933 # check we wrote exactly those bytes, ignoring trailing \n etc
1934 s = stream.getvalue()
1935 #Compare against what the data should be when encoded in CP-1251
1936 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1937
1938
Georg Brandlf9734072008-12-07 15:30:06 +00001939class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001940
Georg Brandlf9734072008-12-07 15:30:06 +00001941 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001942 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001943 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001944 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001945 warnings.filterwarnings("always", category=UserWarning)
1946 stream = io.StringIO()
1947 h = logging.StreamHandler(stream)
1948 logger = logging.getLogger("py.warnings")
1949 logger.addHandler(h)
1950 warnings.warn("I'm warning you...")
1951 logger.removeHandler(h)
1952 s = stream.getvalue()
1953 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001954 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001955
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001956 #See if an explicit file uses the original implementation
1957 a_file = io.StringIO()
1958 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1959 a_file, "Dummy line")
1960 s = a_file.getvalue()
1961 a_file.close()
1962 self.assertEqual(s,
1963 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1964
1965 def test_warnings_no_handlers(self):
1966 with warnings.catch_warnings():
1967 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001968 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001969
1970 # confirm our assumption: no loggers are set
1971 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001972 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001973
1974 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001975 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001976 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001977
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001978
1979def formatFunc(format, datefmt=None):
1980 return logging.Formatter(format, datefmt)
1981
1982def handlerFunc():
1983 return logging.StreamHandler()
1984
1985class CustomHandler(logging.StreamHandler):
1986 pass
1987
1988class ConfigDictTest(BaseTest):
1989
1990 """Reading logging config from a dictionary."""
1991
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001992 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001993
1994 # config0 is a standard configuration.
1995 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001996 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001997 'formatters': {
1998 'form1' : {
1999 'format' : '%(levelname)s ++ %(message)s',
2000 },
2001 },
2002 'handlers' : {
2003 'hand1' : {
2004 'class' : 'logging.StreamHandler',
2005 'formatter' : 'form1',
2006 'level' : 'NOTSET',
2007 'stream' : 'ext://sys.stdout',
2008 },
2009 },
2010 'root' : {
2011 'level' : 'WARNING',
2012 'handlers' : ['hand1'],
2013 },
2014 }
2015
2016 # config1 adds a little to the standard configuration.
2017 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002018 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002019 'formatters': {
2020 'form1' : {
2021 'format' : '%(levelname)s ++ %(message)s',
2022 },
2023 },
2024 'handlers' : {
2025 'hand1' : {
2026 'class' : 'logging.StreamHandler',
2027 'formatter' : 'form1',
2028 'level' : 'NOTSET',
2029 'stream' : 'ext://sys.stdout',
2030 },
2031 },
2032 'loggers' : {
2033 'compiler.parser' : {
2034 'level' : 'DEBUG',
2035 'handlers' : ['hand1'],
2036 },
2037 },
2038 'root' : {
2039 'level' : 'WARNING',
2040 },
2041 }
2042
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002043 # config1a moves the handler to the root. Used with config8a
2044 config1a = {
2045 'version': 1,
2046 'formatters': {
2047 'form1' : {
2048 'format' : '%(levelname)s ++ %(message)s',
2049 },
2050 },
2051 'handlers' : {
2052 'hand1' : {
2053 'class' : 'logging.StreamHandler',
2054 'formatter' : 'form1',
2055 'level' : 'NOTSET',
2056 'stream' : 'ext://sys.stdout',
2057 },
2058 },
2059 'loggers' : {
2060 'compiler.parser' : {
2061 'level' : 'DEBUG',
2062 },
2063 },
2064 'root' : {
2065 'level' : 'WARNING',
2066 'handlers' : ['hand1'],
2067 },
2068 }
2069
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002070 # config2 has a subtle configuration error that should be reported
2071 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002072 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002073 'formatters': {
2074 'form1' : {
2075 'format' : '%(levelname)s ++ %(message)s',
2076 },
2077 },
2078 'handlers' : {
2079 'hand1' : {
2080 'class' : 'logging.StreamHandler',
2081 'formatter' : 'form1',
2082 'level' : 'NOTSET',
2083 'stream' : 'ext://sys.stdbout',
2084 },
2085 },
2086 'loggers' : {
2087 'compiler.parser' : {
2088 'level' : 'DEBUG',
2089 'handlers' : ['hand1'],
2090 },
2091 },
2092 'root' : {
2093 'level' : 'WARNING',
2094 },
2095 }
2096
2097 #As config1 but with a misspelt level on a handler
2098 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002099 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002100 'formatters': {
2101 'form1' : {
2102 'format' : '%(levelname)s ++ %(message)s',
2103 },
2104 },
2105 'handlers' : {
2106 'hand1' : {
2107 'class' : 'logging.StreamHandler',
2108 'formatter' : 'form1',
2109 'level' : 'NTOSET',
2110 'stream' : 'ext://sys.stdout',
2111 },
2112 },
2113 'loggers' : {
2114 'compiler.parser' : {
2115 'level' : 'DEBUG',
2116 'handlers' : ['hand1'],
2117 },
2118 },
2119 'root' : {
2120 'level' : 'WARNING',
2121 },
2122 }
2123
2124
2125 #As config1 but with a misspelt level on a logger
2126 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002127 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002128 'formatters': {
2129 'form1' : {
2130 'format' : '%(levelname)s ++ %(message)s',
2131 },
2132 },
2133 'handlers' : {
2134 'hand1' : {
2135 'class' : 'logging.StreamHandler',
2136 'formatter' : 'form1',
2137 'level' : 'NOTSET',
2138 'stream' : 'ext://sys.stdout',
2139 },
2140 },
2141 'loggers' : {
2142 'compiler.parser' : {
2143 'level' : 'DEBUG',
2144 'handlers' : ['hand1'],
2145 },
2146 },
2147 'root' : {
2148 'level' : 'WRANING',
2149 },
2150 }
2151
2152 # config3 has a less subtle configuration error
2153 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002154 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002155 'formatters': {
2156 'form1' : {
2157 'format' : '%(levelname)s ++ %(message)s',
2158 },
2159 },
2160 'handlers' : {
2161 'hand1' : {
2162 'class' : 'logging.StreamHandler',
2163 'formatter' : 'misspelled_name',
2164 'level' : 'NOTSET',
2165 'stream' : 'ext://sys.stdout',
2166 },
2167 },
2168 'loggers' : {
2169 'compiler.parser' : {
2170 'level' : 'DEBUG',
2171 'handlers' : ['hand1'],
2172 },
2173 },
2174 'root' : {
2175 'level' : 'WARNING',
2176 },
2177 }
2178
2179 # config4 specifies a custom formatter class to be loaded
2180 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002181 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002182 'formatters': {
2183 'form1' : {
2184 '()' : __name__ + '.ExceptionFormatter',
2185 'format' : '%(levelname)s:%(name)s:%(message)s',
2186 },
2187 },
2188 'handlers' : {
2189 'hand1' : {
2190 'class' : 'logging.StreamHandler',
2191 'formatter' : 'form1',
2192 'level' : 'NOTSET',
2193 'stream' : 'ext://sys.stdout',
2194 },
2195 },
2196 'root' : {
2197 'level' : 'NOTSET',
2198 'handlers' : ['hand1'],
2199 },
2200 }
2201
2202 # As config4 but using an actual callable rather than a string
2203 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002204 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002205 'formatters': {
2206 'form1' : {
2207 '()' : ExceptionFormatter,
2208 'format' : '%(levelname)s:%(name)s:%(message)s',
2209 },
2210 'form2' : {
2211 '()' : __name__ + '.formatFunc',
2212 'format' : '%(levelname)s:%(name)s:%(message)s',
2213 },
2214 'form3' : {
2215 '()' : formatFunc,
2216 'format' : '%(levelname)s:%(name)s:%(message)s',
2217 },
2218 },
2219 'handlers' : {
2220 'hand1' : {
2221 'class' : 'logging.StreamHandler',
2222 'formatter' : 'form1',
2223 'level' : 'NOTSET',
2224 'stream' : 'ext://sys.stdout',
2225 },
2226 'hand2' : {
2227 '()' : handlerFunc,
2228 },
2229 },
2230 'root' : {
2231 'level' : 'NOTSET',
2232 'handlers' : ['hand1'],
2233 },
2234 }
2235
2236 # config5 specifies a custom handler class to be loaded
2237 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002238 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002239 'formatters': {
2240 'form1' : {
2241 'format' : '%(levelname)s ++ %(message)s',
2242 },
2243 },
2244 'handlers' : {
2245 'hand1' : {
2246 'class' : __name__ + '.CustomHandler',
2247 'formatter' : 'form1',
2248 'level' : 'NOTSET',
2249 'stream' : 'ext://sys.stdout',
2250 },
2251 },
2252 'loggers' : {
2253 'compiler.parser' : {
2254 'level' : 'DEBUG',
2255 'handlers' : ['hand1'],
2256 },
2257 },
2258 'root' : {
2259 'level' : 'WARNING',
2260 },
2261 }
2262
2263 # config6 specifies a custom handler class to be loaded
2264 # but has bad arguments
2265 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002266 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002267 'formatters': {
2268 'form1' : {
2269 'format' : '%(levelname)s ++ %(message)s',
2270 },
2271 },
2272 'handlers' : {
2273 'hand1' : {
2274 'class' : __name__ + '.CustomHandler',
2275 'formatter' : 'form1',
2276 'level' : 'NOTSET',
2277 'stream' : 'ext://sys.stdout',
2278 '9' : 'invalid parameter name',
2279 },
2280 },
2281 'loggers' : {
2282 'compiler.parser' : {
2283 'level' : 'DEBUG',
2284 'handlers' : ['hand1'],
2285 },
2286 },
2287 'root' : {
2288 'level' : 'WARNING',
2289 },
2290 }
2291
2292 #config 7 does not define compiler.parser but defines compiler.lexer
2293 #so compiler.parser should be disabled after applying it
2294 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002295 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002296 'formatters': {
2297 'form1' : {
2298 'format' : '%(levelname)s ++ %(message)s',
2299 },
2300 },
2301 'handlers' : {
2302 'hand1' : {
2303 'class' : 'logging.StreamHandler',
2304 'formatter' : 'form1',
2305 'level' : 'NOTSET',
2306 'stream' : 'ext://sys.stdout',
2307 },
2308 },
2309 'loggers' : {
2310 'compiler.lexer' : {
2311 'level' : 'DEBUG',
2312 'handlers' : ['hand1'],
2313 },
2314 },
2315 'root' : {
2316 'level' : 'WARNING',
2317 },
2318 }
2319
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002320 # config8 defines both compiler and compiler.lexer
2321 # so compiler.parser should not be disabled (since
2322 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002323 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002324 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002325 'disable_existing_loggers' : False,
2326 'formatters': {
2327 'form1' : {
2328 'format' : '%(levelname)s ++ %(message)s',
2329 },
2330 },
2331 'handlers' : {
2332 'hand1' : {
2333 'class' : 'logging.StreamHandler',
2334 'formatter' : 'form1',
2335 'level' : 'NOTSET',
2336 'stream' : 'ext://sys.stdout',
2337 },
2338 },
2339 'loggers' : {
2340 'compiler' : {
2341 'level' : 'DEBUG',
2342 'handlers' : ['hand1'],
2343 },
2344 'compiler.lexer' : {
2345 },
2346 },
2347 'root' : {
2348 'level' : 'WARNING',
2349 },
2350 }
2351
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002352 # config8a disables existing loggers
2353 config8a = {
2354 'version': 1,
2355 'disable_existing_loggers' : True,
2356 'formatters': {
2357 'form1' : {
2358 'format' : '%(levelname)s ++ %(message)s',
2359 },
2360 },
2361 'handlers' : {
2362 'hand1' : {
2363 'class' : 'logging.StreamHandler',
2364 'formatter' : 'form1',
2365 'level' : 'NOTSET',
2366 'stream' : 'ext://sys.stdout',
2367 },
2368 },
2369 'loggers' : {
2370 'compiler' : {
2371 'level' : 'DEBUG',
2372 'handlers' : ['hand1'],
2373 },
2374 'compiler.lexer' : {
2375 },
2376 },
2377 'root' : {
2378 'level' : 'WARNING',
2379 },
2380 }
2381
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002382 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002383 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002384 'formatters': {
2385 'form1' : {
2386 'format' : '%(levelname)s ++ %(message)s',
2387 },
2388 },
2389 'handlers' : {
2390 'hand1' : {
2391 'class' : 'logging.StreamHandler',
2392 'formatter' : 'form1',
2393 'level' : 'WARNING',
2394 'stream' : 'ext://sys.stdout',
2395 },
2396 },
2397 'loggers' : {
2398 'compiler.parser' : {
2399 'level' : 'WARNING',
2400 'handlers' : ['hand1'],
2401 },
2402 },
2403 'root' : {
2404 'level' : 'NOTSET',
2405 },
2406 }
2407
2408 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002409 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002410 'incremental' : True,
2411 'handlers' : {
2412 'hand1' : {
2413 'level' : 'WARNING',
2414 },
2415 },
2416 'loggers' : {
2417 'compiler.parser' : {
2418 'level' : 'INFO',
2419 },
2420 },
2421 }
2422
2423 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002424 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002425 'incremental' : True,
2426 'handlers' : {
2427 'hand1' : {
2428 'level' : 'INFO',
2429 },
2430 },
2431 'loggers' : {
2432 'compiler.parser' : {
2433 'level' : 'INFO',
2434 },
2435 },
2436 }
2437
2438 #As config1 but with a filter added
2439 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002440 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002441 'formatters': {
2442 'form1' : {
2443 'format' : '%(levelname)s ++ %(message)s',
2444 },
2445 },
2446 'filters' : {
2447 'filt1' : {
2448 'name' : 'compiler.parser',
2449 },
2450 },
2451 'handlers' : {
2452 'hand1' : {
2453 'class' : 'logging.StreamHandler',
2454 'formatter' : 'form1',
2455 'level' : 'NOTSET',
2456 'stream' : 'ext://sys.stdout',
2457 'filters' : ['filt1'],
2458 },
2459 },
2460 'loggers' : {
2461 'compiler.parser' : {
2462 'level' : 'DEBUG',
2463 'filters' : ['filt1'],
2464 },
2465 },
2466 'root' : {
2467 'level' : 'WARNING',
2468 'handlers' : ['hand1'],
2469 },
2470 }
2471
2472 #As config1 but using cfg:// references
2473 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002474 'version': 1,
2475 'true_formatters': {
2476 'form1' : {
2477 'format' : '%(levelname)s ++ %(message)s',
2478 },
2479 },
2480 'handler_configs': {
2481 'hand1' : {
2482 'class' : 'logging.StreamHandler',
2483 'formatter' : 'form1',
2484 'level' : 'NOTSET',
2485 'stream' : 'ext://sys.stdout',
2486 },
2487 },
2488 'formatters' : 'cfg://true_formatters',
2489 'handlers' : {
2490 'hand1' : 'cfg://handler_configs[hand1]',
2491 },
2492 'loggers' : {
2493 'compiler.parser' : {
2494 'level' : 'DEBUG',
2495 'handlers' : ['hand1'],
2496 },
2497 },
2498 'root' : {
2499 'level' : 'WARNING',
2500 },
2501 }
2502
2503 #As config11 but missing the version key
2504 config12 = {
2505 'true_formatters': {
2506 'form1' : {
2507 'format' : '%(levelname)s ++ %(message)s',
2508 },
2509 },
2510 'handler_configs': {
2511 'hand1' : {
2512 'class' : 'logging.StreamHandler',
2513 'formatter' : 'form1',
2514 'level' : 'NOTSET',
2515 'stream' : 'ext://sys.stdout',
2516 },
2517 },
2518 'formatters' : 'cfg://true_formatters',
2519 'handlers' : {
2520 'hand1' : 'cfg://handler_configs[hand1]',
2521 },
2522 'loggers' : {
2523 'compiler.parser' : {
2524 'level' : 'DEBUG',
2525 'handlers' : ['hand1'],
2526 },
2527 },
2528 'root' : {
2529 'level' : 'WARNING',
2530 },
2531 }
2532
2533 #As config11 but using an unsupported version
2534 config13 = {
2535 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002536 'true_formatters': {
2537 'form1' : {
2538 'format' : '%(levelname)s ++ %(message)s',
2539 },
2540 },
2541 'handler_configs': {
2542 'hand1' : {
2543 'class' : 'logging.StreamHandler',
2544 'formatter' : 'form1',
2545 'level' : 'NOTSET',
2546 'stream' : 'ext://sys.stdout',
2547 },
2548 },
2549 'formatters' : 'cfg://true_formatters',
2550 'handlers' : {
2551 'hand1' : 'cfg://handler_configs[hand1]',
2552 },
2553 'loggers' : {
2554 'compiler.parser' : {
2555 'level' : 'DEBUG',
2556 'handlers' : ['hand1'],
2557 },
2558 },
2559 'root' : {
2560 'level' : 'WARNING',
2561 },
2562 }
2563
Vinay Sajip8d270232012-11-15 14:20:18 +00002564 # As config0, but with properties
2565 config14 = {
2566 'version': 1,
2567 'formatters': {
2568 'form1' : {
2569 'format' : '%(levelname)s ++ %(message)s',
2570 },
2571 },
2572 'handlers' : {
2573 'hand1' : {
2574 'class' : 'logging.StreamHandler',
2575 'formatter' : 'form1',
2576 'level' : 'NOTSET',
2577 'stream' : 'ext://sys.stdout',
2578 '.': {
2579 'foo': 'bar',
2580 'terminator': '!\n',
2581 }
2582 },
2583 },
2584 'root' : {
2585 'level' : 'WARNING',
2586 'handlers' : ['hand1'],
2587 },
2588 }
2589
Vinay Sajip3f885b52013-03-22 15:19:54 +00002590 out_of_order = {
2591 "version": 1,
2592 "formatters": {
2593 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002594 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2595 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002596 }
2597 },
2598 "handlers": {
2599 "fileGlobal": {
2600 "class": "logging.StreamHandler",
2601 "level": "DEBUG",
2602 "formatter": "mySimpleFormatter"
2603 },
2604 "bufferGlobal": {
2605 "class": "logging.handlers.MemoryHandler",
2606 "capacity": 5,
2607 "formatter": "mySimpleFormatter",
2608 "target": "fileGlobal",
2609 "level": "DEBUG"
2610 }
2611 },
2612 "loggers": {
2613 "mymodule": {
2614 "level": "DEBUG",
2615 "handlers": ["bufferGlobal"],
2616 "propagate": "true"
2617 }
2618 }
2619 }
2620
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002621 def apply_config(self, conf):
2622 logging.config.dictConfig(conf)
2623
2624 def test_config0_ok(self):
2625 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002626 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002627 self.apply_config(self.config0)
2628 logger = logging.getLogger()
2629 # Won't output anything
2630 logger.info(self.next_message())
2631 # Outputs a message
2632 logger.error(self.next_message())
2633 self.assert_log_lines([
2634 ('ERROR', '2'),
2635 ], stream=output)
2636 # Original logger output is empty.
2637 self.assert_log_lines([])
2638
2639 def test_config1_ok(self, config=config1):
2640 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002641 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002642 self.apply_config(config)
2643 logger = logging.getLogger("compiler.parser")
2644 # Both will output a message
2645 logger.info(self.next_message())
2646 logger.error(self.next_message())
2647 self.assert_log_lines([
2648 ('INFO', '1'),
2649 ('ERROR', '2'),
2650 ], stream=output)
2651 # Original logger output is empty.
2652 self.assert_log_lines([])
2653
2654 def test_config2_failure(self):
2655 # A simple config which overrides the default settings.
2656 self.assertRaises(Exception, self.apply_config, self.config2)
2657
2658 def test_config2a_failure(self):
2659 # A simple config which overrides the default settings.
2660 self.assertRaises(Exception, self.apply_config, self.config2a)
2661
2662 def test_config2b_failure(self):
2663 # A simple config which overrides the default settings.
2664 self.assertRaises(Exception, self.apply_config, self.config2b)
2665
2666 def test_config3_failure(self):
2667 # A simple config which overrides the default settings.
2668 self.assertRaises(Exception, self.apply_config, self.config3)
2669
2670 def test_config4_ok(self):
2671 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002672 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002673 self.apply_config(self.config4)
2674 #logger = logging.getLogger()
2675 try:
2676 raise RuntimeError()
2677 except RuntimeError:
2678 logging.exception("just testing")
2679 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002680 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002681 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2682 # Original logger output is empty
2683 self.assert_log_lines([])
2684
2685 def test_config4a_ok(self):
2686 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002687 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002688 self.apply_config(self.config4a)
2689 #logger = logging.getLogger()
2690 try:
2691 raise RuntimeError()
2692 except RuntimeError:
2693 logging.exception("just testing")
2694 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002695 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002696 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2697 # Original logger output is empty
2698 self.assert_log_lines([])
2699
2700 def test_config5_ok(self):
2701 self.test_config1_ok(config=self.config5)
2702
2703 def test_config6_failure(self):
2704 self.assertRaises(Exception, self.apply_config, self.config6)
2705
2706 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002707 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002708 self.apply_config(self.config1)
2709 logger = logging.getLogger("compiler.parser")
2710 # Both will output a message
2711 logger.info(self.next_message())
2712 logger.error(self.next_message())
2713 self.assert_log_lines([
2714 ('INFO', '1'),
2715 ('ERROR', '2'),
2716 ], stream=output)
2717 # Original logger output is empty.
2718 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01002719 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002720 self.apply_config(self.config7)
2721 logger = logging.getLogger("compiler.parser")
2722 self.assertTrue(logger.disabled)
2723 logger = logging.getLogger("compiler.lexer")
2724 # Both will output a message
2725 logger.info(self.next_message())
2726 logger.error(self.next_message())
2727 self.assert_log_lines([
2728 ('INFO', '3'),
2729 ('ERROR', '4'),
2730 ], stream=output)
2731 # Original logger output is empty.
2732 self.assert_log_lines([])
2733
2734 #Same as test_config_7_ok but don't disable old loggers.
2735 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002736 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002737 self.apply_config(self.config1)
2738 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002739 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002740 logger.info(self.next_message())
2741 logger.error(self.next_message())
2742 self.assert_log_lines([
2743 ('INFO', '1'),
2744 ('ERROR', '2'),
2745 ], stream=output)
2746 # Original logger output is empty.
2747 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01002748 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002749 self.apply_config(self.config8)
2750 logger = logging.getLogger("compiler.parser")
2751 self.assertFalse(logger.disabled)
2752 # Both will output a message
2753 logger.info(self.next_message())
2754 logger.error(self.next_message())
2755 logger = logging.getLogger("compiler.lexer")
2756 # Both will output a message
2757 logger.info(self.next_message())
2758 logger.error(self.next_message())
2759 self.assert_log_lines([
2760 ('INFO', '3'),
2761 ('ERROR', '4'),
2762 ('INFO', '5'),
2763 ('ERROR', '6'),
2764 ], stream=output)
2765 # Original logger output is empty.
2766 self.assert_log_lines([])
2767
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002768 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002769 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002770 self.apply_config(self.config1a)
2771 logger = logging.getLogger("compiler.parser")
2772 # See issue #11424. compiler-hyphenated sorts
2773 # between compiler and compiler.xyz and this
2774 # was preventing compiler.xyz from being included
2775 # in the child loggers of compiler because of an
2776 # overzealous loop termination condition.
2777 hyphenated = logging.getLogger('compiler-hyphenated')
2778 # All will output a message
2779 logger.info(self.next_message())
2780 logger.error(self.next_message())
2781 hyphenated.critical(self.next_message())
2782 self.assert_log_lines([
2783 ('INFO', '1'),
2784 ('ERROR', '2'),
2785 ('CRITICAL', '3'),
2786 ], stream=output)
2787 # Original logger output is empty.
2788 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01002789 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002790 self.apply_config(self.config8a)
2791 logger = logging.getLogger("compiler.parser")
2792 self.assertFalse(logger.disabled)
2793 # Both will output a message
2794 logger.info(self.next_message())
2795 logger.error(self.next_message())
2796 logger = logging.getLogger("compiler.lexer")
2797 # Both will output a message
2798 logger.info(self.next_message())
2799 logger.error(self.next_message())
2800 # Will not appear
2801 hyphenated.critical(self.next_message())
2802 self.assert_log_lines([
2803 ('INFO', '4'),
2804 ('ERROR', '5'),
2805 ('INFO', '6'),
2806 ('ERROR', '7'),
2807 ], stream=output)
2808 # Original logger output is empty.
2809 self.assert_log_lines([])
2810
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002811 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002812 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002813 self.apply_config(self.config9)
2814 logger = logging.getLogger("compiler.parser")
2815 #Nothing will be output since both handler and logger are set to WARNING
2816 logger.info(self.next_message())
2817 self.assert_log_lines([], stream=output)
2818 self.apply_config(self.config9a)
2819 #Nothing will be output since both handler is still set to WARNING
2820 logger.info(self.next_message())
2821 self.assert_log_lines([], stream=output)
2822 self.apply_config(self.config9b)
2823 #Message should now be output
2824 logger.info(self.next_message())
2825 self.assert_log_lines([
2826 ('INFO', '3'),
2827 ], stream=output)
2828
2829 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002830 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002831 self.apply_config(self.config10)
2832 logger = logging.getLogger("compiler.parser")
2833 logger.warning(self.next_message())
2834 logger = logging.getLogger('compiler')
2835 #Not output, because filtered
2836 logger.warning(self.next_message())
2837 logger = logging.getLogger('compiler.lexer')
2838 #Not output, because filtered
2839 logger.warning(self.next_message())
2840 logger = logging.getLogger("compiler.parser.codegen")
2841 #Output, as not filtered
2842 logger.error(self.next_message())
2843 self.assert_log_lines([
2844 ('WARNING', '1'),
2845 ('ERROR', '4'),
2846 ], stream=output)
2847
2848 def test_config11_ok(self):
2849 self.test_config1_ok(self.config11)
2850
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002851 def test_config12_failure(self):
2852 self.assertRaises(Exception, self.apply_config, self.config12)
2853
2854 def test_config13_failure(self):
2855 self.assertRaises(Exception, self.apply_config, self.config13)
2856
Vinay Sajip8d270232012-11-15 14:20:18 +00002857 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002858 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00002859 self.apply_config(self.config14)
2860 h = logging._handlers['hand1']
2861 self.assertEqual(h.foo, 'bar')
2862 self.assertEqual(h.terminator, '!\n')
2863 logging.warning('Exclamation')
2864 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
2865
Victor Stinner45df8202010-04-28 22:31:17 +00002866 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajip4ded5512012-10-02 15:56:16 +01002867 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002868 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002869 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01002870 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002871 t.start()
2872 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002873 # Now get the port allocated
2874 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002875 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002876 try:
2877 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2878 sock.settimeout(2.0)
2879 sock.connect(('localhost', port))
2880
2881 slen = struct.pack('>L', len(text))
2882 s = slen + text
2883 sentsofar = 0
2884 left = len(s)
2885 while left > 0:
2886 sent = sock.send(s[sentsofar:])
2887 sentsofar += sent
2888 left -= sent
2889 sock.close()
2890 finally:
2891 t.ready.wait(2.0)
2892 logging.config.stopListening()
2893 t.join(2.0)
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02002894 if t.is_alive():
2895 self.fail("join() timed out")
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002896
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002897 @unittest.skipUnless(threading, 'Threading required for this test.')
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02002898 @support.reap_threads
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002899 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002900 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002901 self.setup_via_listener(json.dumps(self.config10))
2902 logger = logging.getLogger("compiler.parser")
2903 logger.warning(self.next_message())
2904 logger = logging.getLogger('compiler')
2905 #Not output, because filtered
2906 logger.warning(self.next_message())
2907 logger = logging.getLogger('compiler.lexer')
2908 #Not output, because filtered
2909 logger.warning(self.next_message())
2910 logger = logging.getLogger("compiler.parser.codegen")
2911 #Output, as not filtered
2912 logger.error(self.next_message())
2913 self.assert_log_lines([
2914 ('WARNING', '1'),
2915 ('ERROR', '4'),
2916 ], stream=output)
2917
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002918 @unittest.skipUnless(threading, 'Threading required for this test.')
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02002919 @support.reap_threads
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002920 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01002921 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002922 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2923 logger = logging.getLogger("compiler.parser")
2924 # Both will output a message
2925 logger.info(self.next_message())
2926 logger.error(self.next_message())
2927 self.assert_log_lines([
2928 ('INFO', '1'),
2929 ('ERROR', '2'),
2930 ], stream=output)
2931 # Original logger output is empty.
2932 self.assert_log_lines([])
2933
Vinay Sajip4ded5512012-10-02 15:56:16 +01002934 @unittest.skipUnless(threading, 'Threading required for this test.')
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02002935 @support.reap_threads
Vinay Sajip4ded5512012-10-02 15:56:16 +01002936 def test_listen_verify(self):
2937
2938 def verify_fail(stuff):
2939 return None
2940
2941 def verify_reverse(stuff):
2942 return stuff[::-1]
2943
2944 logger = logging.getLogger("compiler.parser")
2945 to_send = textwrap.dedent(ConfigFileTest.config1)
2946 # First, specify a verification function that will fail.
2947 # We expect to see no output, since our configuration
2948 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002949 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01002950 self.setup_via_listener(to_send, verify_fail)
2951 # Both will output a message
2952 logger.info(self.next_message())
2953 logger.error(self.next_message())
2954 self.assert_log_lines([], stream=output)
2955 # Original logger output has the stuff we logged.
2956 self.assert_log_lines([
2957 ('INFO', '1'),
2958 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002959 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01002960
2961 # Now, perform no verification. Our configuration
2962 # should take effect.
2963
Vinay Sajip1feedb42014-05-31 08:19:12 +01002964 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01002965 self.setup_via_listener(to_send) # no verify callable specified
2966 logger = logging.getLogger("compiler.parser")
2967 # Both will output a message
2968 logger.info(self.next_message())
2969 logger.error(self.next_message())
2970 self.assert_log_lines([
2971 ('INFO', '3'),
2972 ('ERROR', '4'),
2973 ], stream=output)
2974 # Original logger output still has the stuff we logged before.
2975 self.assert_log_lines([
2976 ('INFO', '1'),
2977 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002978 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01002979
2980 # Now, perform verification which transforms the bytes.
2981
Vinay Sajip1feedb42014-05-31 08:19:12 +01002982 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01002983 self.setup_via_listener(to_send[::-1], verify_reverse)
2984 logger = logging.getLogger("compiler.parser")
2985 # Both will output a message
2986 logger.info(self.next_message())
2987 logger.error(self.next_message())
2988 self.assert_log_lines([
2989 ('INFO', '5'),
2990 ('ERROR', '6'),
2991 ], stream=output)
2992 # Original logger output still has the stuff we logged before.
2993 self.assert_log_lines([
2994 ('INFO', '1'),
2995 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002996 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01002997
Vinay Sajip3f885b52013-03-22 15:19:54 +00002998 def test_out_of_order(self):
2999 self.apply_config(self.out_of_order)
3000 handler = logging.getLogger('mymodule').handlers[0]
3001 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003002 self.assertIsInstance(handler.formatter._style,
3003 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003004
Vinay Sajip373baef2011-04-26 20:05:24 +01003005 def test_baseconfig(self):
3006 d = {
3007 'atuple': (1, 2, 3),
3008 'alist': ['a', 'b', 'c'],
3009 'adict': {'d': 'e', 'f': 3 },
3010 'nest1': ('g', ('h', 'i'), 'j'),
3011 'nest2': ['k', ['l', 'm'], 'n'],
3012 'nest3': ['o', 'cfg://alist', 'p'],
3013 }
3014 bc = logging.config.BaseConfigurator(d)
3015 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3016 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3017 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3018 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3019 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3020 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3021 v = bc.convert('cfg://nest3')
3022 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3023 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3024 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3025 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003026
3027class ManagerTest(BaseTest):
3028 def test_manager_loggerclass(self):
3029 logged = []
3030
3031 class MyLogger(logging.Logger):
3032 def _log(self, level, msg, args, exc_info=None, extra=None):
3033 logged.append(msg)
3034
3035 man = logging.Manager(None)
3036 self.assertRaises(TypeError, man.setLoggerClass, int)
3037 man.setLoggerClass(MyLogger)
3038 logger = man.getLogger('test')
3039 logger.warning('should appear in logged')
3040 logging.warning('should not appear in logged')
3041
3042 self.assertEqual(logged, ['should appear in logged'])
3043
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003044 def test_set_log_record_factory(self):
3045 man = logging.Manager(None)
3046 expected = object()
3047 man.setLogRecordFactory(expected)
3048 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003049
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003050class ChildLoggerTest(BaseTest):
3051 def test_child_loggers(self):
3052 r = logging.getLogger()
3053 l1 = logging.getLogger('abc')
3054 l2 = logging.getLogger('def.ghi')
3055 c1 = r.getChild('xyz')
3056 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003057 self.assertIs(c1, logging.getLogger('xyz'))
3058 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003059 c1 = l1.getChild('def')
3060 c2 = c1.getChild('ghi')
3061 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003062 self.assertIs(c1, logging.getLogger('abc.def'))
3063 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3064 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003065
3066
Vinay Sajip6fac8172010-10-19 20:44:14 +00003067class DerivedLogRecord(logging.LogRecord):
3068 pass
3069
Vinay Sajip61561522010-12-03 11:50:38 +00003070class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003071
3072 def setUp(self):
3073 class CheckingFilter(logging.Filter):
3074 def __init__(self, cls):
3075 self.cls = cls
3076
3077 def filter(self, record):
3078 t = type(record)
3079 if t is not self.cls:
3080 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3081 self.cls)
3082 raise TypeError(msg)
3083 return True
3084
3085 BaseTest.setUp(self)
3086 self.filter = CheckingFilter(DerivedLogRecord)
3087 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003088 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003089
3090 def tearDown(self):
3091 self.root_logger.removeFilter(self.filter)
3092 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003093 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003094
3095 def test_logrecord_class(self):
3096 self.assertRaises(TypeError, self.root_logger.warning,
3097 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003098 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003099 self.root_logger.error(self.next_message())
3100 self.assert_log_lines([
3101 ('root', 'ERROR', '2'),
3102 ])
3103
3104
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003105class QueueHandlerTest(BaseTest):
3106 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003107 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003108
3109 def setUp(self):
3110 BaseTest.setUp(self)
3111 self.queue = queue.Queue(-1)
3112 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
3113 self.que_logger = logging.getLogger('que')
3114 self.que_logger.propagate = False
3115 self.que_logger.setLevel(logging.WARNING)
3116 self.que_logger.addHandler(self.que_hdlr)
3117
3118 def tearDown(self):
3119 self.que_hdlr.close()
3120 BaseTest.tearDown(self)
3121
3122 def test_queue_handler(self):
3123 self.que_logger.debug(self.next_message())
3124 self.assertRaises(queue.Empty, self.queue.get_nowait)
3125 self.que_logger.info(self.next_message())
3126 self.assertRaises(queue.Empty, self.queue.get_nowait)
3127 msg = self.next_message()
3128 self.que_logger.warning(msg)
3129 data = self.queue.get_nowait()
3130 self.assertTrue(isinstance(data, logging.LogRecord))
3131 self.assertEqual(data.name, self.que_logger.name)
3132 self.assertEqual((data.msg, data.args), (msg, None))
3133
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003134 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3135 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003136 def test_queue_listener(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003137 handler = support.TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003138 listener = logging.handlers.QueueListener(self.queue, handler)
3139 listener.start()
3140 try:
3141 self.que_logger.warning(self.next_message())
3142 self.que_logger.error(self.next_message())
3143 self.que_logger.critical(self.next_message())
3144 finally:
3145 listener.stop()
3146 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3147 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3148 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003149 handler.close()
3150
3151 # Now test with respect_handler_level set
3152
3153 handler = support.TestHandler(support.Matcher())
3154 handler.setLevel(logging.CRITICAL)
3155 listener = logging.handlers.QueueListener(self.queue, handler,
3156 respect_handler_level=True)
3157 listener.start()
3158 try:
3159 self.que_logger.warning(self.next_message())
3160 self.que_logger.error(self.next_message())
3161 self.que_logger.critical(self.next_message())
3162 finally:
3163 listener.stop()
3164 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3165 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3166 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
3167
Vinay Sajipd61910c2016-09-08 01:13:39 +01003168if hasattr(logging.handlers, 'QueueListener'):
3169 import multiprocessing
3170 from unittest.mock import patch
3171
3172 class QueueListenerTest(BaseTest):
3173 """
3174 Tests based on patch submitted for issue #27930. Ensure that
3175 QueueListener handles all log messages.
3176 """
3177
3178 repeat = 20
3179
3180 @staticmethod
3181 def setup_and_log(log_queue, ident):
3182 """
3183 Creates a logger with a QueueHandler that logs to a queue read by a
3184 QueueListener. Starts the listener, logs five messages, and stops
3185 the listener.
3186 """
3187 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3188 logger.setLevel(logging.DEBUG)
3189 handler = logging.handlers.QueueHandler(log_queue)
3190 logger.addHandler(handler)
3191 listener = logging.handlers.QueueListener(log_queue)
3192 listener.start()
3193
3194 logger.info('one')
3195 logger.info('two')
3196 logger.info('three')
3197 logger.info('four')
3198 logger.info('five')
3199
3200 listener.stop()
3201 logger.removeHandler(handler)
3202 handler.close()
3203
3204 @patch.object(logging.handlers.QueueListener, 'handle')
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003205 @support.reap_threads
Vinay Sajipd61910c2016-09-08 01:13:39 +01003206 def test_handle_called_with_queue_queue(self, mock_handle):
3207 for i in range(self.repeat):
3208 log_queue = queue.Queue()
3209 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3210 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3211 'correct number of handled log messages')
3212
Xavier de Gaye49e8f2d2016-11-15 16:57:52 +01003213 @support.requires_multiprocessing_queue
Vinay Sajipd61910c2016-09-08 01:13:39 +01003214 @patch.object(logging.handlers.QueueListener, 'handle')
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003215 @support.reap_threads
Vinay Sajipd61910c2016-09-08 01:13:39 +01003216 def test_handle_called_with_mp_queue(self, mock_handle):
3217 for i in range(self.repeat):
3218 log_queue = multiprocessing.Queue()
3219 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003220 log_queue.close()
3221 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003222 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3223 'correct number of handled log messages')
3224
3225 @staticmethod
3226 def get_all_from_queue(log_queue):
3227 try:
3228 while True:
3229 yield log_queue.get_nowait()
3230 except queue.Empty:
3231 return []
3232
Xavier de Gaye49e8f2d2016-11-15 16:57:52 +01003233 @support.requires_multiprocessing_queue
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003234 @support.reap_threads
Vinay Sajipd61910c2016-09-08 01:13:39 +01003235 def test_no_messages_in_queue_after_stop(self):
3236 """
3237 Five messages are logged then the QueueListener is stopped. This
3238 test then gets everything off the queue. Failure of this test
3239 indicates that messages were not registered on the queue until
3240 _after_ the QueueListener stopped.
3241 """
3242 for i in range(self.repeat):
3243 queue = multiprocessing.Queue()
3244 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3245 # time.sleep(1)
3246 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003247 queue.close()
3248 queue.join_thread()
3249
Vinay Sajipd61910c2016-09-08 01:13:39 +01003250 expected = [[], [logging.handlers.QueueListener._sentinel]]
3251 self.assertIn(items, expected,
3252 'Found unexpected messages in queue: %s' % (
3253 [m.msg if isinstance(m, logging.LogRecord)
3254 else m for m in items]))
3255
Vinay Sajipe723e962011-04-15 22:27:17 +01003256
Vinay Sajip37eb3382011-04-26 20:26:41 +01003257ZERO = datetime.timedelta(0)
3258
3259class UTC(datetime.tzinfo):
3260 def utcoffset(self, dt):
3261 return ZERO
3262
3263 dst = utcoffset
3264
3265 def tzname(self, dt):
3266 return 'UTC'
3267
3268utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003269
Vinay Sajipa39c5712010-10-25 13:57:39 +00003270class FormatterTest(unittest.TestCase):
3271 def setUp(self):
3272 self.common = {
3273 'name': 'formatter.test',
3274 'level': logging.DEBUG,
3275 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3276 'lineno': 42,
3277 'exc_info': None,
3278 'func': None,
3279 'msg': 'Message with %d %s',
3280 'args': (2, 'placeholders'),
3281 }
3282 self.variants = {
3283 }
3284
3285 def get_record(self, name=None):
3286 result = dict(self.common)
3287 if name is not None:
3288 result.update(self.variants[name])
3289 return logging.makeLogRecord(result)
3290
3291 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003292 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003293 r = self.get_record()
3294 f = logging.Formatter('${%(message)s}')
3295 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3296 f = logging.Formatter('%(random)s')
3297 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003298 self.assertFalse(f.usesTime())
3299 f = logging.Formatter('%(asctime)s')
3300 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003301 f = logging.Formatter('%(asctime)-15s')
3302 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003303 f = logging.Formatter('asctime')
3304 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003305
3306 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003307 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003308 r = self.get_record()
3309 f = logging.Formatter('$%{message}%$', style='{')
3310 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3311 f = logging.Formatter('{random}', style='{')
3312 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003313 self.assertFalse(f.usesTime())
3314 f = logging.Formatter('{asctime}', style='{')
3315 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003316 f = logging.Formatter('{asctime!s:15}', style='{')
3317 self.assertTrue(f.usesTime())
3318 f = logging.Formatter('{asctime:15}', style='{')
3319 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003320 f = logging.Formatter('asctime', style='{')
3321 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003322
3323 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003324 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003325 r = self.get_record()
3326 f = logging.Formatter('$message', style='$')
3327 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3328 f = logging.Formatter('$$%${message}%$$', style='$')
3329 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3330 f = logging.Formatter('${random}', style='$')
3331 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003332 self.assertFalse(f.usesTime())
3333 f = logging.Formatter('${asctime}', style='$')
3334 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003335 f = logging.Formatter('${asctime', style='$')
3336 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003337 f = logging.Formatter('$asctime', style='$')
3338 self.assertTrue(f.usesTime())
3339 f = logging.Formatter('asctime', style='$')
3340 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003341
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003342 def test_invalid_style(self):
3343 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3344
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003345 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003346 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003347 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3348 # We use None to indicate we want the local timezone
3349 # We're essentially converting a UTC time to local time
3350 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003351 r.msecs = 123
3352 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003353 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003354 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3355 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003356 f.format(r)
3357 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3358
3359class TestBufferingFormatter(logging.BufferingFormatter):
3360 def formatHeader(self, records):
3361 return '[(%d)' % len(records)
3362
3363 def formatFooter(self, records):
3364 return '(%d)]' % len(records)
3365
3366class BufferingFormatterTest(unittest.TestCase):
3367 def setUp(self):
3368 self.records = [
3369 logging.makeLogRecord({'msg': 'one'}),
3370 logging.makeLogRecord({'msg': 'two'}),
3371 ]
3372
3373 def test_default(self):
3374 f = logging.BufferingFormatter()
3375 self.assertEqual('', f.format([]))
3376 self.assertEqual('onetwo', f.format(self.records))
3377
3378 def test_custom(self):
3379 f = TestBufferingFormatter()
3380 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3381 lf = logging.Formatter('<%(message)s>')
3382 f = TestBufferingFormatter(lf)
3383 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003384
3385class ExceptionTest(BaseTest):
3386 def test_formatting(self):
3387 r = self.root_logger
3388 h = RecordingHandler()
3389 r.addHandler(h)
3390 try:
3391 raise RuntimeError('deliberate mistake')
3392 except:
3393 logging.exception('failed', stack_info=True)
3394 r.removeHandler(h)
3395 h.close()
3396 r = h.records[0]
3397 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3398 'call last):\n'))
3399 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
3400 'deliberate mistake'))
3401 self.assertTrue(r.stack_info.startswith('Stack (most recent '
3402 'call last):\n'))
3403 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
3404 'stack_info=True)'))
3405
3406
Vinay Sajip5a27d402010-12-10 11:42:57 +00003407class LastResortTest(BaseTest):
3408 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003409 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00003410 root = self.root_logger
3411 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00003412 old_lastresort = logging.lastResort
3413 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01003414
Vinay Sajip5a27d402010-12-10 11:42:57 +00003415 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01003416 with support.captured_stderr() as stderr:
3417 root.debug('This should not appear')
3418 self.assertEqual(stderr.getvalue(), '')
3419 root.warning('Final chance!')
3420 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
3421
3422 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00003423 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01003424 with support.captured_stderr() as stderr:
3425 root.warning('Final chance!')
3426 msg = 'No handlers could be found for logger "root"\n'
3427 self.assertEqual(stderr.getvalue(), msg)
3428
Vinay Sajip5a27d402010-12-10 11:42:57 +00003429 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01003430 with support.captured_stderr() as stderr:
3431 root.warning('Final chance!')
3432 self.assertEqual(stderr.getvalue(), '')
3433
3434 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00003435 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00003436 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01003437 with support.captured_stderr() as stderr:
3438 root.warning('Final chance!')
3439 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00003440 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00003441 root.addHandler(self.root_hdlr)
3442 logging.lastResort = old_lastresort
3443 logging.raiseExceptions = old_raise_exceptions
3444
3445
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003446class FakeHandler:
3447
3448 def __init__(self, identifier, called):
3449 for method in ('acquire', 'flush', 'close', 'release'):
3450 setattr(self, method, self.record_call(identifier, method, called))
3451
3452 def record_call(self, identifier, method_name, called):
3453 def inner():
3454 called.append('{} - {}'.format(identifier, method_name))
3455 return inner
3456
3457
3458class RecordingHandler(logging.NullHandler):
3459
3460 def __init__(self, *args, **kwargs):
3461 super(RecordingHandler, self).__init__(*args, **kwargs)
3462 self.records = []
3463
3464 def handle(self, record):
3465 """Keep track of all the emitted records."""
3466 self.records.append(record)
3467
3468
3469class ShutdownTest(BaseTest):
3470
Vinay Sajip5e66b162011-04-20 15:41:14 +01003471 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003472
3473 def setUp(self):
3474 super(ShutdownTest, self).setUp()
3475 self.called = []
3476
3477 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003478 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003479
3480 def raise_error(self, error):
3481 def inner():
3482 raise error()
3483 return inner
3484
3485 def test_no_failure(self):
3486 # create some fake handlers
3487 handler0 = FakeHandler(0, self.called)
3488 handler1 = FakeHandler(1, self.called)
3489 handler2 = FakeHandler(2, self.called)
3490
3491 # create live weakref to those handlers
3492 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3493
3494 logging.shutdown(handlerList=list(handlers))
3495
3496 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3497 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3498 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3499 self.assertEqual(expected, self.called)
3500
3501 def _test_with_failure_in_method(self, method, error):
3502 handler = FakeHandler(0, self.called)
3503 setattr(handler, method, self.raise_error(error))
3504 handlers = [logging.weakref.ref(handler)]
3505
3506 logging.shutdown(handlerList=list(handlers))
3507
3508 self.assertEqual('0 - release', self.called[-1])
3509
3510 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02003511 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003512
3513 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02003514 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003515
3516 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02003517 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003518
3519 def test_with_valueerror_in_acquire(self):
3520 self._test_with_failure_in_method('acquire', ValueError)
3521
3522 def test_with_valueerror_in_flush(self):
3523 self._test_with_failure_in_method('flush', ValueError)
3524
3525 def test_with_valueerror_in_close(self):
3526 self._test_with_failure_in_method('close', ValueError)
3527
3528 def test_with_other_error_in_acquire_without_raise(self):
3529 logging.raiseExceptions = False
3530 self._test_with_failure_in_method('acquire', IndexError)
3531
3532 def test_with_other_error_in_flush_without_raise(self):
3533 logging.raiseExceptions = False
3534 self._test_with_failure_in_method('flush', IndexError)
3535
3536 def test_with_other_error_in_close_without_raise(self):
3537 logging.raiseExceptions = False
3538 self._test_with_failure_in_method('close', IndexError)
3539
3540 def test_with_other_error_in_acquire_with_raise(self):
3541 logging.raiseExceptions = True
3542 self.assertRaises(IndexError, self._test_with_failure_in_method,
3543 'acquire', IndexError)
3544
3545 def test_with_other_error_in_flush_with_raise(self):
3546 logging.raiseExceptions = True
3547 self.assertRaises(IndexError, self._test_with_failure_in_method,
3548 'flush', IndexError)
3549
3550 def test_with_other_error_in_close_with_raise(self):
3551 logging.raiseExceptions = True
3552 self.assertRaises(IndexError, self._test_with_failure_in_method,
3553 'close', IndexError)
3554
3555
3556class ModuleLevelMiscTest(BaseTest):
3557
Vinay Sajip5e66b162011-04-20 15:41:14 +01003558 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003559
3560 def test_disable(self):
3561 old_disable = logging.root.manager.disable
3562 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003563 self.assertEqual(old_disable, 0)
3564 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003565
3566 logging.disable(83)
3567 self.assertEqual(logging.root.manager.disable, 83)
3568
Vinay Sajipd489ac92016-12-31 11:40:11 +00003569 # test the default value introduced in 3.7
3570 # (Issue #28524)
3571 logging.disable()
3572 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
3573
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003574 def _test_log(self, method, level=None):
3575 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01003576 support.patch(self, logging, 'basicConfig',
3577 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003578
3579 recording = RecordingHandler()
3580 logging.root.addHandler(recording)
3581
3582 log_method = getattr(logging, method)
3583 if level is not None:
3584 log_method(level, "test me: %r", recording)
3585 else:
3586 log_method("test me: %r", recording)
3587
3588 self.assertEqual(len(recording.records), 1)
3589 record = recording.records[0]
3590 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3591
3592 expected_level = level if level is not None else getattr(logging, method.upper())
3593 self.assertEqual(record.levelno, expected_level)
3594
3595 # basicConfig was not called!
3596 self.assertEqual(called, [])
3597
3598 def test_log(self):
3599 self._test_log('log', logging.ERROR)
3600
3601 def test_debug(self):
3602 self._test_log('debug')
3603
3604 def test_info(self):
3605 self._test_log('info')
3606
3607 def test_warning(self):
3608 self._test_log('warning')
3609
3610 def test_error(self):
3611 self._test_log('error')
3612
3613 def test_critical(self):
3614 self._test_log('critical')
3615
3616 def test_set_logger_class(self):
3617 self.assertRaises(TypeError, logging.setLoggerClass, object)
3618
3619 class MyLogger(logging.Logger):
3620 pass
3621
3622 logging.setLoggerClass(MyLogger)
3623 self.assertEqual(logging.getLoggerClass(), MyLogger)
3624
3625 logging.setLoggerClass(logging.Logger)
3626 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3627
Serhiy Storchakaa7930372016-07-03 22:27:26 +03003628 @support.requires_type_collecting
Antoine Pitrou712cb732013-12-21 15:51:54 +01003629 def test_logging_at_shutdown(self):
3630 # Issue #20037
3631 code = """if 1:
3632 import logging
3633
3634 class A:
3635 def __del__(self):
3636 try:
3637 raise ValueError("some error")
3638 except Exception:
3639 logging.exception("exception in __del__")
3640
3641 a = A()"""
3642 rc, out, err = assert_python_ok("-c", code)
3643 err = err.decode()
3644 self.assertIn("exception in __del__", err)
3645 self.assertIn("ValueError: some error", err)
3646
3647
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003648class LogRecordTest(BaseTest):
3649 def test_str_rep(self):
3650 r = logging.makeLogRecord({})
3651 s = str(r)
3652 self.assertTrue(s.startswith('<LogRecord: '))
3653 self.assertTrue(s.endswith('>'))
3654
3655 def test_dict_arg(self):
3656 h = RecordingHandler()
3657 r = logging.getLogger()
3658 r.addHandler(h)
3659 d = {'less' : 'more' }
3660 logging.warning('less is %(less)s', d)
3661 self.assertIs(h.records[0].args, d)
3662 self.assertEqual(h.records[0].message, 'less is more')
3663 r.removeHandler(h)
3664 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003665
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003666 def test_multiprocessing(self):
3667 r = logging.makeLogRecord({})
3668 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003669 try:
3670 import multiprocessing as mp
3671 r = logging.makeLogRecord({})
3672 self.assertEqual(r.processName, mp.current_process().name)
3673 except ImportError:
3674 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003675
3676 def test_optional(self):
3677 r = logging.makeLogRecord({})
3678 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003679 if threading:
3680 NOT_NONE(r.thread)
3681 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003682 NOT_NONE(r.process)
3683 NOT_NONE(r.processName)
3684 log_threads = logging.logThreads
3685 log_processes = logging.logProcesses
3686 log_multiprocessing = logging.logMultiprocessing
3687 try:
3688 logging.logThreads = False
3689 logging.logProcesses = False
3690 logging.logMultiprocessing = False
3691 r = logging.makeLogRecord({})
3692 NONE = self.assertIsNone
3693 NONE(r.thread)
3694 NONE(r.threadName)
3695 NONE(r.process)
3696 NONE(r.processName)
3697 finally:
3698 logging.logThreads = log_threads
3699 logging.logProcesses = log_processes
3700 logging.logMultiprocessing = log_multiprocessing
3701
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003702class BasicConfigTest(unittest.TestCase):
3703
Vinay Sajip95bf5042011-04-20 11:50:56 +01003704 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003705
3706 def setUp(self):
3707 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003708 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003709 self.saved_handlers = logging._handlers.copy()
3710 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003711 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003712 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003713 logging.root.handlers = []
3714
3715 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003716 for h in logging.root.handlers[:]:
3717 logging.root.removeHandler(h)
3718 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003719 super(BasicConfigTest, self).tearDown()
3720
Vinay Sajip3def7e02011-04-20 10:58:06 +01003721 def cleanup(self):
3722 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003723 logging._handlers.clear()
3724 logging._handlers.update(self.saved_handlers)
3725 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003726 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003727
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003728 def test_no_kwargs(self):
3729 logging.basicConfig()
3730
3731 # handler defaults to a StreamHandler to sys.stderr
3732 self.assertEqual(len(logging.root.handlers), 1)
3733 handler = logging.root.handlers[0]
3734 self.assertIsInstance(handler, logging.StreamHandler)
3735 self.assertEqual(handler.stream, sys.stderr)
3736
3737 formatter = handler.formatter
3738 # format defaults to logging.BASIC_FORMAT
3739 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3740 # datefmt defaults to None
3741 self.assertIsNone(formatter.datefmt)
3742 # style defaults to %
3743 self.assertIsInstance(formatter._style, logging.PercentStyle)
3744
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003745 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003746 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003747
Vinay Sajip1fd12022014-01-13 21:59:56 +00003748 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003749 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00003750 logging.basicConfig(stream=sys.stdout, style="{")
3751 logging.error("Log an error")
3752 sys.stdout.seek(0)
3753 self.assertEqual(output.getvalue().strip(),
3754 "ERROR:root:Log an error")
3755
3756 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003757 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00003758 logging.basicConfig(stream=sys.stdout, style="$")
3759 logging.error("Log an error")
3760 sys.stdout.seek(0)
3761 self.assertEqual(output.getvalue().strip(),
3762 "ERROR:root:Log an error")
3763
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003764 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00003765
3766 def cleanup(h1, h2, fn):
3767 h1.close()
3768 h2.close()
3769 os.remove(fn)
3770
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003771 logging.basicConfig(filename='test.log')
3772
3773 self.assertEqual(len(logging.root.handlers), 1)
3774 handler = logging.root.handlers[0]
3775 self.assertIsInstance(handler, logging.FileHandler)
3776
3777 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003778 self.assertEqual(handler.stream.mode, expected.stream.mode)
3779 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00003780 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003781
3782 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00003783
3784 def cleanup(h1, h2, fn):
3785 h1.close()
3786 h2.close()
3787 os.remove(fn)
3788
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003789 logging.basicConfig(filename='test.log', filemode='wb')
3790
3791 handler = logging.root.handlers[0]
3792 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003793 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00003794 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003795
3796 def test_stream(self):
3797 stream = io.StringIO()
3798 self.addCleanup(stream.close)
3799 logging.basicConfig(stream=stream)
3800
3801 self.assertEqual(len(logging.root.handlers), 1)
3802 handler = logging.root.handlers[0]
3803 self.assertIsInstance(handler, logging.StreamHandler)
3804 self.assertEqual(handler.stream, stream)
3805
3806 def test_format(self):
3807 logging.basicConfig(format='foo')
3808
3809 formatter = logging.root.handlers[0].formatter
3810 self.assertEqual(formatter._style._fmt, 'foo')
3811
3812 def test_datefmt(self):
3813 logging.basicConfig(datefmt='bar')
3814
3815 formatter = logging.root.handlers[0].formatter
3816 self.assertEqual(formatter.datefmt, 'bar')
3817
3818 def test_style(self):
3819 logging.basicConfig(style='$')
3820
3821 formatter = logging.root.handlers[0].formatter
3822 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3823
3824 def test_level(self):
3825 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003826 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003827
3828 logging.basicConfig(level=57)
3829 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003830 # Test that second call has no effect
3831 logging.basicConfig(level=58)
3832 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003833
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003834 def test_incompatible(self):
3835 assertRaises = self.assertRaises
3836 handlers = [logging.StreamHandler()]
3837 stream = sys.stderr
3838 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3839 stream=stream)
3840 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3841 handlers=handlers)
3842 assertRaises(ValueError, logging.basicConfig, stream=stream,
3843 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00003844 # Issue 23207: test for invalid kwargs
3845 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
3846 # Should pop both filename and filemode even if filename is None
3847 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003848
3849 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003850 handlers = [
3851 logging.StreamHandler(),
3852 logging.StreamHandler(sys.stdout),
3853 logging.StreamHandler(),
3854 ]
3855 f = logging.Formatter()
3856 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003857 logging.basicConfig(handlers=handlers)
3858 self.assertIs(handlers[0], logging.root.handlers[0])
3859 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003860 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003861 self.assertIsNotNone(handlers[0].formatter)
3862 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003863 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003864 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3865
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003866 def _test_log(self, method, level=None):
3867 # logging.root has no handlers so basicConfig should be called
3868 called = []
3869
3870 old_basic_config = logging.basicConfig
3871 def my_basic_config(*a, **kw):
3872 old_basic_config()
3873 old_level = logging.root.level
3874 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003875 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003876 called.append((a, kw))
3877
Vinay Sajip1feedb42014-05-31 08:19:12 +01003878 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003879
3880 log_method = getattr(logging, method)
3881 if level is not None:
3882 log_method(level, "test me")
3883 else:
3884 log_method("test me")
3885
3886 # basicConfig was called with no arguments
3887 self.assertEqual(called, [((), {})])
3888
3889 def test_log(self):
3890 self._test_log('log', logging.WARNING)
3891
3892 def test_debug(self):
3893 self._test_log('debug')
3894
3895 def test_info(self):
3896 self._test_log('info')
3897
3898 def test_warning(self):
3899 self._test_log('warning')
3900
3901 def test_error(self):
3902 self._test_log('error')
3903
3904 def test_critical(self):
3905 self._test_log('critical')
3906
3907
3908class LoggerAdapterTest(unittest.TestCase):
3909
3910 def setUp(self):
3911 super(LoggerAdapterTest, self).setUp()
3912 old_handler_list = logging._handlerList[:]
3913
3914 self.recording = RecordingHandler()
3915 self.logger = logging.root
3916 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003917 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003918 self.addCleanup(self.recording.close)
3919
3920 def cleanup():
3921 logging._handlerList[:] = old_handler_list
3922
3923 self.addCleanup(cleanup)
3924 self.addCleanup(logging.shutdown)
3925 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3926
3927 def test_exception(self):
3928 msg = 'testing exception: %r'
3929 exc = None
3930 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003931 1 / 0
3932 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003933 exc = e
3934 self.adapter.exception(msg, self.recording)
3935
3936 self.assertEqual(len(self.recording.records), 1)
3937 record = self.recording.records[0]
3938 self.assertEqual(record.levelno, logging.ERROR)
3939 self.assertEqual(record.msg, msg)
3940 self.assertEqual(record.args, (self.recording,))
3941 self.assertEqual(record.exc_info,
3942 (exc.__class__, exc, exc.__traceback__))
3943
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01003944 def test_exception_excinfo(self):
3945 try:
3946 1 / 0
3947 except ZeroDivisionError as e:
3948 exc = e
3949
3950 self.adapter.exception('exc_info test', exc_info=exc)
3951
3952 self.assertEqual(len(self.recording.records), 1)
3953 record = self.recording.records[0]
3954 self.assertEqual(record.exc_info,
3955 (exc.__class__, exc, exc.__traceback__))
3956
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003957 def test_critical(self):
3958 msg = 'critical test! %r'
3959 self.adapter.critical(msg, self.recording)
3960
3961 self.assertEqual(len(self.recording.records), 1)
3962 record = self.recording.records[0]
3963 self.assertEqual(record.levelno, logging.CRITICAL)
3964 self.assertEqual(record.msg, msg)
3965 self.assertEqual(record.args, (self.recording,))
3966
3967 def test_is_enabled_for(self):
3968 old_disable = self.adapter.logger.manager.disable
3969 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003970 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3971 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003972 self.assertFalse(self.adapter.isEnabledFor(32))
3973
3974 def test_has_handlers(self):
3975 self.assertTrue(self.adapter.hasHandlers())
3976
3977 for handler in self.logger.handlers:
3978 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003979
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003980 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003981 self.assertFalse(self.adapter.hasHandlers())
3982
3983
3984class LoggerTest(BaseTest):
3985
3986 def setUp(self):
3987 super(LoggerTest, self).setUp()
3988 self.recording = RecordingHandler()
3989 self.logger = logging.Logger(name='blah')
3990 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003991 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003992 self.addCleanup(self.recording.close)
3993 self.addCleanup(logging.shutdown)
3994
3995 def test_set_invalid_level(self):
3996 self.assertRaises(TypeError, self.logger.setLevel, object())
3997
3998 def test_exception(self):
3999 msg = 'testing exception: %r'
4000 exc = None
4001 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004002 1 / 0
4003 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004004 exc = e
4005 self.logger.exception(msg, self.recording)
4006
4007 self.assertEqual(len(self.recording.records), 1)
4008 record = self.recording.records[0]
4009 self.assertEqual(record.levelno, logging.ERROR)
4010 self.assertEqual(record.msg, msg)
4011 self.assertEqual(record.args, (self.recording,))
4012 self.assertEqual(record.exc_info,
4013 (exc.__class__, exc, exc.__traceback__))
4014
4015 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004016 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004017 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004018
4019 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004020 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004021 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004022
4023 def test_find_caller_with_stack_info(self):
4024 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004025 support.patch(self, logging.traceback, 'print_stack',
4026 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004027
4028 self.logger.findCaller(stack_info=True)
4029
4030 self.assertEqual(len(called), 1)
4031 self.assertEqual('Stack (most recent call last):\n', called[0])
4032
4033 def test_make_record_with_extra_overwrite(self):
4034 name = 'my record'
4035 level = 13
4036 fn = lno = msg = args = exc_info = func = sinfo = None
4037 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4038 exc_info, func, sinfo)
4039
4040 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4041 extra = {key: 'some value'}
4042 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4043 fn, lno, msg, args, exc_info,
4044 extra=extra, sinfo=sinfo)
4045
4046 def test_make_record_with_extra_no_overwrite(self):
4047 name = 'my record'
4048 level = 13
4049 fn = lno = msg = args = exc_info = func = sinfo = None
4050 extra = {'valid_key': 'some value'}
4051 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4052 exc_info, extra=extra, sinfo=sinfo)
4053 self.assertIn('valid_key', result.__dict__)
4054
4055 def test_has_handlers(self):
4056 self.assertTrue(self.logger.hasHandlers())
4057
4058 for handler in self.logger.handlers:
4059 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004060 self.assertFalse(self.logger.hasHandlers())
4061
4062 def test_has_handlers_no_propagate(self):
4063 child_logger = logging.getLogger('blah.child')
4064 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004065 self.assertFalse(child_logger.hasHandlers())
4066
4067 def test_is_enabled_for(self):
4068 old_disable = self.logger.manager.disable
4069 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004070 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004071 self.assertFalse(self.logger.isEnabledFor(22))
4072
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004073 def test_root_logger_aliases(self):
4074 root = logging.getLogger()
4075 self.assertIs(root, logging.root)
4076 self.assertIs(root, logging.getLogger(None))
4077 self.assertIs(root, logging.getLogger(''))
4078 self.assertIs(root, logging.getLogger('foo').root)
4079 self.assertIs(root, logging.getLogger('foo.bar').root)
4080 self.assertIs(root, logging.getLogger('foo').parent)
4081
4082 self.assertIsNot(root, logging.getLogger('\0'))
4083 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4084
4085 def test_invalid_names(self):
4086 self.assertRaises(TypeError, logging.getLogger, any)
4087 self.assertRaises(TypeError, logging.getLogger, b'foo')
4088
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004089 def test_pickling(self):
4090 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4091 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4092 logger = logging.getLogger(name)
4093 s = pickle.dumps(logger, proto)
4094 unpickled = pickle.loads(s)
4095 self.assertIs(unpickled, logger)
4096
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004097
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004098class BaseFileTest(BaseTest):
4099 "Base class for handler tests that write log files"
4100
4101 def setUp(self):
4102 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004103 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
4104 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004105 self.rmfiles = []
4106
4107 def tearDown(self):
4108 for fn in self.rmfiles:
4109 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004110 if os.path.exists(self.fn):
4111 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00004112 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004113
4114 def assertLogFile(self, filename):
4115 "Assert a log file is there and register it for deletion"
4116 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01004117 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004118 self.rmfiles.append(filename)
4119
4120
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004121class FileHandlerTest(BaseFileTest):
4122 def test_delay(self):
4123 os.unlink(self.fn)
4124 fh = logging.FileHandler(self.fn, delay=True)
4125 self.assertIsNone(fh.stream)
4126 self.assertFalse(os.path.exists(self.fn))
4127 fh.handle(logging.makeLogRecord({}))
4128 self.assertIsNotNone(fh.stream)
4129 self.assertTrue(os.path.exists(self.fn))
4130 fh.close()
4131
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004132class RotatingFileHandlerTest(BaseFileTest):
4133 def next_rec(self):
4134 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
4135 self.next_message(), None, None, None)
4136
4137 def test_should_not_rollover(self):
4138 # If maxbytes is zero rollover never occurs
4139 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
4140 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00004141 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004142
4143 def test_should_rollover(self):
4144 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
4145 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00004146 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004147
4148 def test_file_created(self):
4149 # checks that the file is created and assumes it was created
4150 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004151 rh = logging.handlers.RotatingFileHandler(self.fn)
4152 rh.emit(self.next_rec())
4153 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00004154 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004155
4156 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00004157 def namer(name):
4158 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004159 rh = logging.handlers.RotatingFileHandler(
4160 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00004161 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004162 rh.emit(self.next_rec())
4163 self.assertLogFile(self.fn)
4164 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00004165 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004166 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00004167 self.assertLogFile(namer(self.fn + ".2"))
4168 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
4169 rh.close()
4170
Vinay Sajip1feedb42014-05-31 08:19:12 +01004171 @support.requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00004172 def test_rotator(self):
4173 def namer(name):
4174 return name + ".gz"
4175
4176 def rotator(source, dest):
4177 with open(source, "rb") as sf:
4178 data = sf.read()
4179 compressed = zlib.compress(data, 9)
4180 with open(dest, "wb") as df:
4181 df.write(compressed)
4182 os.remove(source)
4183
4184 rh = logging.handlers.RotatingFileHandler(
4185 self.fn, backupCount=2, maxBytes=1)
4186 rh.rotator = rotator
4187 rh.namer = namer
4188 m1 = self.next_rec()
4189 rh.emit(m1)
4190 self.assertLogFile(self.fn)
4191 m2 = self.next_rec()
4192 rh.emit(m2)
4193 fn = namer(self.fn + ".1")
4194 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00004195 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00004196 with open(fn, "rb") as f:
4197 compressed = f.read()
4198 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00004199 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00004200 rh.emit(self.next_rec())
4201 fn = namer(self.fn + ".2")
4202 self.assertLogFile(fn)
4203 with open(fn, "rb") as f:
4204 compressed = f.read()
4205 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00004206 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00004207 rh.emit(self.next_rec())
4208 fn = namer(self.fn + ".2")
4209 with open(fn, "rb") as f:
4210 compressed = f.read()
4211 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00004212 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00004213 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00004214 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004215
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004216class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01004217 # other test methods added below
4218 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01004219 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
4220 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00004221 fmt = logging.Formatter('%(asctime)s %(message)s')
4222 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00004223 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00004224 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01004225 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00004226 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00004227 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00004228 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01004229 fh.close()
4230 # At this point, we should have a recent rotated file which we
4231 # can test for the existence of. However, in practice, on some
4232 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01004233 # in time to go to look for the log file. So, we go back a fair
4234 # bit, and stop as soon as we see a rotated file. In theory this
4235 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01004236 found = False
4237 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01004238 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01004239 for secs in range(GO_BACK):
4240 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01004241 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
4242 found = os.path.exists(fn)
4243 if found:
4244 self.rmfiles.append(fn)
4245 break
Vinay Sajip672c5812011-05-13 07:09:40 +01004246 msg = 'No rotated files found, went back %d seconds' % GO_BACK
4247 if not found:
4248 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01004249 dn, fn = os.path.split(self.fn)
4250 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02004251 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
4252 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00004253 for f in files:
4254 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00004255 path = os.path.join(dn, f)
4256 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00004257 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01004258 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004259
Vinay Sajip0372e102011-05-05 12:59:14 +01004260 def test_invalid(self):
4261 assertRaises = self.assertRaises
4262 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01004263 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01004264 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01004265 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01004266 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01004267 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01004268
Vinay Sajipa7130792013-04-12 17:04:23 +01004269 def test_compute_rollover_daily_attime(self):
4270 currentTime = 0
4271 atTime = datetime.time(12, 0, 0)
4272 rh = logging.handlers.TimedRotatingFileHandler(
4273 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
4274 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01004275 try:
4276 actual = rh.computeRollover(currentTime)
4277 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01004278
Vinay Sajipd86ac962013-04-14 12:20:46 +01004279 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
4280 self.assertEqual(actual, currentTime + 36 * 60 * 60)
4281 finally:
4282 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01004283
Vinay Sajip10e8c492013-05-18 10:19:54 -07004284 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01004285 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01004286 currentTime = int(time.time())
4287 today = currentTime - currentTime % 86400
4288
Vinay Sajipa7130792013-04-12 17:04:23 +01004289 atTime = datetime.time(12, 0, 0)
4290
Vinay Sajip10e8c492013-05-18 10:19:54 -07004291 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01004292 for day in range(7):
4293 rh = logging.handlers.TimedRotatingFileHandler(
4294 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
4295 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01004296 try:
4297 if wday > day:
4298 # The rollover day has already passed this week, so we
4299 # go over into next week
4300 expected = (7 - wday + day)
4301 else:
4302 expected = (day - wday)
4303 # At this point expected is in days from now, convert to seconds
4304 expected *= 24 * 60 * 60
4305 # Add in the rollover time
4306 expected += 12 * 60 * 60
4307 # Add in adjustment for today
4308 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01004309 actual = rh.computeRollover(today)
4310 if actual != expected:
4311 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01004312 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01004313 self.assertEqual(actual, expected)
4314 if day == wday:
4315 # goes into following week
4316 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01004317 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01004318 if actual != expected:
4319 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01004320 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01004321 self.assertEqual(actual, expected)
4322 finally:
4323 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01004324
4325
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004326def secs(**kw):
4327 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
4328
4329for when, exp in (('S', 1),
4330 ('M', 60),
4331 ('H', 60 * 60),
4332 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00004333 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004334 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00004335 ('W0', secs(days=4, hours=24)),
4336 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004337 def test_compute_rollover(self, when=when, exp=exp):
4338 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00004339 self.fn, when=when, interval=1, backupCount=0, utc=True)
4340 currentTime = 0.0
4341 actual = rh.computeRollover(currentTime)
4342 if exp != actual:
4343 # Failures occur on some systems for MIDNIGHT and W0.
4344 # Print detailed calculation for MIDNIGHT so we can try to see
4345 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00004346 if when == 'MIDNIGHT':
4347 try:
4348 if rh.utc:
4349 t = time.gmtime(currentTime)
4350 else:
4351 t = time.localtime(currentTime)
4352 currentHour = t[3]
4353 currentMinute = t[4]
4354 currentSecond = t[5]
4355 # r is the number of seconds left between now and midnight
4356 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
4357 currentMinute) * 60 +
4358 currentSecond)
4359 result = currentTime + r
4360 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
4361 print('currentHour: %s' % currentHour, file=sys.stderr)
4362 print('currentMinute: %s' % currentMinute, file=sys.stderr)
4363 print('currentSecond: %s' % currentSecond, file=sys.stderr)
4364 print('r: %s' % r, file=sys.stderr)
4365 print('result: %s' % result, file=sys.stderr)
4366 except Exception:
4367 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
4368 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00004369 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004370 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
4371
Vinay Sajip60ccd822011-05-09 17:32:09 +01004372
Vinay Sajip223349c2015-10-01 20:37:54 +01004373@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01004374class NTEventLogHandlerTest(BaseTest):
4375 def test_basic(self):
4376 logtype = 'Application'
4377 elh = win32evtlog.OpenEventLog(None, logtype)
4378 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01004379
4380 try:
4381 h = logging.handlers.NTEventLogHandler('test_logging')
4382 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05004383 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01004384 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05004385 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01004386
Vinay Sajip60ccd822011-05-09 17:32:09 +01004387 r = logging.makeLogRecord({'msg': 'Test Log Message'})
4388 h.handle(r)
4389 h.close()
4390 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02004391 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01004392 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
4393 win32evtlog.EVENTLOG_SEQUENTIAL_READ
4394 found = False
4395 GO_BACK = 100
4396 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
4397 for e in events:
4398 if e.SourceName != 'test_logging':
4399 continue
4400 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
4401 if msg != 'Test Log Message\r\n':
4402 continue
4403 found = True
4404 break
4405 msg = 'Record not found in event log, went back %d records' % GO_BACK
4406 self.assertTrue(found, msg=msg)
4407
Vinay Sajipdb8f4632016-09-08 01:37:03 +01004408
4409class MiscTestCase(unittest.TestCase):
4410 def test__all__(self):
4411 blacklist = {'logThreads', 'logMultiprocessing',
4412 'logProcesses', 'currentframe',
4413 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
4414 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00004415 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01004416 support.check__all__(self, logging, blacklist=blacklist)
4417
4418
Christian Heimes180510d2008-03-03 19:15:45 +00004419# Set the locale to the platform-dependent default. I have no idea
4420# why the test does this, but in any case we save the current locale
4421# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01004422@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00004423def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01004424 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01004425 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
4426 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
4427 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
4428 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
4429 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
4430 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
4431 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
4432 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08004433 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01004434 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01004435 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01004436 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01004437 ]
4438 if hasattr(logging.handlers, 'QueueListener'):
4439 tests.append(QueueListenerTest)
4440 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00004441
Christian Heimes180510d2008-03-03 19:15:45 +00004442if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00004443 test_main()