From d211cb97d434f2a50ef4683c86e7d8f088ac01c1 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 5 May 2020 14:27:40 +0300 Subject: [PATCH] Add testing for exceptions --- pysnooper/tracer.py | 2 +- tests/test_chinese.py | 3 +- .../test_multiple_files.py | 3 +- tests/test_not_implemented.py | 4 ++- tests/test_pysnooper.py | 34 ++++++++++++++++++- tests/utils.py | 31 ++++++++++++++--- 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/pysnooper/tracer.py b/pysnooper/tracer.py index 4b4ac05..b9d2300 100644 --- a/pysnooper/tracer.py +++ b/pysnooper/tracer.py @@ -492,7 +492,7 @@ class Tracer: exception = '\n'.join(traceback.format_exception_only(*arg[:2])).strip() if self.max_variable_length: exception = utils.truncate(exception, self.max_variable_length) - self.write('{indent}{exception}'. + self.write('{indent}Exception:..... {exception}'. format(**locals())) return self.trace diff --git a/tests/test_chinese.py b/tests/test_chinese.py index 90e52aa..9964957 100644 --- a/tests/test_chinese.py +++ b/tests/test_chinese.py @@ -16,7 +16,8 @@ from pysnooper import pycompat from pysnooper.variables import needs_parentheses from .utils import (assert_output, assert_sample_output, VariableEntry, CallEntry, LineEntry, ReturnEntry, OpcodeEntry, - ReturnValueEntry, ExceptionEntry, SourcePathEntry, + ReturnValueEntry, ExceptionEntry, ExceptionValueEntry, + SourcePathEntry, CallEndedByExceptionEntry, ElapsedTimeEntry) from . import mini_toolbox diff --git a/tests/test_multiple_files/test_multiple_files.py b/tests/test_multiple_files/test_multiple_files.py index e217ce4..9f78d1b 100644 --- a/tests/test_multiple_files/test_multiple_files.py +++ b/tests/test_multiple_files/test_multiple_files.py @@ -15,7 +15,8 @@ import pysnooper from pysnooper.variables import needs_parentheses from ..utils import (assert_output, assert_sample_output, VariableEntry, CallEntry, LineEntry, ReturnEntry, OpcodeEntry, - ReturnValueEntry, ExceptionEntry, SourcePathEntry, + ReturnValueEntry, ExceptionEntry, ExceptionValueEntry, + SourcePathEntry, CallEndedByExceptionEntry, ElapsedTimeEntry) from .. import mini_toolbox from .multiple_files import foo diff --git a/tests/test_not_implemented.py b/tests/test_not_implemented.py index 65b2645..e392dbf 100644 --- a/tests/test_not_implemented.py +++ b/tests/test_not_implemented.py @@ -17,7 +17,9 @@ from pysnooper.variables import needs_parentheses from pysnooper import pycompat from .utils import (assert_output, assert_sample_output, VariableEntry, CallEntry, LineEntry, ReturnEntry, OpcodeEntry, - ReturnValueEntry, ExceptionEntry, SourcePathEntry) + ReturnValueEntry, ExceptionEntry, ExceptionValueEntry, + SourcePathEntry, CallEndedByExceptionEntry, + ElapsedTimeEntry) from . import mini_toolbox diff --git a/tests/test_pysnooper.py b/tests/test_pysnooper.py index 2623f35..fdf650c 100644 --- a/tests/test_pysnooper.py +++ b/tests/test_pysnooper.py @@ -16,7 +16,8 @@ import pysnooper from pysnooper.variables import needs_parentheses from .utils import (assert_output, assert_sample_output, VariableEntry, CallEntry, LineEntry, ReturnEntry, OpcodeEntry, - ReturnValueEntry, ExceptionEntry, SourcePathEntry, + ReturnValueEntry, ExceptionEntry, ExceptionValueEntry, + SourcePathEntry, CallEndedByExceptionEntry, ElapsedTimeEntry) from . import mini_toolbox @@ -1864,3 +1865,34 @@ def test_normalize_thread_info(): with pytest.raises(NotImplementedError): add() + + +def test_exception(): + string_io = io.StringIO() + @pysnooper.snoop(string_io) + def f(): + x = 8 + raise MemoryError + + with pytest.raises(MemoryError): + f() + + output = string_io.getvalue() + assert_output( + output, + ( + SourcePathEntry(), + CallEntry(), + LineEntry(), + VariableEntry(), + LineEntry(), + ExceptionEntry(), + ExceptionValueEntry('MemoryError'), + CallEndedByExceptionEntry(), + ElapsedTimeEntry(), + ) + ) + + + + diff --git a/tests/utils.py b/tests/utils.py index 10fd249..6260cdc 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -101,6 +101,19 @@ class ElapsedTimeEntry(_BaseEntry): +class CallEndedByExceptionEntry(_BaseEntry): + # Todo: Looking at this class, we could rework the hierarchy. + def __init__(self, prefix=''): + _BaseEntry.__init__(self, prefix=prefix) + + def check(self, s): + return re.match( + r'''(?P(?: {4})*)Call ended by exception''', + s + ) + + + class VariableEntry(_BaseValueEntry): def __init__(self, name=None, value=None, stage=None, prefix='', name_regex=None, value_regex=None): @@ -165,7 +178,7 @@ class VariableEntry(_BaseValueEntry): return stage == self.stage -class ReturnValueEntry(_BaseValueEntry): +class _BaseSimpleValueEntry(_BaseValueEntry): def __init__(self, value=None, value_regex=None, prefix=''): _BaseValueEntry.__init__(self, prefix=prefix) if value is not None: @@ -175,10 +188,6 @@ class ReturnValueEntry(_BaseValueEntry): self.value_regex = (None if value_regex is None else re.compile(value_regex)) - _preamble_pattern = re.compile( - r"""^Return value$""" - ) - def _check_preamble(self, preamble): return bool(self._preamble_pattern.match(preamble)) @@ -193,6 +202,16 @@ class ReturnValueEntry(_BaseValueEntry): else: return True +class ReturnValueEntry(_BaseSimpleValueEntry): + _preamble_pattern = re.compile( + r"""^Return value$""" + ) + +class ExceptionValueEntry(_BaseSimpleValueEntry): + _preamble_pattern = re.compile( + r"""^Exception$""" + ) + class SourcePathEntry(_BaseValueEntry): def __init__(self, source_path=None, source_path_regex=None, prefix=''): _BaseValueEntry.__init__(self, prefix=prefix) @@ -315,6 +334,8 @@ def verify_normalize(lines, prefix): def assert_output(output, expected_entries, prefix=None, normalize=False): lines = tuple(filter(None, output.split('\n'))) + if expected_entries and not lines: + raise OutputFailure("Output is empty") if prefix is not None: for line in lines: