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