If the user clicks on the test button when reporting a bug, and the program
[zeroinstall.git] / zeroinstall / 0launch-gui / bugs.py
blob62a0e2d15b272d2501ebdc7d43dd63b2b49681a6
1 # Copyright (C) 2007, Thomas Leonard
2 # See http://0install.net/0compile.html
4 import sys, os
5 import zeroinstall
6 import gtk, pango
7 import dialog
9 def report_bug(policy, iface):
10 assert iface
12 # TODO: Check the interface to decide where to send bug reports
14 issue_file = '/etc/issue'
15 if os.path.exists(issue_file):
16 issue = file(issue_file).read().strip()
17 else:
18 issue = "(file '%s' not found)" % issue
20 text = 'Problem with %s\n' % iface.uri
21 if iface.uri != policy.root:
22 text = ' (while attempting to run %s)\n' % policy.root
23 text += '\n'
25 text += 'Zero Install: Version %s, with Python %s\n' % (zeroinstall.version, sys.version)
27 text += '\nChosen implementations:\n'
29 if not policy.ready:
30 text += ' Failed to select all required implementations\n'
32 for chosen_iface in policy.implementation:
33 text += '\n Interface: %s\n' % chosen_iface.uri
34 impl = policy.implementation[chosen_iface]
35 if impl:
36 text += ' Version: %s\n' % impl.get_version()
37 if impl.interface != chosen_iface:
38 text += ' From feed: %s\n' % impl.interface.uri
39 text += ' ID: %s\n' % impl.id
40 else:
41 text += ' No implementation selected\n'
43 text += '\nSystem:\n %s\n\nIssue:\n %s\n' % ('\n '.join(os.uname()), issue)
45 reporter = BugReporter(policy, iface, text)
46 reporter.show()
48 class BugReporter(dialog.Dialog):
49 def __init__(self, policy, iface, env):
50 dialog.Dialog.__init__(self)
51 self.set_title('Report a Bug')
52 self.set_modal(True)
53 self.set_has_separator(False)
54 self.policy = policy
55 self.frames = []
57 vbox = gtk.VBox(False, 4)
58 vbox.set_border_width(10)
59 self.vbox.pack_start(vbox, True, True, 0)
61 self.set_default_size(gtk.gdk.screen_width() / 2, -1)
63 def frame(title, contents, buffer):
64 fr = gtk.Frame()
65 label = gtk.Label('')
66 label.set_markup('<b>%s</b>' % title)
67 fr.set_label_widget(label)
68 fr.set_shadow_type(gtk.SHADOW_NONE)
69 vbox.pack_start(fr, True, True, 0)
71 align = gtk.Alignment(0, 0, 1, 1)
72 align.set_padding(0, 0, 16, 0)
73 fr.add(align)
74 align.add(contents)
76 self.frames.append((title, buffer))
78 def text_area(text = None, mono = False):
79 swin = gtk.ScrolledWindow()
80 swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
81 swin.set_shadow_type(gtk.SHADOW_IN)
83 tv = gtk.TextView()
84 tv.set_wrap_mode(gtk.WRAP_WORD)
85 swin.add(tv)
86 if text:
87 tv.get_buffer().insert_at_cursor(text)
89 if mono:
90 tv.modify_font(pango.FontDescription('mono'))
92 tv.set_accepts_tab(False)
94 return swin, tv.get_buffer()
96 actual = text_area()
97 frame("What doesn't work?", *actual)
99 expected = text_area()
100 frame('What did you expect to happen?', *expected)
102 errors_box = gtk.VBox(False, 0)
103 errors_swin, errors_buffer = text_area(mono = True)
104 errors_box.pack_start(errors_swin, True, True, 0)
105 buttons = gtk.HButtonBox()
106 buttons.set_layout(gtk.BUTTONBOX_START)
107 errors_box.pack_start(buttons, False, True, 4)
108 get_errors = gtk.Button('Run it now and record the output')
109 get_errors.connect('clicked', lambda button: self.collect_output(errors_buffer))
110 buttons.add(get_errors)
112 frame('Are any errors or warnings displayed?', errors_box, errors_buffer)
114 if dialog.last_error:
115 errors_buffer.insert_at_cursor(str(dialog.last_error))
117 environ = text_area(env, mono = True)
118 frame('Information about your setup', *environ)
120 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
121 self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
122 self.set_default_response(gtk.RESPONSE_OK)
124 def resp(box, r):
125 if r == gtk.RESPONSE_OK:
126 text = ''
127 for title, buffer in self.frames:
128 start = buffer.get_start_iter()
129 end = buffer.get_end_iter()
130 text += '%s\n\n%s\n\n' % (title, buffer.get_text(start, end).strip())
131 title = 'Bug for %s' % iface.get_name()
132 self.report_bug(title, text)
133 self.destroy()
134 dialog.alert(self, "Your bug report has been sent. Thank you.",
135 type = gtk.MESSAGE_INFO)
136 else:
137 self.destroy()
138 self.connect('response', resp)
140 self.show_all()
142 def collect_output(self, buffer):
143 import logging
144 from zeroinstall.injector import run
146 iter = buffer.get_end_iter()
147 buffer.place_cursor(iter)
149 if not self.policy.ready:
150 missing = [iface.uri for iface in self.policy.implementation if self.policy.implementation[iface] is None]
151 buffer.insert_at_cursor("Can't run: no version has been selected for:\n- " +
152 "\n- ".join(missing))
153 return
154 uncached = self.policy.get_uncached_implementations()
155 if uncached:
156 buffer.insert_at_cursor("Can't run: the chosen versions have not been downloaded yet. I need:\n\n- " +
157 "\n\n- " . join(['%s version %s\n (%s)' %(x[0].uri, x[1].get_version(), x[1].id) for x in uncached]))
158 return
160 r, w = os.pipe()
161 child = os.fork()
162 if child == 0:
163 # We are the child
164 try:
165 try:
166 os.close(r)
167 os.dup2(w, 1)
168 os.dup2(w, 2)
170 logger = logging.getLogger()
171 logger.setLevel(logging.DEBUG)
172 run.execute(self.policy, self.policy.prog_args)
173 except:
174 import traceback
175 traceback.print_exc()
176 finally:
177 os._exit(1)
178 else:
179 os.close(w)
180 reader = os.fdopen(r, 'r')
182 self.hide()
183 try:
184 gtk.gdk.flush()
185 iter = buffer.get_end_iter()
186 buffer.place_cursor(iter)
188 # Cope with invalid UTF-8
189 import codecs
190 decoder = codecs.getdecoder('utf-8')
191 data = decoder(reader.read(), 'replace')[0]
193 buffer.insert_at_cursor(data)
194 reader.close()
196 pid, status = os.waitpid(child, 0)
197 assert pid == child
198 finally:
199 self.show()
201 def report_bug(self, title, text):
202 try:
203 import urllib
204 from urllib2 import urlopen
206 stream = urlopen('http://sourceforge.net/tracker/index.php',
207 urllib.urlencode({
208 'group_id': '76468',
209 'atid': '929902',
210 'func': 'postadd',
211 'is_private': '0',
212 'summary': title,
213 'details': text}))
214 stream.read()
215 stream.close()
216 except:
217 # Write to stderr in the hope that it doesn't get lost
218 print >>sys.stderr, "Error sending bug report: %s\n\n%s" % (title, text)
219 raise