Merge lp:~jml/python-fixtures/logger into lp:~python-fixtures/python-fixtures/trunk

Proposed by Jonathan Lange
Status: Merged
Merged at revision: 60
Proposed branch: lp:~jml/python-fixtures/logger
Merge into: lp:~python-fixtures/python-fixtures/trunk
Diff against target: 196 lines (+109/-13)
4 files modified
lib/fixtures/__init__.py (+2/-0)
lib/fixtures/_fixtures/__init__.py (+2/-0)
lib/fixtures/_fixtures/logger.py (+39/-12)
lib/fixtures/tests/_fixtures/test_logger.py (+66/-1)
To merge this branch: bzr merge lp:~jml/python-fixtures/logger
Reviewer Review Type Date Requested Status
Robert Collins Approve
Review via email: mp+139867@code.launchpad.net

Commit message

Make the logging handler more generic.

Description of the change

Make the logger fixture a bit more generic by parametrizing the handler.

Change the existing logger fixture to use the newer more generic one.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

77 + if self._nuke_handlers:
78 + for handler in reversed(logger.handlers):
79 + logger.removeHandler(handler)
80 + self.addCleanup(logger.addHandler, handler)

line 80 should be before 79 I think.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/fixtures/__init__.py'
2--- lib/fixtures/__init__.py 2012-07-22 09:17:32 +0000
3+++ lib/fixtures/__init__.py 2012-12-14 10:50:04 +0000
4@@ -46,6 +46,7 @@
5 'Fixture',
6 'FunctionFixture',
7 'LoggerFixture',
8+ 'LogHandler',
9 'MethodFixture',
10 'MonkeyPatch',
11 'NestedTempfile',
12@@ -72,6 +73,7 @@
13 FakeLogger,
14 FakePopen,
15 LoggerFixture,
16+ LogHandler,
17 MonkeyPatch,
18 NestedTempfile,
19 PackagePathEntry,
20
21=== modified file 'lib/fixtures/_fixtures/__init__.py'
22--- lib/fixtures/_fixtures/__init__.py 2012-07-22 09:17:32 +0000
23+++ lib/fixtures/_fixtures/__init__.py 2012-12-14 10:50:04 +0000
24@@ -22,6 +22,7 @@
25 'FakeLogger',
26 'FakePopen',
27 'LoggerFixture',
28+ 'LogHandler',
29 'MonkeyPatch',
30 'NestedTempfile',
31 'PackagePathEntry',
32@@ -42,6 +43,7 @@
33 from fixtures._fixtures.logger import (
34 FakeLogger,
35 LoggerFixture,
36+ LogHandler,
37 )
38 from fixtures._fixtures.monkeypatch import MonkeyPatch
39 from fixtures._fixtures.popen import (
40
41=== modified file 'lib/fixtures/_fixtures/logger.py'
42--- lib/fixtures/_fixtures/logger.py 2012-06-07 20:08:37 +0000
43+++ lib/fixtures/_fixtures/logger.py 2012-12-14 10:50:04 +0000
44@@ -24,9 +24,45 @@
45 __all__ = [
46 'FakeLogger',
47 'LoggerFixture',
48+ 'LogHandler',
49 ]
50
51
52+class LogHandler(Fixture):
53+ """Replace a logger's handlers."""
54+
55+ def __init__(self, handler, name="", level=None, nuke_handlers=True):
56+ """Create a LogHandler fixture.
57+
58+ :param handler: The handler to replace other handlers with.
59+ If nuke_handlers is False, then added as an extra handler.
60+ :param name: The name of the logger to replace. Defaults to "".
61+ :param level: The log level to set, defaults to not changing the level.
62+ :param nuke_handlers: If True remove all existing handles (prevents
63+ existing messages going to e.g. stdout). Defaults to True.
64+ """
65+ super(LogHandler, self).__init__()
66+ self.handler = handler
67+ self._name = name
68+ self._level = level
69+ self._nuke_handlers = nuke_handlers
70+
71+ def setUp(self):
72+ super(LogHandler, self).setUp()
73+ logger = getLogger(self._name)
74+ if self._level:
75+ self.addCleanup(logger.setLevel, logger.level)
76+ logger.setLevel(self._level)
77+ if self._nuke_handlers:
78+ for handler in reversed(logger.handlers):
79+ logger.removeHandler(handler)
80+ self.addCleanup(logger.addHandler, handler)
81+ try:
82+ logger.addHandler(self.handler)
83+ finally:
84+ self.addCleanup(logger.removeHandler, self.handler)
85+
86+
87 class FakeLogger(Fixture):
88 """Replace a logger and capture its output."""
89
90@@ -60,21 +96,12 @@
91 u"pythonlogging:'%s'" % self._name,
92 Content(UTF8_TEXT, lambda: [output.getvalue()]))
93 self._output = output
94- logger = getLogger(self._name)
95- if self._level:
96- self.addCleanup(logger.setLevel, logger.level)
97- logger.setLevel(self._level)
98- if self._nuke_handlers:
99- for handler in reversed(logger.handlers):
100- logger.removeHandler(handler)
101- self.addCleanup(logger.addHandler, handler)
102 handler = StreamHandler(output)
103 if self._format:
104 handler.setFormatter(Formatter(self._format))
105- try:
106- logger.addHandler(handler)
107- finally:
108- self.addCleanup(logger.removeHandler, handler)
109+ self.useFixture(
110+ LogHandler(handler, name=self._name, level=self._level,
111+ nuke_handlers=self._nuke_handlers))
112
113 @property
114 def output(self):
115
116=== modified file 'lib/fixtures/tests/_fixtures/test_logger.py'
117--- lib/fixtures/tests/_fixtures/test_logger.py 2012-06-07 20:08:37 +0000
118+++ lib/fixtures/tests/_fixtures/test_logger.py 2012-12-14 10:50:04 +0000
119@@ -18,7 +18,11 @@
120 from testtools import TestCase
121 from cStringIO import StringIO
122
123-from fixtures import FakeLogger, TestWithFixtures
124+from fixtures import (
125+ FakeLogger,
126+ LogHandler,
127+ TestWithFixtures,
128+ )
129
130
131 class FakeLoggerTest(TestCase, TestWithFixtures):
132@@ -90,3 +94,64 @@
133 self.assertEqual("some message\n", content.as_text())
134 # A new one returns the new output:
135 self.assertEqual("", fixture.getDetails()[detail_name].as_text())
136+
137+
138+class LogHandlerTest(TestCase, TestWithFixtures):
139+
140+ class CustomHandler(logging.Handler):
141+
142+ def __init__(self, *args, **kwargs):
143+ """Create the instance, and add a records attribute."""
144+ logging.Handler.__init__(self, *args, **kwargs)
145+ self.msgs = []
146+
147+ def emit(self, record):
148+ self.msgs.append(record.msg)
149+
150+ def setUp(self):
151+ super(LogHandlerTest, self).setUp()
152+ self.logger = logging.getLogger()
153+ self.addCleanup(self.removeHandlers, self.logger)
154+
155+ def removeHandlers(self, logger):
156+ for handler in logger.handlers:
157+ logger.removeHandler(handler)
158+
159+ def test_captures_logging(self):
160+ fixture = self.useFixture(LogHandler(self.CustomHandler()))
161+ logging.info("some message")
162+ self.assertEqual(["some message"], fixture.handler.msgs)
163+
164+ def test_replace_and_restore_handlers(self):
165+ stream = StringIO()
166+ logger = logging.getLogger()
167+ logger.addHandler(logging.StreamHandler(stream))
168+ logger.setLevel(logging.INFO)
169+ logging.info("one")
170+ fixture = LogHandler(self.CustomHandler())
171+ with fixture:
172+ logging.info("two")
173+ logging.info("three")
174+ self.assertEqual(["two"], fixture.handler.msgs)
175+ self.assertEqual("one\nthree\n", stream.getvalue())
176+
177+ def test_preserving_existing_handlers(self):
178+ stream = StringIO()
179+ self.logger.addHandler(logging.StreamHandler(stream))
180+ self.logger.setLevel(logging.INFO)
181+ fixture = LogHandler(self.CustomHandler(), nuke_handlers=False)
182+ with fixture:
183+ logging.info("message")
184+ self.assertEqual(["message"], fixture.handler.msgs)
185+ self.assertEqual("message\n", stream.getvalue())
186+
187+ def test_logging_level_restored(self):
188+ self.logger.setLevel(logging.DEBUG)
189+ fixture = LogHandler(self.CustomHandler(), level=logging.WARNING)
190+ with fixture:
191+ # The fixture won't capture this, because the DEBUG level
192+ # is lower than the WARNING one
193+ logging.debug("debug message")
194+ self.assertEqual(logging.WARNING, self.logger.level)
195+ self.assertEqual([], fixture.handler.msgs)
196+ self.assertEqual(logging.DEBUG, self.logger.level)

Subscribers

People subscribed via source and target branches