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