Use list.sort's keyword parameters
[joel/kofoto.git] / src / packages / kofoto / gkofoto / crashdialog.py
1 import gtk
2 import pango
3 import linecache
4 import os
5 import traceback
6 import re
7
8 class CrashDialog(gtk.Dialog):
9     def __init__(self, exctype, value, tb, parent=None):
10         gtk.Dialog.__init__(
11             self, "GKofoto crash", parent,
12             gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR,
13             (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
14         self._exctype = exctype
15         self._value = value
16         self._tb = tb
17
18         self.set_default_size(
19             gtk.gdk.screen_width() / 2,
20             gtk.gdk.screen_height() / 2)
21         self.set_border_width(10)
22
23         self.vbox.set_spacing(5)
24
25         button = gtk.Button(stock=gtk.STOCK_SAVE)
26         button.show()
27         button.connect("clicked", self._save_cb)
28         self.action_area.pack_start(button)
29         self.action_area.reorder_child(button, 0)
30
31         label = gtk.Label()
32         label.set_markup(
33             "<big><b>A programming error has been detected during the "
34             "execution of this program.</b></big>\n\n"
35             "Please report this problem at "
36             "<span font_family='monospace' foreground='blue'>"
37             "http://kofoto.rosdahl.net</span> "
38             "or <span font_family='monospace' foreground='blue'>"
39             "kofoto@rosdahl.net</span>\n"
40             "and include the information below along with a description of "
41             "what you did before the crash.")
42         label.set_selectable(True)
43         label.show()
44         self.vbox.pack_start(label, False, False)
45
46         sw = gtk.ScrolledWindow()
47         sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
48         sw.set_shadow_type(gtk.SHADOW_IN)
49         sw.show()
50         self.vbox.pack_start(sw)
51
52         self._textbuffer = gtk.TextBuffer()
53         self._textbuffer.create_tag("filename", style=pango.STYLE_ITALIC)
54         self._textbuffer.create_tag("name", weight=pango.WEIGHT_BOLD)
55         self._textbuffer.create_tag("lineno", weight=pango.WEIGHT_BOLD)
56         self._textbuffer.create_tag("exception", weight=pango.WEIGHT_BOLD)
57         self._textbuffer.create_tag("source", family="monospace")
58
59         self._formatTraceback(self._textbuffer, exctype, value, tb)
60
61         textview = gtk.TextView(self._textbuffer)
62         textview.set_editable(False)
63         textview.set_cursor_visible(False)
64         sw.add(textview)
65         textview.show()
66
67     def _save_cb(self, widget):
68         filechooser = gtk.FileChooserDialog(
69             "Save crash log",
70             self,
71             gtk.FILE_CHOOSER_ACTION_SAVE,
72             (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
73              gtk.STOCK_OK, gtk.RESPONSE_OK))
74         if filechooser.run() == gtk.RESPONSE_OK:
75             try:
76                 f = open(filechooser.get_filename(), "w")
77                 traceback.print_exception(
78                     self._exctype, self._value, self._tb, None, f)
79                 f.close()
80             except (OSError, IOError):
81                 dialog = gtk.MessageDialog(
82                     self,
83                     gtk.DIALOG_MODAL,
84                     gtk.MESSAGE_ERROR,
85                     gtk.BUTTONS_OK,
86                     "Could not write to selected file.")
87                 dialog.run()
88                 dialog.destroy()
89         filechooser.destroy()
90
91     def _formatTraceback(self, textbuffer, exctype, value, tb):
92         def add(line, *tags):
93             textbuffer.insert_with_tags_by_name(
94                 textbuffer.get_end_iter(),
95                 line,
96                 *tags)
97         def addFile(filename, lineno, name=None):
98             add("  File ")
99             add(filename, "filename")
100             add(", line ")
101             add(str(lineno), "lineno")
102             if name:
103                 add(", in ")
104                 add(name, "name")
105             add("\n")
106         def addSource(line):
107             add("  %s" % line.lstrip(), "source")
108
109         cwd = os.getcwd()
110         add("Traceback (most recent call last):\n")
111         while tb is not None:
112             lineno = tb.tb_lineno
113             filename = tb.tb_frame.f_code.co_filename
114             name = tb.tb_frame.f_code.co_name
115             if filename.startswith(cwd):
116                 filename = filename[len(cwd) + 1:]
117             addFile(filename, lineno, name)
118             line = linecache.getline(filename, lineno)
119             if line:
120                 addSource(line)
121             tb = tb.tb_next
122         lines = traceback.format_exception_only(exctype, value)
123         for line in lines[:-1]:
124             m = re.match("^  File \"([^\"]+)\", line (\d+)", line)
125             if m:
126                 addFile(m.group(1), m.group(2))
127             else:
128                 addSource(line)
129         a = lines[-1].split(":")
130         if len(a) == 1:
131             add(a[0], "exception")
132         else:
133             add(a[0], "exception")
134             add(":" + ":".join(a[1:]))
135
136 def show(exctype, value, tb):
137     if exctype != KeyboardInterrupt:
138         window = CrashDialog(exctype, value, tb)
139         window.run()
140         window.destroy()
141     raise SystemExit