Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for attaching logs on error. #34

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions fixtures/_fixtures/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class FakeLogger(Fixture):
"""Replace a logger and capture its output."""

def __init__(self, name="", level=INFO, format=None,
datefmt=None, nuke_handlers=True, formatter=None):
datefmt=None, nuke_handlers=True, formatter=None,
only_on_error=False):
"""Create a FakeLogger fixture.

:param name: The name of the logger to replace. Defaults to "".
Expand All @@ -86,6 +87,9 @@ def __init__(self, name="", level=INFO, format=None,
existing messages going to e.g. stdout). Defaults to True.
:param formatter: a custom log formatter class. Use this if you want
to use a log Formatter other than the default one in python.
:param only_on_error: Only attach the captured output to the TestResult
if the test fails. This can be important for some test suites where
the full debug logging needed is enormous.

Example:

Expand All @@ -101,10 +105,12 @@ def test_log(self)
self._datefmt = datefmt
self._nuke_handlers = nuke_handlers
self._formatter = formatter
self._only_on_error = only_on_error

def _setUp(self):
name = _u("pythonlogging:'%s'") % self._name
output = self.useFixture(StringStream(name)).stream
output = self.useFixture(
StringStream(name, only_on_error=self._only_on_error)).stream
self._output = output
handler = StreamHandlerRaiseException(output)
if self._format:
Expand Down
26 changes: 20 additions & 6 deletions fixtures/_fixtures/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,48 @@ class Stream(Fixture):
:attr stream: The file-like object.
"""

def __init__(self, detail_name, stream_factory):
def __init__(self, detail_name, stream_factory, only_on_error=False):
"""Create a ByteStream.

:param detail_name: Use this as the name of the stream.
:param stream_factory: Called to construct a pair of streams:
(write_stream, content_stream).
:param only_on_error: Only attach the stream output if an error occurs.
"""
self._detail_name = detail_name
self._stream_factory = stream_factory
self._only_on_error = only_on_error

def _setUp(self):
write_stream, read_stream = self._stream_factory()
self.stream = write_stream
self._read_stream = read_stream
if self._only_on_error:
self.addOnException(self._add_stream_detail)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make sense to me. There's no "addOnException" method in the Fixture API.

else:
self._add_stream_detail()

def _add_stream_detail(self):
self.addDetail(self._detail_name,
testtools.content.content_from_stream(read_stream, seek_offset=0))
testtools.content.content_from_stream(
self._read_stream, seek_offset=0))


def _byte_stream_factory():
result = io.BytesIO()
return (result, result)


def ByteStream(detail_name):
def ByteStream(detail_name, only_on_error=only_on_error):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no global variable called "only_on_error", so the Python interpreter tracebacks when getting to this line of code (see also the unit test failures attached to this branch by Travis).

"""Provide a file-like object that accepts bytes and expose as a detail.

:param detail_name: The name of the detail.
:param only_on_error: Only attach the stream output if an error occurs.
:return: A fixture which has an attribute `stream` containing the file-like
object.
"""
return Stream(detail_name, _byte_stream_factory)
return Stream(
detail_name, _byte_stream_factory, only_on_error=only_on_error)


def _string_stream_factory():
Expand All @@ -81,14 +93,16 @@ def safe_write(str_or_bytes):
return upper, lower


def StringStream(detail_name):
def StringStream(detail_name, only_on_error=False):
"""Provide a file-like object that accepts strings and expose as a detail.

:param detail_name: The name of the detail.
:param only_on_error: Only attach the stream output if an error occurs.
:return: A fixture which has an attribute `stream` containing the file-like
object.
"""
return Stream(detail_name, _string_stream_factory)
return Stream(
detail_name, _string_stream_factory, only_on_error=only_on_error)


def DetailStream(detail_name):
Expand Down