root / ase / gui / gtkexcepthook.py @ 4
Historique | Voir | Annoter | Télécharger (6,47 ko)
| 1 |
# vim: sw=4 ts=4:
|
|---|---|
| 2 |
#
|
| 3 |
# (c) 2003 Gustavo J A M Carneiro gjc at inescporto.pt
|
| 4 |
# 2004-2005 Filip Van Raemdonck
|
| 5 |
#
|
| 6 |
# http://www.daa.com.au/pipermail/pygtk/2003-August/005775.html
|
| 7 |
# Message-ID: <1062087716.1196.5.camel@emperor.homelinux.net>
|
| 8 |
# "The license is whatever you want."
|
| 9 |
#
|
| 10 |
# This file was downloaded from http://www.sysfs.be/downloads/
|
| 11 |
# Minor adaptions 2009 by Martin Renold:
|
| 12 |
# - let KeyboardInterrupt through
|
| 13 |
# - print traceback to stderr before showing the dialog
|
| 14 |
# - nonzero exit code when hitting the "quit" button
|
| 15 |
# - suppress more dialogs while one is already active
|
| 16 |
# see also http://faq.pygtk.org/index.py?req=show&file=faq20.010.htp
|
| 17 |
# (The license is still whatever you want.)
|
| 18 |
|
| 19 |
import inspect, linecache, pydoc, sys, traceback |
| 20 |
from cStringIO import StringIO |
| 21 |
from gettext import gettext as _ |
| 22 |
from smtplib import SMTP |
| 23 |
|
| 24 |
import pygtk |
| 25 |
pygtk.require ('2.0')
|
| 26 |
import gtk, pango |
| 27 |
|
| 28 |
def analyse_simple (exctyp, value, tb): |
| 29 |
trace = StringIO() |
| 30 |
traceback.print_exception (exctyp, value, tb, None, trace)
|
| 31 |
return trace
|
| 32 |
|
| 33 |
def lookup (name, frame, lcls): |
| 34 |
'''Find the value for a given name in the given frame'''
|
| 35 |
if name in lcls: |
| 36 |
return 'local', lcls[name] |
| 37 |
elif name in frame.f_globals: |
| 38 |
return 'global', frame.f_globals[name] |
| 39 |
elif '__builtins__' in frame.f_globals: |
| 40 |
builtins = frame.f_globals['__builtins__']
|
| 41 |
if type (builtins) is dict: |
| 42 |
if name in builtins: |
| 43 |
return 'builtin', builtins[name] |
| 44 |
else:
|
| 45 |
if hasattr (builtins, name): |
| 46 |
return 'builtin', getattr (builtins, name) |
| 47 |
return None, [] |
| 48 |
|
| 49 |
def analyse (exctyp, value, tb): |
| 50 |
import tokenize, keyword |
| 51 |
|
| 52 |
trace = StringIO() |
| 53 |
nlines = 3
|
| 54 |
frecs = inspect.getinnerframes (tb, nlines) |
| 55 |
trace.write ('Traceback (most recent call last):\n')
|
| 56 |
for frame, fname, lineno, funcname, context, cindex in frecs: |
| 57 |
trace.write (' File "%s", line %d, ' % (fname, lineno))
|
| 58 |
args, varargs, varkw, lcls = inspect.getargvalues (frame) |
| 59 |
|
| 60 |
def readline (lno=[lineno], *args): |
| 61 |
if args: print args |
| 62 |
try: return linecache.getline (fname, lno[0]) |
| 63 |
finally: lno[0] += 1 |
| 64 |
all, prev, name, scope = {}, None, '', None |
| 65 |
for ttype, tstr, stup, etup, line in tokenize.generate_tokens (readline): |
| 66 |
if ttype == tokenize.NAME and tstr not in keyword.kwlist: |
| 67 |
if name:
|
| 68 |
if name[-1] == '.': |
| 69 |
try:
|
| 70 |
val = getattr (prev, tstr)
|
| 71 |
except AttributeError: |
| 72 |
# XXX skip the rest of this identifier only
|
| 73 |
break
|
| 74 |
name += tstr |
| 75 |
else:
|
| 76 |
assert not name and not scope |
| 77 |
scope, val = lookup (tstr, frame, lcls) |
| 78 |
name = tstr |
| 79 |
if hasattr(val, "shape") and len(val): |
| 80 |
prev = val |
| 81 |
elif val:
|
| 82 |
prev = val |
| 83 |
#print ' found', scope, 'name', name, 'val', val, 'in', prev, 'for token', tstr
|
| 84 |
elif tstr == '.': |
| 85 |
if prev:
|
| 86 |
name += '.'
|
| 87 |
else: |
| 88 |
if name:
|
| 89 |
all[name] = (scope, prev)
|
| 90 |
prev, name, scope = None, '', None |
| 91 |
if ttype == tokenize.NEWLINE:
|
| 92 |
break
|
| 93 |
|
| 94 |
trace.write (funcname + |
| 95 |
inspect.formatargvalues (args, varargs, varkw, lcls, formatvalue=lambda v: '=' + pydoc.text.repr (v)) + '\n') |
| 96 |
trace.write (''.join ([' ' + x.replace ('\t', ' ') for x in filter (lambda a: a.strip(), context)])) |
| 97 |
if len (all): |
| 98 |
trace.write (' variables: %s\n' % str (all)) |
| 99 |
|
| 100 |
trace.write ('%s: %s' % (exctyp.__name__, value))
|
| 101 |
return trace
|
| 102 |
|
| 103 |
def _info (exctyp, value, tb): |
| 104 |
global exception_dialog_active
|
| 105 |
if exctyp is KeyboardInterrupt: |
| 106 |
return original_excepthook(exctyp, value, tb)
|
| 107 |
sys.stderr.write(analyse_simple (exctyp, value, tb).getvalue()) |
| 108 |
if exception_dialog_active:
|
| 109 |
return
|
| 110 |
|
| 111 |
gtk.gdk.pointer_ungrab() |
| 112 |
gtk.gdk.keyboard_ungrab() |
| 113 |
|
| 114 |
exception_dialog_active = True
|
| 115 |
trace = None
|
| 116 |
dialog = gtk.MessageDialog (parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE) |
| 117 |
dialog.set_title (_("Bug Detected"))
|
| 118 |
if gtk.check_version (2, 4, 0) is not None: |
| 119 |
dialog.set_has_separator (False)
|
| 120 |
|
| 121 |
primary = _("<big><b>A programming error has been detected.</b></big>")
|
| 122 |
secondary = _("It probably isn't fatal, but the details should be reported to the developers nonetheless.")
|
| 123 |
|
| 124 |
try:
|
| 125 |
setsec = dialog.format_secondary_text |
| 126 |
except AttributeError: |
| 127 |
raise
|
| 128 |
dialog.vbox.get_children()[0].get_children()[1].set_markup ('%s\n\n%s' % (primary, secondary)) |
| 129 |
#lbl.set_property ("use-markup", True)
|
| 130 |
else:
|
| 131 |
del setsec
|
| 132 |
dialog.set_markup (primary) |
| 133 |
dialog.format_secondary_text (secondary) |
| 134 |
|
| 135 |
try:
|
| 136 |
email = feedback |
| 137 |
dialog.add_button (_("Report..."), 3) |
| 138 |
except NameError: |
| 139 |
# could ask for an email address instead...
|
| 140 |
pass
|
| 141 |
dialog.add_button (_("Details..."), 2) |
| 142 |
dialog.add_button (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) |
| 143 |
dialog.add_button (gtk.STOCK_QUIT, 1)
|
| 144 |
|
| 145 |
while True: |
| 146 |
resp = dialog.run() |
| 147 |
if resp == 3: |
| 148 |
if trace == None: |
| 149 |
trace = analyse (exctyp, value, tb) |
| 150 |
|
| 151 |
# TODO: prettyprint, deal with problems in sending feedback, &tc
|
| 152 |
try:
|
| 153 |
server = smtphost |
| 154 |
except NameError: |
| 155 |
server = 'localhost'
|
| 156 |
|
| 157 |
message = 'From: buggy_application"\nTo: bad_programmer\nSubject: Exception feedback\n\n%s' % trace.getvalue()
|
| 158 |
|
| 159 |
s = SMTP() |
| 160 |
s.connect (server) |
| 161 |
s.sendmail (email, (email,), message) |
| 162 |
s.quit() |
| 163 |
break
|
| 164 |
|
| 165 |
elif resp == 2: |
| 166 |
if trace == None: |
| 167 |
trace = analyse (exctyp, value, tb) |
| 168 |
|
| 169 |
# Show details...
|
| 170 |
details = gtk.Dialog (_("Bug Details"), dialog,
|
| 171 |
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, |
| 172 |
(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, )) |
| 173 |
details.set_property ("has-separator", False) |
| 174 |
|
| 175 |
textview = gtk.TextView(); textview.show() |
| 176 |
textview.set_editable (False)
|
| 177 |
textview.modify_font (pango.FontDescription ("Monospace"))
|
| 178 |
|
| 179 |
sw = gtk.ScrolledWindow(); sw.show() |
| 180 |
sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
| 181 |
sw.add (textview) |
| 182 |
details.vbox.add (sw) |
| 183 |
textbuffer = textview.get_buffer() |
| 184 |
textbuffer.set_text (trace.getvalue()) |
| 185 |
|
| 186 |
monitor = gtk.gdk.screen_get_default ().get_monitor_at_window (dialog.window) |
| 187 |
area = gtk.gdk.screen_get_default ().get_monitor_geometry (monitor) |
| 188 |
try:
|
| 189 |
w = area.width // 1.6
|
| 190 |
h = area.height // 1.6
|
| 191 |
except SyntaxError: |
| 192 |
# python < 2.2
|
| 193 |
w = area.width / 1.6
|
| 194 |
h = area.height / 1.6
|
| 195 |
details.set_default_size (int (w), int (h)) |
| 196 |
|
| 197 |
details.run() |
| 198 |
details.destroy() |
| 199 |
|
| 200 |
elif resp == 1 and gtk.main_level() > 0: |
| 201 |
#gtk.main_quit() - why...? Exit code 0 is bad for IDEs.
|
| 202 |
sys.exit(1)
|
| 203 |
break
|
| 204 |
else:
|
| 205 |
break
|
| 206 |
|
| 207 |
dialog.destroy() |
| 208 |
exception_dialog_active = False
|
| 209 |
|
| 210 |
original_excepthook = sys.excepthook |
| 211 |
sys.excepthook = _info |
| 212 |
exception_dialog_active = False
|
| 213 |
|
| 214 |
if __name__ == '__main__': |
| 215 |
class X (object): |
| 216 |
pass
|
| 217 |
x = X() |
| 218 |
x.y = 'Test'
|
| 219 |
x.z = x |
| 220 |
w = ' e'
|
| 221 |
#feedback = 'developer@bigcorp.comp'
|
| 222 |
#smtphost = 'mx.bigcorp.comp'
|
| 223 |
1, x.z.y, f, w
|
| 224 |
raise Exception (x.z.y + w) |