root / ase / gui / gtkexcepthook.py @ 1
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) |