1b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik##############################################################################
4b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#
5b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
6b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# All Rights Reserved.
7b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#
8b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# This software is subject to the provisions of the Zope Public License,
9b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
10b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
11b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
12b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
13b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# FOR A PARTICULAR PURPOSE.
14b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik#
15b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik##############################################################################
16b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik## Originally zExceptions.ExceptionFormatter from Zope;
17b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik## Modified by Ian Bicking, Imaginary Landscape, 2005
18b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik"""
19b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikAn exception collector that finds traceback information plus
20b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiksupplements
21b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik"""
22b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
23b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport sys
24b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport traceback
25b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport time
26b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom six.moves import cStringIO as StringIO
27b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport linecache
28b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste.exceptions import serial_number_generator
29b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport warnings
30b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
31b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikDEBUG_EXCEPTION_FORMATTER = True
32b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikDEBUG_IDENT_PREFIX = 'E-'
33b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikFALLBACK_ENCODING = 'UTF-8'
34b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
35b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik__all__ = ['collect_exception', 'ExceptionCollector']
36b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
37b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass ExceptionCollector(object):
38b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
39b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
40b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Produces a data structure that can be used by formatters to
41b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    display exception reports.
42b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
43b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Magic variables:
44b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
45b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    If you define one of these variables in your local scope, you can
46b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    add information to tracebacks that happen in that context.  This
47b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    allows applications to add all sorts of extra information about
48b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    the context of the error, including URLs, environmental variables,
49b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    users, hostnames, etc.  These are the variables we look for:
50b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
51b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_supplement__``:
52b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        You can define this locally or globally (unlike all the other
53b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        variables, which must be defined locally).
54b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
55b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ``__traceback_supplement__`` is a tuple of ``(factory, arg1,
56b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        arg2...)``.  When there is an exception, ``factory(arg1, arg2,
57b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ...)`` is called, and the resulting object is inspected for
58b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        supplemental information.
59b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
60b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_info__``:
61b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        This information is added to the traceback, usually fairly
62b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        literally.
63b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
64b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_hide__``:
65b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        If set and true, this indicates that the frame should be
66b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        hidden from abbreviated tracebacks.  This way you can hide
67b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        some of the complexity of the larger framework and let the
68b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        user focus on their own errors.
69b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
70b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        By setting it to ``'before'``, all frames before this one will
71b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        be thrown away.  By setting it to ``'after'`` then all frames
72b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        after this will be thrown away until ``'reset'`` is found.  In
73b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        each case the frame where it is set is included, unless you
74b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        append ``'_and_this'`` to the value (e.g.,
75b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ``'before_and_this'``).
76b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
77b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        Note that formatters will ignore this entirely if the frame
78b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        that contains the error wouldn't normally be shown according
79b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        to these rules.
80b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
81b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_reporter__``:
82b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        This should be a reporter object (see the reporter module),
83b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        or a list/tuple of reporter objects.  All reporters found this
84b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        way will be given the exception, innermost first.
85b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
86b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_decorator__``:
87b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        This object (defined in a local or global scope) will get the
88b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        result of this function (the CollectedException defined
89b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        below).  It may modify this object in place, or return an
90b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        entirely new object.  This gives the object the ability to
91b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        manipulate the traceback arbitrarily.
92b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
93b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    The actually interpretation of these values is largely up to the
94b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    reporters and formatters.
95b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
96b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``collect_exception(*sys.exc_info())`` will return an object with
97b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    several attributes:
98b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
99b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``frames``:
100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        A list of frames
101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``exception_formatted``:
102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        The formatted exception, generally a full traceback
103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``exception_type``:
104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        The type of the exception, like ``ValueError``
105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``exception_value``:
106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        The string value of the exception, like ``'x not in list'``
107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``identification_code``:
108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        A hash of the exception data meant to identify the general
109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        exception, so that it shares this code with other exceptions
110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        that derive from the same problem.  The code is a hash of
111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        all the module names and function names in the traceback,
112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        plus exception_type.  This should be shown to users so they
113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        can refer to the exception later. (@@: should it include a
114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        portion that allows identification of the specific instance
115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        of the exception as well?)
116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    The list of frames goes innermost first.  Each frame has these
118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    attributes; some values may be None if they could not be
119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    determined.
120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``modname``:
122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the name of the module
123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``filename``:
124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the filename of the module
125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``lineno``:
126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the line of the error
127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``revision``:
128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the contents of __version__ or __revision__
129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``name``:
130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the function name
131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``supplement``:
132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        an object created from ``__traceback_supplement__``
133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``supplement_exception``:
134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        a simple traceback of any exception ``__traceback_supplement__``
135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        created
136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``traceback_info``:
137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the str() of any ``__traceback_info__`` variable found in the local
138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        scope (@@: should it str()-ify it or not?)
139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``traceback_hide``:
140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the value of any ``__traceback_hide__`` variable
141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``traceback_log``:
142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the value of any ``__traceback_log__`` variable
143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_supplement__`` is thrown away, but a fixed
146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    set of attributes are captured; each of these attributes is
147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    optional.
148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``object``:
150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the name of the object being visited
151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``source_url``:
152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the original URL requested
153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``line``:
154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the line of source being executed (for interpreters, like ZPT)
155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``column``:
156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the column of source being executed
157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``expression``:
158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the expression being evaluated (also for interpreters)
159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``warnings``:
160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        a list of (string) warnings to be displayed
161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``getInfo``:
162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        a function/method that takes no arguments, and returns a string
163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        describing any extra information
164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``extraData``:
165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        a function/method that takes no arguments, and returns a
166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        dictionary.  The contents of this dictionary will not be
167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        displayed in the context of the traceback, but globally for
168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        the exception.  Results will be grouped by the keys in the
169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        dictionaries (which also serve as titles).  The keys can also
170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        be tuples of (importance, title); in this case the importance
171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        should be ``important`` (shows up at top), ``normal`` (shows
172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        up somewhere; unspecified), ``supplemental`` (shows up at
173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        bottom), or ``extra`` (shows up hidden or not at all).
174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    These are used to create an object with attributes of the same
176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    names (``getInfo`` becomes a string attribute, not a method).
177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    ``__traceback_supplement__`` implementations should be careful to
178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    produce values that are relatively static and unlikely to cause
179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    further errors in the reporting system -- any complex
180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    introspection should go in ``getInfo()`` and should ultimately
181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    return a string.
182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Note that all attributes are optional, and under certain
184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    circumstances may be None or may not exist at all -- the collector
185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    can only do a best effort, but must avoid creating any exceptions
186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    itself.
187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Formatters may want to use ``__traceback_hide__`` as a hint to
189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    hide frames that are part of the 'framework' or underlying system.
190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    There are a variety of rules about special values for this
191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    variables that formatters should be aware of.
192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    TODO:
194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    More attributes in __traceback_supplement__?  Maybe an attribute
196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    that gives a list of local variables that should also be
197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    collected?  Also, attributes that would be explicitly meant for
198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    the entire request, not just a single frame.  Right now some of
199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    the fixed set of attributes (e.g., source_url) are meant for this
200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    use, but there's no explicit way for the supplement to indicate
201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    new values, e.g., logged-in user, HTTP referrer, environment, etc.
202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Also, the attributes that do exist are Zope/Web oriented.
203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    More information on frames?  cgitb, for instance, produces
205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    extensive information on local variables.  There exists the
206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    possibility that getting this information may cause side effects,
207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    which can make debugging more difficult; but it also provides
208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    fodder for post-mortem debugging.  However, the collector is not
209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    meant to be configurable, but to capture everything it can and let
210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    the formatters be configurable.  Maybe this would have to be a
211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    configuration value, or maybe it could be indicated by another
212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    magical variable (which would probably mean 'show all local
213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    variables below this frame')
214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    show_revisions = 0
217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def __init__(self, limit=None):
219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        self.limit = limit
220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def getLimit(self):
222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        limit = self.limit
223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if limit is None:
224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            limit = getattr(sys, 'tracebacklimit', None)
225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return limit
226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def getRevision(self, globals):
228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if not self.show_revisions:
229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            return None
230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        revision = globals.get('__revision__', None)
231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if revision is None:
232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            # Incorrect but commonly used spelling
233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            revision = globals.get('__version__', None)
234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if revision is not None:
236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            try:
237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                revision = str(revision).strip()
238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            except:
239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                revision = '???'
240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return revision
241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def collectSupplement(self, supplement, tb):
243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        result = {}
244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        for name in ('object', 'source_url', 'line', 'column',
246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                     'expression', 'warnings'):
247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            result[name] = getattr(supplement, name, None)
248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        func = getattr(supplement, 'getInfo', None)
250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if func:
251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            result['info'] = func()
252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        else:
253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            result['info'] = None
254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        func = getattr(supplement, 'extraData', None)
255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if func:
256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            result['extra'] = func()
257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        else:
258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            result['extra'] = None
259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return SupplementaryData(**result)
260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def collectLine(self, tb, extra_data):
262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        f = tb.tb_frame
263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        lineno = tb.tb_lineno
264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        co = f.f_code
265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        filename = co.co_filename
266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        name = co.co_name
267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        globals = f.f_globals
268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        locals = f.f_locals
269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if not hasattr(locals, 'has_key'):
270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            # Something weird about this frame; it's not a real dict
271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            warnings.warn(
272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                "Frame %s has an invalid locals(): %r" % (
273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                globals.get('__name__', 'unknown'), locals))
274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            locals = {}
275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data = {}
276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['modname'] = globals.get('__name__', None)
277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['filename'] = filename
278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['lineno'] = lineno
279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['revision'] = self.getRevision(globals)
280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['name'] = name
281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        data['tbid'] = id(tb)
282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        # Output a traceback supplement, if any.
284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if '__traceback_supplement__' in locals:
285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            # Use the supplement defined in the function.
286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            tbs = locals['__traceback_supplement__']
287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        elif '__traceback_supplement__' in globals:
288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            # Use the supplement defined in the module.
289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            # This is used by Scripts (Python).
290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            tbs = globals['__traceback_supplement__']
291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        else:
292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            tbs = None
293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if tbs is not None:
294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            factory = tbs[0]
295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            args = tbs[1:]
296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            try:
297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                supp = factory(*args)
298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                data['supplement'] = self.collectSupplement(supp, tb)
299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                if data['supplement'].extra:
300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    for key, value in data['supplement'].extra.items():
301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                        extra_data.setdefault(key, []).append(value)
302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            except:
303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                if DEBUG_EXCEPTION_FORMATTER:
304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    out = StringIO()
305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    traceback.print_exc(file=out)
306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    text = out.getvalue()
307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    data['supplement_exception'] = text
308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                # else just swallow the exception.
309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        try:
311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            tbi = locals.get('__traceback_info__', None)
312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            if tbi is not None:
313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                data['traceback_info'] = str(tbi)
314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        except:
315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            pass
316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        marker = []
318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        for name in ('__traceback_hide__', '__traceback_log__',
319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                     '__traceback_decorator__'):
320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            try:
321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                tbh = locals.get(name, globals.get(name, marker))
322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                if tbh is not marker:
323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    data[name[2:-2]] = tbh
324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            except:
325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                pass
326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return data
328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def collectExceptionOnly(self, etype, value):
330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return traceback.format_exception_only(etype, value)
331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def collectException(self, etype, value, tb, limit=None):
333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        # The next line provides a way to detect recursion.
334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        __exception_formatter__ = 1
335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        frames = []
336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ident_data = []
337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        traceback_decorators = []
338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if limit is None:
339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            limit = self.getLimit()
340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        n = 0
341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        extra_data = {}
342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        while tb is not None and (limit is None or n < limit):
343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            if tb.tb_frame.f_locals.get('__exception_formatter__'):
344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                # Stop recursion. @@: should make a fake ExceptionFrame
345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                frames.append('(Recursive formatException() stopped)\n')
346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                break
347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            data = self.collectLine(tb, extra_data)
348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            frame = ExceptionFrame(**data)
349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            frames.append(frame)
350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            if frame.traceback_decorator is not None:
351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                traceback_decorators.append(frame.traceback_decorator)
352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            ident_data.append(frame.modname or '?')
353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            ident_data.append(frame.name or '?')
354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            tb = tb.tb_next
355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            n = n + 1
356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ident_data.append(str(etype))
357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        ident = serial_number_generator.hash_identifier(
358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            ' '.join(ident_data), length=5, upper=True,
359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            prefix=DEBUG_IDENT_PREFIX)
360b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
361b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        result = CollectedException(
362b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            frames=frames,
363b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            exception_formatted=self.collectExceptionOnly(etype, value),
364b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            exception_type=etype,
365b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            exception_value=self.safeStr(value),
366b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            identification_code=ident,
367b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            date=time.localtime(),
368b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            extra_data=extra_data)
369b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if etype is ImportError:
370b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            extra_data[('important', 'sys.path')] = [sys.path]
371b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        for decorator in traceback_decorators:
372b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            try:
373b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                new_result = decorator(result)
374b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                if new_result is not None:
375b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                    result = new_result
376b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            except:
377b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                pass
378b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return result
379b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
380b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def safeStr(self, obj):
381b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        try:
382b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            return str(obj)
383b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        except UnicodeEncodeError:
384b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            try:
385b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                return unicode(obj).encode(FALLBACK_ENCODING, 'replace')
386b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            except UnicodeEncodeError:
387b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                # This is when something is really messed up, but this can
388b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                # happen when the __str__ of an object has to handle unicode
389b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                return repr(obj)
390b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
391b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiklimit = 200
392b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
393b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass Bunch(object):
394b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
395b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
396b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    A generic container
397b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
398b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
399b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def __init__(self, **attrs):
400b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        for name, value in attrs.items():
401b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            setattr(self, name, value)
402b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
403b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def __repr__(self):
404b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        name = '<%s ' % self.__class__.__name__
405b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        name += ' '.join(['%s=%r' % (name, str(value)[:30])
406b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                          for name, value in self.__dict__.items()
407b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik                          if not name.startswith('_')])
408b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return name + '>'
409b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
410b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass CollectedException(Bunch):
411b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
412b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    This is the result of collection the exception; it contains copies
413b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    of data of interest.
414b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
415b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # A list of frames (ExceptionFrame instances), innermost last:
416b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    frames = []
417b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The result of traceback.format_exception_only; this looks
418b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # like a normal traceback you'd see in the interactive interpreter
419b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    exception_formatted = None
420b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The *string* representation of the type of the exception
421b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # (@@: should we give the # actual class? -- we can't keep the
422b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # actual exception around, but the class should be safe)
423b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # Something like 'ValueError'
424b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    exception_type = None
425b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The string representation of the exception, from ``str(e)``.
426b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    exception_value = None
427b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # An identifier which should more-or-less classify this particular
428b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # exception, including where in the code it happened.
429b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    identification_code = None
430b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The date, as time.localtime() returns:
431b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    date = None
432b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # A dictionary of supplemental data:
433b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    extra_data = {}
434b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
435b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass SupplementaryData(Bunch):
436b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
437b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    The result of __traceback_supplement__.  We don't keep the
438b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    supplement object around, for fear of GC problems and whatnot.
439b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    (@@: Maybe I'm being too superstitious about copying only specific
440b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    information over)
441b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
442b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
443b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # These attributes are copied from the object, or left as None
444b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # if the object doesn't have these attributes:
445b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    object = None
446b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    source_url = None
447b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    line = None
448b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    column = None
449b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    expression = None
450b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    warnings = None
451b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # This is the *return value* of supplement.getInfo():
452b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    info = None
453b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
454b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass ExceptionFrame(Bunch):
455b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
456b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    This represents one frame of the exception.  Each frame is a
457b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    context in the call stack, typically represented by a line
458b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    number and module name in the traceback.
459b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
460b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
461b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The name of the module; can be None, especially when the code
462b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # isn't associated with a module.
463b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    modname = None
464b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The filename (@@: when no filename, is it None or '?'?)
465b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    filename = None
466b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # Line number
467b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    lineno = None
468b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The value of __revision__ or __version__ -- but only if
469b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # show_revision = True (by defaut it is false).  (@@: Why not
470b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # collect this?)
471b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    revision = None
472b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The name of the function with the error (@@: None or '?' when
473b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # unknown?)
474b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    name = None
475b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # A SupplementaryData object, if __traceback_supplement__ was found
476b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # (and produced no errors)
477b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    supplement = None
478b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # If accessing __traceback_supplement__ causes any error, the
479b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # plain-text traceback is stored here
480b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    supplement_exception = None
481b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The str() of any __traceback_info__ value found
482b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    traceback_info = None
483b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The value of __traceback_hide__
484b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    traceback_hide = False
485b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The value of __traceback_decorator__
486b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    traceback_decorator = None
487b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # The id() of the traceback scope, can be used to reference the
488b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    # scope for use elsewhere
489b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    tbid = None
490b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
491b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    def get_source_line(self, context=0):
492b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        """
493b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        Return the source of the current line of this frame.  You
494b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        probably want to .strip() it as well, as it is likely to have
495b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        leading whitespace.
496b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
497b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        If context is given, then that many lines on either side will
498b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        also be returned.  E.g., context=1 will give 3 lines.
499b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        """
500b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        if not self.filename or not self.lineno:
501b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            return None
502b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        lines = []
503b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        for lineno in range(self.lineno-context, self.lineno+context+1):
504b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik            lines.append(linecache.getline(self.filename, lineno))
505b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik        return ''.join(lines)
506b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
507b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikif hasattr(sys, 'tracebacklimit'):
508b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    limit = min(limit, sys.tracebacklimit)
509b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
510b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikcol = ExceptionCollector()
511b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
512b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef collect_exception(t, v, tb, limit=None):
513b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
514b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Collection an exception from ``sys.exc_info()``.
515b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
516b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    Use like::
517b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik
518b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik      try:
519b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik          blah blah
520b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik      except:
521b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik          exc_data = collect_exception(*sys.exc_info())
522b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    """
523b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik    return col.collectException(t, v, tb, limit=limit)
524