summaryrefslogtreecommitdiff
path: root/paste/exceptions
diff options
context:
space:
mode:
authorianb <devnull@localhost>2005-05-23 02:42:01 +0000
committerianb <devnull@localhost>2005-05-23 02:42:01 +0000
commit55de8d0c0ac435c08cd8b1d68e0606d8c13369f0 (patch)
tree88ba13408215a218d592d8d46dcca51086599773 /paste/exceptions
parentcf616dc480dee55291240283e46d64f8d7f0d8b7 (diff)
downloadpaste-55de8d0c0ac435c08cd8b1d68e0606d8c13369f0.tar.gz
Added better consolidated __traceback_hide__ support
Diffstat (limited to 'paste/exceptions')
-rw-r--r--paste/exceptions/collector.py41
-rw-r--r--paste/exceptions/formatter.py64
-rw-r--r--paste/exceptions/tests/test_formatter.py93
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 '&nbsp;&nbsp;<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