Statistiques
| Révision :

root / ase / gui / gtkexcepthook.py

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)