From 0ea7e20ab6334e7ef5614df61c3548fa0d463e88 Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 14:00:44 +0000 Subject: Add MementoHandler. --- lib/fixtures/_fixtures/logger.py | 17 ++++++++++++++++- lib/fixtures/tests/_fixtures/test_logger.py | 23 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index 4b85b22..a00bba4 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -13,7 +13,7 @@ # license you chose for the specific language governing permissions and # limitations under that license. -from logging import StreamHandler, getLogger, INFO, Formatter +from logging import StreamHandler, getLogger, INFO, Formatter, Handler from cStringIO import StringIO from testtools.content import Content @@ -82,3 +82,18 @@ class FakeLogger(Fixture): LoggerFixture = FakeLogger + + +class MementoHandler(Handler): + """A handler class which stores logging records in a list. + + From http://nessita.pastebin.com/mgc85uQT + """ + def __init__(self, *args, **kwargs): + """Create the instance, and add a records attribute.""" + Handler.__init__(self, *args, **kwargs) + self.records = [] + + def emit(self, record): + """Just add the record to self.records.""" + self.records.append(record) diff --git a/lib/fixtures/tests/_fixtures/test_logger.py b/lib/fixtures/tests/_fixtures/test_logger.py index 9b88b5f..b1e4615 100644 --- a/lib/fixtures/tests/_fixtures/test_logger.py +++ b/lib/fixtures/tests/_fixtures/test_logger.py @@ -18,7 +18,11 @@ import logging from testtools import TestCase from cStringIO import StringIO -from fixtures import FakeLogger, TestWithFixtures +from fixtures import ( + FakeLogger, + TestWithFixtures, + ) +from fixtures._fixtures.logger import MementoHandler class FakeLoggerTest(TestCase, TestWithFixtures): @@ -90,3 +94,20 @@ class FakeLoggerTest(TestCase, TestWithFixtures): self.assertEqual("some message\n", content.as_text()) # A new one returns the new output: self.assertEqual("", fixture.getDetails()[detail_name].as_text()) + + +class TestMementoHandler(TestCase): + + def test_initialy_no_records(self): + handler = MementoHandler() + self.assertEqual([], handler.records) + + def test_emit_stored_in_records(self): + handler = MementoHandler() + marker = object() + handler.emit(marker) + self.assertEqual([marker], handler.records) + + def test_is_log_handler(self): + handler = MementoHandler() + self.assertIsInstance(handler, logging.Handler) -- cgit v1.2.1 From d62aacc622e9ffe55083ab8050019a5e20b83371 Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 14:22:23 +0000 Subject: Extract the handler managing bit of FakeLogger into its own fixture. --- lib/fixtures/_fixtures/logger.py | 50 ++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index a00bba4..dd6dc85 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -27,6 +27,41 @@ __all__ = [ ] +class LogHandler(Fixture): + """Replace a logger's handlers.""" + + def __init__(self, handler, name="", level=None, nuke_handlers=True): + """Create a LogHandler fixture. + + :param handler: The handler to replace other handlers with. + If nuke_handlers is False, then added as an extra handler. + :param name: The name of the logger to replace. Defaults to "". + :param level: The log level to set, defaults to not changing the level. + :param nuke_handlers: If True remove all existing handles (prevents + existing messages going to e.g. stdout). Defaults to True. + """ + super(LogHandler, self).__init__() + self._handler = handler + self._name = name + self._level = level + self._nuke_handlers = nuke_handlers + + def setUp(self): + super(LogHandler, self).setUp() + logger = getLogger(self._name) + if self._level: + self.addCleanup(logger.setLevel, logger.level) + logger.setLevel(self._level) + if self._nuke_handlers: + for handler in reversed(logger.handlers): + logger.removeHandler(handler) + self.addCleanup(logger.addHandler, handler) + try: + logger.addHandler(self._handler) + finally: + self.addCleanup(logger.removeHandler, self._handler) + + class FakeLogger(Fixture): """Replace a logger and capture its output.""" @@ -60,21 +95,12 @@ class FakeLogger(Fixture): u"pythonlogging:'%s'" % self._name, Content(UTF8_TEXT, lambda: [output.getvalue()])) self._output = output - logger = getLogger(self._name) - if self._level: - self.addCleanup(logger.setLevel, logger.level) - logger.setLevel(self._level) - if self._nuke_handlers: - for handler in reversed(logger.handlers): - logger.removeHandler(handler) - self.addCleanup(logger.addHandler, handler) handler = StreamHandler(output) if self._format: handler.setFormatter(Formatter(self._format)) - try: - logger.addHandler(handler) - finally: - self.addCleanup(logger.removeHandler, handler) + self.useFixture( + LogHandler(handler, name=self._name, level=self._level, + nuke_handlers=self._nuke_handlers)) @property def output(self): -- cgit v1.2.1 From d1d0d970987e923367e3cbe1723e6fc05e016352 Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 14:32:28 +0000 Subject: Make handler public. --- lib/fixtures/_fixtures/logger.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index dd6dc85..1cd0ce2 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -41,7 +41,7 @@ class LogHandler(Fixture): existing messages going to e.g. stdout). Defaults to True. """ super(LogHandler, self).__init__() - self._handler = handler + self.handler = handler self._name = name self._level = level self._nuke_handlers = nuke_handlers @@ -57,9 +57,9 @@ class LogHandler(Fixture): logger.removeHandler(handler) self.addCleanup(logger.addHandler, handler) try: - logger.addHandler(self._handler) + logger.addHandler(self.handler) finally: - self.addCleanup(logger.removeHandler, self._handler) + self.addCleanup(logger.removeHandler, self.handler) class FakeLogger(Fixture): -- cgit v1.2.1 From 98dd5d5c6559056670575cf2a6e171e2869597ef Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 15:13:52 +0000 Subject: Change the MementoHandler to store a dict. Start testing the logger fixture. --- lib/fixtures/__init__.py | 2 ++ lib/fixtures/_fixtures/__init__.py | 2 ++ lib/fixtures/_fixtures/logger.py | 31 +++++++++++++++++++++++-- lib/fixtures/tests/_fixtures/test_logger.py | 35 +++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/lib/fixtures/__init__.py b/lib/fixtures/__init__.py index c0b0d48..46f2139 100644 --- a/lib/fixtures/__init__.py +++ b/lib/fixtures/__init__.py @@ -46,6 +46,7 @@ __all__ = [ 'Fixture', 'FunctionFixture', 'LoggerFixture', + 'MementoLogger', 'MethodFixture', 'MonkeyPatch', 'NestedTempfile', @@ -72,6 +73,7 @@ from fixtures._fixtures import ( FakeLogger, FakePopen, LoggerFixture, + MementoLogger, MonkeyPatch, NestedTempfile, PackagePathEntry, diff --git a/lib/fixtures/_fixtures/__init__.py b/lib/fixtures/_fixtures/__init__.py index ec88780..da2a70b 100644 --- a/lib/fixtures/_fixtures/__init__.py +++ b/lib/fixtures/_fixtures/__init__.py @@ -22,6 +22,7 @@ __all__ = [ 'FakeLogger', 'FakePopen', 'LoggerFixture', + 'MementoLogger', 'MonkeyPatch', 'NestedTempfile', 'PackagePathEntry', @@ -42,6 +43,7 @@ from fixtures._fixtures.environ import ( from fixtures._fixtures.logger import ( FakeLogger, LoggerFixture, + MementoLogger, ) from fixtures._fixtures.monkeypatch import MonkeyPatch from fixtures._fixtures.popen import ( diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index 1cd0ce2..2b60205 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -121,5 +121,32 @@ class MementoHandler(Handler): self.records = [] def emit(self, record): - """Just add the record to self.records.""" - self.records.append(record) + """Just add the dict of the record to self.records.""" + # logging actually uses LogRecord.__dict__ regularly. Sheesh. + self.records.append(record.__dict__) + + +class MementoLogger(Fixture): + + def __init__(self, name="", level=INFO, nuke_handlers=True): + """Create a MementoLogger fixture. + + :param name: The name of the logger to replace. Defaults to "". + :param level: The log level to set, defaults to INFO. + :param nuke_handlers: If True remove all existing handles (prevents + existing messages going to e.g. stdout). Defaults to True. + """ + super(MementoLogger, self).__init__() + self._name = name + self._level = level + self._nuke_handlers = nuke_handlers + + def setUp(self): + super(MementoLogger, self).setUp() + self._handler = MementoHandler() + self.useFixture( + LogHandler(self._handler, name=self._name, level=self._level, + nuke_handlers=self._nuke_handlers)) + + def get_records(self): + return self._handler.records diff --git a/lib/fixtures/tests/_fixtures/test_logger.py b/lib/fixtures/tests/_fixtures/test_logger.py index b1e4615..361a377 100644 --- a/lib/fixtures/tests/_fixtures/test_logger.py +++ b/lib/fixtures/tests/_fixtures/test_logger.py @@ -16,10 +16,16 @@ import logging from testtools import TestCase +from testtools.matchers import ( + ContainsDict, + Equals, + MatchesListwise, + ) from cStringIO import StringIO from fixtures import ( FakeLogger, + MementoLogger, TestWithFixtures, ) from fixtures._fixtures.logger import MementoHandler @@ -98,16 +104,41 @@ class FakeLoggerTest(TestCase, TestWithFixtures): class TestMementoHandler(TestCase): + class FakeRecord(object): + def __init__(self, **kw): + self.__dict__.update(kw) + def test_initialy_no_records(self): handler = MementoHandler() self.assertEqual([], handler.records) def test_emit_stored_in_records(self): handler = MementoHandler() - marker = object() + marker = self.FakeRecord(foo='bar') handler.emit(marker) - self.assertEqual([marker], handler.records) + self.assertEqual([{'foo': 'bar'}], handler.records) def test_is_log_handler(self): handler = MementoHandler() self.assertIsInstance(handler, logging.Handler) + + +class MementoLoggerTest(TestCase, TestWithFixtures): + + def setUp(self): + super(MementoLoggerTest, self).setUp() + self.logger = logging.getLogger() + self.addCleanup(self.removeHandlers, self.logger) + + def removeHandlers(self, logger): + for handler in logger.handlers: + logger.removeHandler(handler) + + def test_get_records_has_records(self): + fixture = self.useFixture(MementoLogger()) + logging.info("some message") + self.assertThat( + fixture.get_records(), + MatchesListwise( + [ContainsDict({'msg': Equals("some message"), + 'levelname': Equals('INFO')})])) -- cgit v1.2.1 From f67cb5ae95d46231d9c93666bc31f351fe3d3644 Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 15:28:12 +0000 Subject: Give up on MementoHandler, just test LogHandler instead. --- lib/fixtures/__init__.py | 4 ++-- lib/fixtures/_fixtures/__init__.py | 4 ++-- lib/fixtures/_fixtures/logger.py | 1 + lib/fixtures/tests/_fixtures/test_logger.py | 31 +++++++++++++++-------------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/fixtures/__init__.py b/lib/fixtures/__init__.py index 46f2139..5863c52 100644 --- a/lib/fixtures/__init__.py +++ b/lib/fixtures/__init__.py @@ -46,7 +46,7 @@ __all__ = [ 'Fixture', 'FunctionFixture', 'LoggerFixture', - 'MementoLogger', + 'LogHandler', 'MethodFixture', 'MonkeyPatch', 'NestedTempfile', @@ -73,7 +73,7 @@ from fixtures._fixtures import ( FakeLogger, FakePopen, LoggerFixture, - MementoLogger, + LogHandler, MonkeyPatch, NestedTempfile, PackagePathEntry, diff --git a/lib/fixtures/_fixtures/__init__.py b/lib/fixtures/_fixtures/__init__.py index da2a70b..b61b010 100644 --- a/lib/fixtures/_fixtures/__init__.py +++ b/lib/fixtures/_fixtures/__init__.py @@ -22,7 +22,7 @@ __all__ = [ 'FakeLogger', 'FakePopen', 'LoggerFixture', - 'MementoLogger', + 'LogHandler', 'MonkeyPatch', 'NestedTempfile', 'PackagePathEntry', @@ -43,7 +43,7 @@ from fixtures._fixtures.environ import ( from fixtures._fixtures.logger import ( FakeLogger, LoggerFixture, - MementoLogger, + LogHandler, ) from fixtures._fixtures.monkeypatch import MonkeyPatch from fixtures._fixtures.popen import ( diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index 2b60205..f3ba844 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -24,6 +24,7 @@ from fixtures import Fixture __all__ = [ 'FakeLogger', 'LoggerFixture', + 'LogHandler', ] diff --git a/lib/fixtures/tests/_fixtures/test_logger.py b/lib/fixtures/tests/_fixtures/test_logger.py index 361a377..d464d5c 100644 --- a/lib/fixtures/tests/_fixtures/test_logger.py +++ b/lib/fixtures/tests/_fixtures/test_logger.py @@ -16,16 +16,11 @@ import logging from testtools import TestCase -from testtools.matchers import ( - ContainsDict, - Equals, - MatchesListwise, - ) from cStringIO import StringIO from fixtures import ( FakeLogger, - MementoLogger, + LogHandler, TestWithFixtures, ) from fixtures._fixtures.logger import MementoHandler @@ -123,10 +118,20 @@ class TestMementoHandler(TestCase): self.assertIsInstance(handler, logging.Handler) -class MementoLoggerTest(TestCase, TestWithFixtures): +class LogHandlerTest(TestCase, TestWithFixtures): + + class CustomHandler(logging.Handler): + + def __init__(self, *args, **kwargs): + """Create the instance, and add a records attribute.""" + logging.Handler.__init__(self, *args, **kwargs) + self.msgs = [] + + def emit(self, record): + self.msgs.append(record.msg) def setUp(self): - super(MementoLoggerTest, self).setUp() + super(LogHandlerTest, self).setUp() self.logger = logging.getLogger() self.addCleanup(self.removeHandlers, self.logger) @@ -134,11 +139,7 @@ class MementoLoggerTest(TestCase, TestWithFixtures): for handler in logger.handlers: logger.removeHandler(handler) - def test_get_records_has_records(self): - fixture = self.useFixture(MementoLogger()) + def test_captures_logging(self): + fixture = self.useFixture(LogHandler(self.CustomHandler())) logging.info("some message") - self.assertThat( - fixture.get_records(), - MatchesListwise( - [ContainsDict({'msg': Equals("some message"), - 'levelname': Equals('INFO')})])) + self.assertEqual(["some message"], fixture.handler.msgs) -- cgit v1.2.1 From 5306c96b61e499e36e1658ee0893986a5182f13b Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 15:40:48 +0000 Subject: Rest of the tests for LogHandler. --- lib/fixtures/tests/_fixtures/test_logger.py | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/fixtures/tests/_fixtures/test_logger.py b/lib/fixtures/tests/_fixtures/test_logger.py index d464d5c..c3d23fb 100644 --- a/lib/fixtures/tests/_fixtures/test_logger.py +++ b/lib/fixtures/tests/_fixtures/test_logger.py @@ -143,3 +143,37 @@ class LogHandlerTest(TestCase, TestWithFixtures): fixture = self.useFixture(LogHandler(self.CustomHandler())) logging.info("some message") self.assertEqual(["some message"], fixture.handler.msgs) + + def test_replace_and_restore_handlers(self): + stream = StringIO() + logger = logging.getLogger() + logger.addHandler(logging.StreamHandler(stream)) + logger.setLevel(logging.INFO) + logging.info("one") + fixture = LogHandler(self.CustomHandler()) + with fixture: + logging.info("two") + logging.info("three") + self.assertEqual(["two"], fixture.handler.msgs) + self.assertEqual("one\nthree\n", stream.getvalue()) + + def test_preserving_existing_handlers(self): + stream = StringIO() + self.logger.addHandler(logging.StreamHandler(stream)) + self.logger.setLevel(logging.INFO) + fixture = LogHandler(self.CustomHandler(), nuke_handlers=False) + with fixture: + logging.info("message") + self.assertEqual(["message"], fixture.handler.msgs) + self.assertEqual("message\n", stream.getvalue()) + + def test_logging_level_restored(self): + self.logger.setLevel(logging.DEBUG) + fixture = LogHandler(self.CustomHandler(), level=logging.WARNING) + with fixture: + # The fixture won't capture this, because the DEBUG level + # is lower than the WARNING one + logging.debug("debug message") + self.assertEqual(logging.WARNING, self.logger.level) + self.assertEqual([], fixture.handler.msgs) + self.assertEqual(logging.DEBUG, self.logger.level) -- cgit v1.2.1 From 1663737e38deb18972190a56e951e913e20cd82a Mon Sep 17 00:00:00 2001 From: Jonathan Lange Date: Thu, 13 Dec 2012 15:42:32 +0000 Subject: Drop the MementoHandler stuff. --- lib/fixtures/_fixtures/logger.py | 44 +---------------------------- lib/fixtures/tests/_fixtures/test_logger.py | 22 --------------- 2 files changed, 1 insertion(+), 65 deletions(-) diff --git a/lib/fixtures/_fixtures/logger.py b/lib/fixtures/_fixtures/logger.py index f3ba844..660e736 100644 --- a/lib/fixtures/_fixtures/logger.py +++ b/lib/fixtures/_fixtures/logger.py @@ -13,7 +13,7 @@ # license you chose for the specific language governing permissions and # limitations under that license. -from logging import StreamHandler, getLogger, INFO, Formatter, Handler +from logging import StreamHandler, getLogger, INFO, Formatter from cStringIO import StringIO from testtools.content import Content @@ -109,45 +109,3 @@ class FakeLogger(Fixture): LoggerFixture = FakeLogger - - -class MementoHandler(Handler): - """A handler class which stores logging records in a list. - - From http://nessita.pastebin.com/mgc85uQT - """ - def __init__(self, *args, **kwargs): - """Create the instance, and add a records attribute.""" - Handler.__init__(self, *args, **kwargs) - self.records = [] - - def emit(self, record): - """Just add the dict of the record to self.records.""" - # logging actually uses LogRecord.__dict__ regularly. Sheesh. - self.records.append(record.__dict__) - - -class MementoLogger(Fixture): - - def __init__(self, name="", level=INFO, nuke_handlers=True): - """Create a MementoLogger fixture. - - :param name: The name of the logger to replace. Defaults to "". - :param level: The log level to set, defaults to INFO. - :param nuke_handlers: If True remove all existing handles (prevents - existing messages going to e.g. stdout). Defaults to True. - """ - super(MementoLogger, self).__init__() - self._name = name - self._level = level - self._nuke_handlers = nuke_handlers - - def setUp(self): - super(MementoLogger, self).setUp() - self._handler = MementoHandler() - self.useFixture( - LogHandler(self._handler, name=self._name, level=self._level, - nuke_handlers=self._nuke_handlers)) - - def get_records(self): - return self._handler.records diff --git a/lib/fixtures/tests/_fixtures/test_logger.py b/lib/fixtures/tests/_fixtures/test_logger.py index c3d23fb..c05e862 100644 --- a/lib/fixtures/tests/_fixtures/test_logger.py +++ b/lib/fixtures/tests/_fixtures/test_logger.py @@ -23,7 +23,6 @@ from fixtures import ( LogHandler, TestWithFixtures, ) -from fixtures._fixtures.logger import MementoHandler class FakeLoggerTest(TestCase, TestWithFixtures): @@ -97,27 +96,6 @@ class FakeLoggerTest(TestCase, TestWithFixtures): self.assertEqual("", fixture.getDetails()[detail_name].as_text()) -class TestMementoHandler(TestCase): - - class FakeRecord(object): - def __init__(self, **kw): - self.__dict__.update(kw) - - def test_initialy_no_records(self): - handler = MementoHandler() - self.assertEqual([], handler.records) - - def test_emit_stored_in_records(self): - handler = MementoHandler() - marker = self.FakeRecord(foo='bar') - handler.emit(marker) - self.assertEqual([{'foo': 'bar'}], handler.records) - - def test_is_log_handler(self): - handler = MementoHandler() - self.assertIsInstance(handler, logging.Handler) - - class LogHandlerTest(TestCase, TestWithFixtures): class CustomHandler(logging.Handler): -- cgit v1.2.1