diff options
author | ianb <devnull@localhost> | 2005-05-23 02:42:01 +0000 |
---|---|---|
committer | ianb <devnull@localhost> | 2005-05-23 02:42:01 +0000 |
commit | 55de8d0c0ac435c08cd8b1d68e0606d8c13369f0 (patch) | |
tree | 88ba13408215a218d592d8d46dcca51086599773 /paste/exceptions | |
parent | cf616dc480dee55291240283e46d64f8d7f0d8b7 (diff) | |
download | paste-55de8d0c0ac435c08cd8b1d68e0606d8c13369f0.tar.gz |
Added better consolidated __traceback_hide__ support
Diffstat (limited to 'paste/exceptions')
-rw-r--r-- | paste/exceptions/collector.py | 41 | ||||
-rw-r--r-- | paste/exceptions/formatter.py | 64 | ||||
-rw-r--r-- | paste/exceptions/tests/test_formatter.py | 93 |
3 files changed, 162 insertions, 36 deletions
diff --git a/paste/exceptions/collector.py b/paste/exceptions/collector.py index 1c10d0f..89be9bd 100644 --- a/paste/exceptions/collector.py +++ b/paste/exceptions/collector.py @@ -32,6 +32,8 @@ import serial_number_generator DEBUG_EXCEPTION_FORMATTER = True DEBUG_IDENT_PREFIX = 'E-' +__all__ = ['collect_exception', 'ExceptionCollector'] + class ExceptionCollector: """ @@ -65,14 +67,16 @@ class ExceptionCollector: some of the complexity of the larger framework and let the user focus on their own errors. - ``__traceback_stop__``: - If set and true, then all frames before this should be hidden - (as though they had ``__traceback_hide__`` set). This way - you can, for instance, hide an entire server. + By setting it to ``'before'``, all frames before this one will + be thrown away. By setting it to ``'after'`` then all frames + after this will be thrown away until ``'reset'`` is found. In + each case the frame where it is set is included, unless you + append ``'_and_this'`` to the value (e.g., + ``'before_and_this'``). - ``__traceback_start__``: - Start up the traceback again if ``__traceback_stop__`` has - come into play. + Note that formatters will ignore this entirely if the frame + that contains the error wouldn't normally be shown according + to these rules. ``__traceback_reporter__``: This should be a reporter object (see the reporter module), @@ -127,10 +131,6 @@ class ExceptionCollector: scope (@@: should it str()-ify it or not?) ``traceback_hide``: the value of any ``__traceback_hide__`` variable - ``traceback_stop``: - the value of any ``__traceback_stop__`` variable - ``traceback_start``: - the value of any ``__traceback_start__`` variable ``traceback_log``: the value of any ``__traceback_log__`` variable @@ -179,15 +179,10 @@ class ExceptionCollector: itself. Formatters may want to use ``__traceback_hide__`` as a hint to - hide frames that are part of the 'framework' or underlying system; - any frames that precede ``__traceback_stop__`` should be treated - similarly. Completely hiding these frames may be confusing, but - it allows an abbreviated view of the exception that may highlight - problems (it is advised that a complete traceback also be - generated). If the last frame has one of these variables set, you - should probably ignore the variables entirely, as it means there - is an unexpected error in the framework. - + hide frames that are part of the 'framework' or underlying system. + There are a variety of rules about special values for this + variables that formatters should be aware of. + TODO: More attributes in __traceback_supplement__? Maybe an attribute @@ -307,8 +302,7 @@ class ExceptionCollector: pass marker = [] - for name in ('__traceback_hide__', '__traceback_stop__', - '__traceback_start__', '__traceback_log__'): + for name in ('__traceback_hide__', '__traceback_log__'): try: tbh = locals.get(name, marker) if tbh is not marker: @@ -451,9 +445,8 @@ class ExceptionFrame(Bunch): supplement_exception = None # The str() of any __traceback_info__ value found traceback_info = None - # The value of __traceback_hide__ and __traceback_stop__ variables: + # The value of __traceback_hide__ traceback_hide = False - traceback_stop = False def get_source_line(self): """ diff --git a/paste/exceptions/formatter.py b/paste/exceptions/formatter.py index 9d94d97..9491f3e 100644 --- a/paste/exceptions/formatter.py +++ b/paste/exceptions/formatter.py @@ -27,16 +27,8 @@ class AbstractFormatter: general_data[(importance, name)] = self.format_extra_data( importance, title, value) lines = [] - show_hidden_frames = self.show_hidden_frames - last = exc_data.frames[-1] - if last.traceback_hide or last.traceback_stop: - # If the last frame was supposed to have been hidden, - # there's clearly a problem in the hidden portion of - # the framework itself - show_hidden_frames = True - for frame in exc_data.frames: - if frame.traceback_hide and not show_hidden_frames: - continue + frames = self.filter_frames(exc_data.frames) + for frame in frames: sup = frame.supplement if sup: if sup.object: @@ -57,6 +49,8 @@ class AbstractFormatter: if frame.supplement_exception: lines.append('Exception in supplement:') lines.append(self.quote_long(frame.supplement_exception)) + if frame.traceback_info: + lines.append(self.format_traceback_info(frame.traceback_info)) filename = frame.filename if filename and self.trim_source_paths: for path, repl in self.trim_source_paths: @@ -82,6 +76,50 @@ class AbstractFormatter: value.sort() return self.format_combine(data_by_importance, lines, exc_info) + def filter_frames(self, frames): + """ + Removes any frames that should be hidden, according to the + values of traceback_hide, self.show_hidden_frames, and the + hidden status of the final frame. + """ + if self.show_hidden_frames: + return frames + new_frames = [] + hidden = False + for frame in frames: + hide = frame.traceback_hide + # @@: It would be nice to signal a warning if an unknown + # hide string was used, but I'm not sure where to put + # that warning. + if hide == 'before': + new_frames = [] + hidden = False + elif hide == 'before_and_this': + new_frames = [] + hidden = False + continue + elif hide == 'reset': + hidden = False + elif hide == 'reset_and_this': + hidden = False + continue + elif hide == 'after': + hidden = True + elif hide == 'after_and_this': + hidden = True + continue + elif hide: + continue + elif hidden: + continue + new_frames.append(frame) + if frames[-1] not in new_frames: + # We must include the last frame; that we don't indicates + # that the error happened where something was "hidden", + # so we just have to show everything + return frames + return new_frames + def pretty_string_repr(self, s): """ Formats the string as a triple-quoted string when it contains @@ -138,7 +176,9 @@ class TextFormatter(AbstractFormatter): def format_exception_info(self, etype, evalue): return self.emphasize( '%s: %s' % (self.quote(etype), self.quote(evalue))) - + def format_traceback_info(self, info): + return info + def format_combine(self, data_by_importance, lines, exc_info): lines[:0] = [value for n, value in data_by_importance['important']] lines.append(exc_info) @@ -186,6 +226,8 @@ class HTMLFormatter(TextFormatter): return 'File %r, line %s in <tt>%s</tt>' % (filename, lineno, name) def format_source(self, source_line): return ' <tt>%s</tt>' % self.quote(source_line.strip()) + def format_traceback_info(self, info): + return '<pre>%s</pre>' % self.quote(info) def format_extra_data(self, importance, title, value): if isinstance(value, str): diff --git a/paste/exceptions/tests/test_formatter.py b/paste/exceptions/tests/test_formatter.py index 6a9b255..53b0d10 100644 --- a/paste/exceptions/tests/test_formatter.py +++ b/paste/exceptions/tests/test_formatter.py @@ -2,6 +2,7 @@ from paste.exceptions import formatter from paste.exceptions import collector import sys import os +import difflib class Mock(object): def __init__(self, **kw): @@ -39,12 +40,24 @@ def raise_error(sup='default'): if i == 5: call_error(sup=sup) +def hide(t, inner, *args, **kw): + __traceback_hide__ = t + return inner(*args, **kw) + +def pass_through(info, inner, *args, **kw): + """ + To add another frame to the call; detectable because + __tracback_info__ is set to `info` + """ + __traceback_info__ = info + return inner(*args, **kw) + def format(type='html', **ops): data = collector.collect_exception(*sys.exc_info()) report = getattr(formatter, 'format_' + type)(data, **ops) return report -formats = ('html', 'text') +formats = ('text', 'html') def test_excersize(): for f in formats: @@ -67,6 +80,8 @@ def test_content(): assert 'call_error' in result assert '5' in result assert 'test_content' in result + else: + assert 0 def test_trim(): current = os.path.abspath(os.getcwd()) @@ -77,3 +92,79 @@ def test_trim(): result = format(f, trim_source_paths=[(current, '.')]) assert current not in result assert '/test_formatter.py' in result + else: + assert 0 + +def test_hide(): + for f in formats: + try: + hide(True, raise_error) + except: + result = format(f) + print result + assert 'in hide_inner' not in result + assert 'inner(*args, **kw)' not in result + else: + assert 0 + +def print_diff(s1, s2): + differ = difflib.Differ() + result = list(differ.compare(s1.splitlines(), s2.splitlines())) + print '\n'.join(result) + +def test_hide_supppressed(): + """ + When an error occurs and __traceback_stop__ is true for the + erroneous frame, then that setting should be ignored. + """ + for f in formats: + results = [] + for hide_value in (False, 'after'): + try: + pass_through( + 'a', + hide, + hide_value, + pass_through, + 'b', + raise_error) + except: + results.append(format(f)) + else: + assert 0 + if results[0] != results[1]: + print_diff(results[0], results[1]) + assert 0 + +def test_hide_after(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'after', + pass_through, 'CCDD', + hide, 'reset', + raise_error) + except: + result = format(f) + print result + assert 'AABB' in result + assert 'CCDD' not in result + assert 'raise_error' in result + else: + assert 0 + +def test_hide_before(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'before', + raise_error) + except: + result = format(f) + print result + assert 'AABB' not in result + assert 'raise_error' in result + else: + assert 0 |