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