1 # Copyright (C) 2009, Thomas Leonard
2 # See http://0install.net/0compile.html
9 from zeroinstall
import support
10 from zeroinstall
.injector
import selections
12 def report_bug(policy
, iface
):
15 # TODO: Check the interface to decide where to send bug reports
17 issue_file
= '/etc/issue'
18 if os
.path
.exists(issue_file
):
19 issue
= file(issue_file
).read().strip()
21 issue
= "(file '%s' not found)" % issue_file
23 text
= 'Problem with %s\n' % iface
.uri
24 if iface
.uri
!= policy
.root
:
25 text
= ' (while attempting to run %s)\n' % policy
.root
28 text
+= 'Zero Install: Version %s, with Python %s\n' % (zeroinstall
.version
, sys
.version
)
30 text
+= '\nChosen implementations:\n'
33 text
+= ' Failed to select all required implementations\n'
35 for chosen_iface
in policy
.implementation
:
36 text
+= '\n Interface: %s\n' % chosen_iface
.uri
37 impl
= policy
.implementation
[chosen_iface
]
39 text
+= ' Version: %s\n' % impl
.get_version()
40 if impl
.feed
.url
!= chosen_iface
.uri
:
41 text
+= ' From feed: %s\n' % impl
.feed
.url
42 text
+= ' ID: %s\n' % impl
.id
44 impls
= policy
.solver
.details
.get(chosen_iface
, None)
46 best
, reason
= impls
[0]
47 note
= 'best was %s, but: %s' % (best
, reason
)
49 note
= 'not considered; %d available' % len(chosen_iface
.implementations
)
51 text
+= ' No implementation selected (%s)\n' % note
53 if hasattr(os
, 'uname'):
54 text
+= '\nSystem:\n %s\n\nIssue:\n %s\n' % ('\n '.join(os
.uname()), issue
)
56 text
+= '\nSystem without uname()\n'
58 if policy
.solver
.ready
:
59 sels
= selections
.Selections(policy
)
60 text
+= "\n" + sels
.toDOM().toprettyxml(encoding
= 'utf-8')
62 reporter
= BugReporter(policy
, iface
, text
)
65 class BugReporter(dialog
.Dialog
):
66 def __init__(self
, policy
, iface
, env
):
67 dialog
.Dialog
.__init
__(self
)
69 self
.sf_group_id
= 76468
70 self
.sf_artifact_id
= 929902
72 self
.set_title(_('Report a Bug'))
74 self
.set_has_separator(False)
78 vbox
= gtk
.VBox(False, 4)
79 vbox
.set_border_width(10)
80 self
.vbox
.pack_start(vbox
, True, True, 0)
82 self
.set_default_size(gtk
.gdk
.screen_width() / 2, -1)
84 def frame(title
, contents
, buffer):
87 label
.set_markup('<b>%s</b>' % title
)
88 fr
.set_label_widget(label
)
89 fr
.set_shadow_type(gtk
.SHADOW_NONE
)
90 vbox
.pack_start(fr
, True, True, 0)
92 align
= gtk
.Alignment(0, 0, 1, 1)
93 align
.set_padding(0, 0, 16, 0)
97 self
.frames
.append((title
, buffer))
99 def text_area(text
= None, mono
= False):
100 swin
= gtk
.ScrolledWindow()
101 swin
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_ALWAYS
)
102 swin
.set_shadow_type(gtk
.SHADOW_IN
)
105 tv
.set_wrap_mode(gtk
.WRAP_WORD
)
108 tv
.get_buffer().insert_at_cursor(text
)
111 tv
.modify_font(pango
.FontDescription('mono'))
113 tv
.set_accepts_tab(False)
115 return swin
, tv
.get_buffer()
118 frame(_("What doesn't work?"), *actual
)
120 expected
= text_area()
121 frame(_('What did you expect to happen?'), *expected
)
123 errors_box
= gtk
.VBox(False, 0)
124 errors_swin
, errors_buffer
= text_area(mono
= True)
125 errors_box
.pack_start(errors_swin
, True, True, 0)
126 buttons
= gtk
.HButtonBox()
127 buttons
.set_layout(gtk
.BUTTONBOX_START
)
128 errors_box
.pack_start(buttons
, False, True, 4)
129 get_errors
= gtk
.Button(_('Run it now and record the output'))
130 get_errors
.connect('clicked', lambda button
: self
.collect_output(errors_buffer
))
131 buttons
.add(get_errors
)
133 frame(_('Are any errors or warnings displayed?'), errors_box
, errors_buffer
)
135 if dialog
.last_error
:
136 errors_buffer
.insert_at_cursor(str(dialog
.last_error
))
138 environ
= text_area(env
, mono
= True)
139 frame(_('Information about your setup'), *environ
)
141 browse_url
= 'http://sourceforge.net/tracker/?group_id=%d&atid=%d' % (self
.sf_group_id
, self
.sf_artifact_id
)
142 location_hbox
= gtk
.HBox(False, 4)
143 location_hbox
.pack_start(gtk
.Label(_('Bugs reports will be sent to:')), False, True, 0)
144 if hasattr(gtk
, 'LinkButton'):
146 url_box
= gtk
.LinkButton(browse_url
)
147 url_box
.connect('clicked', lambda button
: browser
.open_in_browser(browse_url
))
149 url_box
= gtk
.Label(browse_url
)
150 url_box
.set_selectable(True)
151 location_hbox
.pack_start(url_box
, False, True, 0)
152 vbox
.pack_start(location_hbox
, False, True, 0)
154 self
.add_button(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
)
155 self
.add_button(gtk
.STOCK_OK
, gtk
.RESPONSE_OK
)
156 self
.set_default_response(gtk
.RESPONSE_OK
)
159 if r
== gtk
.RESPONSE_OK
:
161 for title
, buffer in self
.frames
:
162 start
= buffer.get_start_iter()
163 end
= buffer.get_end_iter()
164 text
+= '%s\n\n%s\n\n' % (title
, buffer.get_text(start
, end
).strip())
165 title
= _('Bug for %s') % iface
.get_name()
166 self
.report_bug(title
, text
)
168 dialog
.alert(self
, _("Your bug report has been sent. Thank you."),
169 type = gtk
.MESSAGE_INFO
)
172 self
.connect('response', resp
)
176 def collect_output(self
, buffer):
177 iter = buffer.get_end_iter()
178 buffer.place_cursor(iter)
180 if not self
.policy
.ready
:
181 missing
= [iface
.uri
for iface
in self
.policy
.implementation
if self
.policy
.implementation
[iface
] is None]
182 buffer.insert_at_cursor("Can't run: no version has been selected for:\n- " +
183 "\n- ".join(missing
))
185 uncached
= self
.policy
.get_uncached_implementations()
187 buffer.insert_at_cursor("Can't run: the chosen versions have not been downloaded yet. I need:\n\n- " +
188 "\n\n- " . join(['%s version %s\n (%s)' %(x
[0].uri
, x
[1].get_version(), x
[1].id) for x
in uncached
]))
191 from zeroinstall
.injector
import selections
192 sels
= selections
.Selections(self
.policy
)
198 iter = buffer.get_end_iter()
199 buffer.place_cursor(iter)
201 # Tell 0launch to run the program
202 doc
.documentElement
.setAttribute('run-test', 'true')
203 payload
= doc
.toxml('utf-8')
204 sys
.stdout
.write(('Length:%8x\n' % len(payload
)) + payload
)
207 reply
= support
.read_bytes(0, len('Length:') + 9)
208 assert reply
.startswith('Length:')
209 test_output
= support
.read_bytes(0, int(reply
.split(':', 1)[1], 16))
211 # Cope with invalid UTF-8
213 decoder
= codecs
.getdecoder('utf-8')
214 data
= decoder(test_output
, 'replace')[0]
216 buffer.insert_at_cursor(data
)
220 def report_bug(self
, title
, text
):
223 from urllib2
import urlopen
225 stream
= urlopen('http://sourceforge.net/tracker/index.php',
227 'group_id': str(self
.sf_group_id
),
228 'atid': str(self
.sf_artifact_id
),
236 # Write to stderr in the hope that it doesn't get lost
237 print >>sys
.stderr
, "Error sending bug report: %s\n\n%s" % (title
, text
)