Merge lp:~brian-murray/ubuntu/natty/apport/rr-if-idontknow into lp:~package-import/ubuntu/natty/apport/defunct

Proposed by Brian Murray on 2011-02-14
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~brian-murray/ubuntu/natty/apport/rr-if-idontknow
Merge into: lp:~package-import/ubuntu/natty/apport/defunct
Diff against target: 7133 lines (+2156/-1226)
48 files modified
NEWS (+87/-1)
apport/REThread.py (+7/-7)
apport/__init__.py (+23/-1)
apport/chroot.py (+6/-6)
apport/crashdb.py (+24/-24)
apport/crashdb_impl/launchpad.py (+301/-247)
apport/crashdb_impl/memory.py (+5/-5)
apport/crashdb_impl/multipartpost_handler.py (+1/-1)
apport/fileutils.py (+22/-12)
apport/hookutils.py (+68/-22)
apport/packaging.py (+19/-19)
apport/report.py (+133/-81)
apport/ui.py (+343/-246)
apport_python_hook.py (+20/-16)
backends/packaging-apt-dpkg.py (+79/-112)
bin/apport-cli (+24/-22)
bin/apport-retrace (+10/-13)
bin/apport-unpack (+3/-6)
bin/crash-digger (+17/-20)
bin/dupdb-admin (+5/-6)
data/apport (+15/-16)
data/gcc_ice_hook (+2/-2)
data/general-hooks/parse_segv.py (+23/-32)
data/general-hooks/ubuntu.py (+47/-1)
data/java_uncaught_exception (+86/-0)
data/kernel_crashdump (+1/-2)
data/package-hooks/source_linux.py (+14/-13)
data/package_hook (+1/-1)
data/unkillable_shutdown (+5/-5)
debian/apport.install (+1/-0)
debian/apport.postinst (+10/-0)
debian/changelog (+197/-0)
debian/control (+41/-21)
debian/rules (+3/-3)
doc/symptoms.txt (+1/-1)
etc/apport/crashdb.conf (+0/-1)
etc/default/apport (+1/-1)
gtk/apport-gtk (+85/-96)
gtk/apport-gtk.ui (+5/-17)
java/README (+13/-0)
java/com/ubuntu/apport/ApportUncaughtExceptionHandler.java (+108/-0)
java/crash.java (+8/-0)
kde/apport-kde (+3/-4)
problem_report.py (+129/-134)
setup.py (+65/-8)
test/hooks (+5/-1)
test/java (+86/-0)
test/run (+4/-0)
To merge this branch: bzr merge lp:~brian-murray/ubuntu/natty/apport/rr-if-idontknow
Reviewer Review Type Date Requested Status
Martin Pitt (community) 2011-02-14 Approve on 2011-02-15
Review via email: mp+49696@code.launchpad.net

Description of the change

If the bug reporter chooses "I don't know" when asked how to describe the regression a Traceback is received as seen in https://bugs.launchpad.net/null/+bug/708712/comments/9. This fixes that.

To post a comment you must log in.
Martin Pitt (pitti) wrote :

Thank you!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-07-13 05:42:32 +0000
3+++ NEWS 2011-02-14 19:45:28 +0000
4@@ -1,12 +1,74 @@
5 This file summarizes the major and interesting changes for each release. For a
6 detailled list of changes, please see ChangeLog.
7
8-1.15 (UNRELEASED)
9+1.17.3 (UNRELEASED)
10+-------------------
11+Bug fixes:
12+ - Ensure that symptom scripts define a run() function, and don't show them if
13+ not.
14+ - Do not show symptom scripts which start with an underscore. These can be
15+ used for private libraries for the actual symptom scripts.
16+
17+1.17.2 (2011-02-04)
18+-------------------
19+Improvements:
20+ - Be more Python 3 compatible (not fully working with Python 3 yet, though).
21+ - apt/dpkg backend: Drop support for pre-0.7.9 python-apt API.
22+ - Add --tag option to add extra tags to reports. (LP: #572504)
23+
24+Bug fixes:
25+ - hookutils.py, attach_dmesg(): Do not overwrite already existing dmesg.
26+ - hookutils.py: Be more robust against file permission errors. (LP: #444678)
27+ - ui.py: Do not show all the options in --help when invoked as *-bug.
28+ (LP: #665953)
29+ - launchpad.py: Adapt test cases to current standard_title() behaviour.
30+
31+1.17.1 (2011-01-10)
32+-------------------
33+Bug fixes:
34+ - Make the GTK frontend work with GTK 2.0 as well, and drop "3.0" requirement.
35+
36+1.17 (2010-12-31)
37+-----------------
38+Improvements:
39+ - Better standard bug titles for Python crashes. Thanks Matt Zimmerman!
40+ (LP: #681574)
41+ - Add handler for uncaught Java exceptions. There is no integration for
42+ automatically intercepting all Java crashes yet, see java/README.
43+ Thanks Matt Zimmerman! (LP: #548877)
44+
45+Bug fixes:
46+ - GTK frontend: Require GTK 3.0.
47+ - launchpad.py: Default to "production" instance, not "edge", since edge is
48+ obsolete now.
49+ - hookutils.py, attach_alsa(): Fix crash if /proc/asound/cards does not exist.
50+ (LP: #626215)
51+ - ui.py, format_filesize(): Fix to work with stricter locale.format() in
52+ Python 2.7. (LP: #688535). While we are at it, also change it to use base-10
53+ units.
54+ - hookutils.py, package_versions(): Always include all requested package names
55+ even if they're unknown to us. Thanks Matt Zimmerman! (LP: #695188)
56+ - launchpad.py: When updating a bug, also add new tags. Thanks Brian Murray!
57+
58+1.16 (2010-11-19)
59+-----------------
60+New features:
61+ - Port GTK frontend from pygtk2 to GTK+3.0 and gobject-introspection.
62+
63+Bug fixes:
64+ - Fix symptoms again. Version 1.15 broke the default symptom directory.
65+ - Fix memory test case to work with current Python versions, where the SQLite
66+ integrity check throws a different exception.
67+
68+1.15 (2010-11-11)
69 -----------------
70 New features:
71 - Add dump_acpi_tables.py script. This can be called by package hooks which
72 need ACPI table information (in particular, kernel bug reports). Thanks to
73 Brad Figg for the script!
74+ - Order symptom descriptions alphabetically. Thanks to Javier Collado.
75+ - Check $APPORT_SYMPTOMS_DIR environment variable for overriding the system
76+ default path. Thanks to Javier Collado.
77
78 Bug fixes:
79 - testsuite: Check that crashdb.conf can have dynamic code to determine DB
80@@ -14,6 +76,30 @@
81 - ui.py test suite: Rewrite _gen_test_crash() to have the test process core
82 dump itself, instead of using gdb to do it. The latter fails in ptrace
83 restricted environments, such as Ubuntu 10.10.
84+ - packaging-apt-dpkg.py: Fix handling of /etc/apport/native-origins.d to
85+ actually work. Thanks Steve Langasek. (LP: #627777)
86+ - apport-kde: Load correct translation catalogue. Thanks Jonathan Riddell.
87+ (LP: #633483)
88+ - launchpad.py: Use launchpadlib to file a bug instead of screen scraping.
89+ The latter was completely broken with current Launchpad, so this makes the
90+ test suite actually work again. Thanks to Diogo Matsubara!
91+ - launchpad.py: Change $APPORT_STAGING to $APPORT_LAUNCHPAD_INSTANCE, so that
92+ you can now specify "staging", "edge", or "dev" (for a local
93+ http://launchpad.dev installation). Thanks to Diogo Matsubara!
94+ - backends/packaging-apt-dpkg.py: Fix crash on empty lines in ProcMaps
95+ attachment.
96+ - doc/symptoms.txt: Fix typo, thanks Philip Muskovac. (LP: #590521)
97+ - apport/hookutils.py: rename ProcCmdLine to ProcKernelCmdLine to not wipe
98+ wipe out /proc/$pid/cmdline information. (LP: #657091)
99+ - apport/hookutils.py: attach_file() will not overwrite existing report
100+ keys, instead appending "_" until the key is unique.
101+ - Fix --save option to recognise ~, thanks Philip Muškovac. (LP: #657278)
102+ - Remove escalation_subscription from Ubuntu bug DB definition, turned out to
103+ not be useful; thanks Brian Murray.
104+ - launchpad.py: Fix APPORT_LAUNCHPAD_INSTANCE values with a https:// prefix.
105+ - apt backend: Opportunistically try to install a -dbg package in addition to
106+ -dbgsym, to increase the chance that at least one of it exists. Thanks
107+ Daniel J Blueman!
108
109 1.14.1 (2010-06-24)
110 -------------------
111
112=== modified file 'apport/REThread.py'
113--- apport/REThread.py 2009-12-23 11:01:21 +0000
114+++ apport/REThread.py 2011-02-14 19:45:28 +0000
115@@ -64,7 +64,7 @@
116 #
117
118 if __name__ == '__main__':
119- import unittest, time, traceback, exceptions
120+ import unittest, time, traceback
121
122 def idle(seconds):
123 '''Test thread to just wait a bit.'''
124@@ -107,11 +107,11 @@
125 t.join()
126 # thread did not terminate normally, no return value
127 self.assertRaises(AssertionError, t.return_value)
128- self.assert_(t.exc_info()[0] == exceptions.ZeroDivisionError)
129+ self.assertTrue(t.exc_info()[0] == ZeroDivisionError)
130 exc = traceback.format_exception(t.exc_info()[0], t.exc_info()[1],
131 t.exc_info()[2])
132- self.assert_(exc[-1].startswith('ZeroDivisionError'))
133- self.assert_(exc[-2].endswith('return x / y\n'))
134+ self.assertTrue(exc[-1].startswith('ZeroDivisionError'), 'not a ZeroDivisionError:' + str(exc))
135+ self.assertTrue(exc[-2].endswith('return x / y\n'))
136
137 def test_exc_raise(self):
138 '''exc_raise() raises caught thread exception.'''
139@@ -128,9 +128,9 @@
140 raised = True
141 e = sys.exc_info()
142 exc = traceback.format_exception(e[0], e[1], e[2])
143- self.assert_(exc[-1].startswith('ZeroDivisionError'))
144- self.assert_(exc[-2].endswith('return x / y\n'))
145- self.assert_(raised)
146+ self.assertTrue(exc[-1].startswith('ZeroDivisionError'), 'not a ZeroDivisionError:' + str(e))
147+ self.assertTrue(exc[-2].endswith('return x / y\n'))
148+ self.assertTrue(raised)
149
150 unittest.main()
151
152
153=== modified file 'apport/__init__.py'
154--- apport/__init__.py 2009-09-08 12:03:44 +0000
155+++ apport/__init__.py 2011-02-14 19:45:28 +0000
156@@ -2,11 +2,33 @@
157
158 from apport.packaging_impl import impl as packaging
159
160+import sys
161+
162 # fix gettext to output proper unicode strings
163 import gettext
164
165 def unicode_gettext(str):
166 trans = gettext.gettext(str)
167- if type(trans) == type(u''):
168+ if isinstance(trans, unicode):
169 return trans
170 return trans.decode('UTF-8')
171+
172+def fatal(msg, *args):
173+ '''Print out an error message and exit the program.'''
174+
175+ error(msg, *args)
176+ sys.exit(1)
177+
178+def error(msg, *args):
179+ '''Print out an error message.'''
180+
181+ sys.stderr.write('ERROR: ')
182+ sys.stderr.write(msg % args)
183+ sys.stderr.write('\n')
184+
185+def warning(msg, *args):
186+ '''Print out an warning message.'''
187+
188+ sys.stderr.write('WARNING: ')
189+ sys.stderr.write(msg % args)
190+ sys.stderr.write('\n')
191
192=== modified file 'apport/chroot.py'
193--- apport/chroot.py 2009-12-23 11:01:21 +0000
194+++ apport/chroot.py 2011-02-14 19:45:28 +0000
195@@ -227,7 +227,7 @@
196 os.close(fd)
197 c.tar(tarpath)
198 t = tarfile.open(tarpath)
199- self.assert_(
200+ self.assertTrue(
201 # python 2.5's tarfile
202 set(['./bin/42', './bin/hello', './newfile']).issubset(set(t.getnames())) or
203 # python 2.4's tarfile
204@@ -236,7 +236,7 @@
205
206 # test cleanup
207 del c
208- self.assert_(os.path.exists(os.path.join(d, 'bin', '42')),
209+ self.assertTrue(os.path.exists(os.path.join(d, 'bin', '42')),
210 'directory chroot should not delete the chroot')
211
212 finally:
213@@ -274,7 +274,7 @@
214 open(os.path.join(c.root, 'newfile'), 'w')
215 c.tar()
216 t = tarfile.open(tar)
217- self.assert_(
218+ self.assertTrue(
219 # python 2.5's tarfile
220 set(['./bin/42', './bin/hello', './newfile']).issubset(set(t.getnames())) or
221 # python 2.4's tarfile
222@@ -284,7 +284,7 @@
223 # test cleanup
224 d = c.root
225 del c
226- self.assert_(not os.path.exists(d),
227+ self.assertTrue(not os.path.exists(d),
228 'tarball chroot should delete the temporary chroot')
229 finally:
230 os.unlink(tar)
231@@ -364,8 +364,8 @@
232 self.assertEqual(err, '')
233 self.assertEqual(result, 0)
234 files = out.splitlines()
235- self.assert_('bin' in files)
236- self.assert_('lib' in files)
237+ self.assertTrue('bin' in files)
238+ self.assertTrue('lib' in files)
239
240 # complex shell commands: relative symlinks and paths
241 self.assertEqual(c.run_capture(['bash'], '''set -e
242
243=== modified file 'apport/crashdb.py'
244--- apport/crashdb.py 2009-12-23 10:55:52 +0000
245+++ apport/crashdb.py 2011-02-14 19:45:28 +0000
246@@ -83,7 +83,7 @@
247 cur.execute('PRAGMA integrity_check')
248 result = cur.fetchall()
249 if result != [('ok',)]:
250- raise SystemError, 'Corrupt duplicate db:' + str(result)
251+ raise SystemError('Corrupt duplicate db:' + str(result))
252
253 def check_duplicate(self, id, report=None):
254 '''Check whether a crash is already known.
255@@ -225,10 +225,10 @@
256 # crash got fixed/rejected
257 fixed_ver = self.get_fixed_version(id)
258 if fixed_ver == 'invalid':
259- print 'DEBUG: bug %i was invalidated, removing from database' % id
260+ print('DEBUG: bug %i was invalidated, removing from database' % id)
261 cur2.execute('DELETE FROM crashes WHERE crash_id = ?', [id])
262 elif not fixed_ver:
263- print 'WARNING: inconsistency detected: bug #%i does not appear in get_unfixed(), but is not fixed yet' % id
264+ print('WARNING: inconsistency detected: bug #%i does not appear in get_unfixed(), but is not fixed yet' % id)
265 else:
266 cur2.execute('UPDATE crashes SET fixed_version = ?, last_change = CURRENT_TIMESTAMP WHERE crash_id = ?',
267 (fixed_ver, id))
268@@ -328,7 +328,7 @@
269
270 This method can raise a NeedsCredentials exception in case of failure.
271 '''
272- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
273+ raise NotImplementedError('this method must be implemented by a concrete subclass')
274
275 def get_comment_url(self, report, handle):
276 '''Return an URL that should be opened after report has been uploaded
277@@ -338,12 +338,12 @@
278 user comments); in that case this function should do whichever
279 interactive steps it wants to perform.
280 '''
281- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
282+ raise NotImplementedError('this method must be implemented by a concrete subclass')
283
284 def download(self, id):
285 '''Download the problem report from given ID and return a Report.'''
286
287- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
288+ raise NotImplementedError('this method must be implemented by a concrete subclass')
289
290 def update(self, id, report, comment, change_description=False,
291 attachment_comment=None, key_filter=None):
292@@ -362,7 +362,7 @@
293
294 If key_filter is a list or set, then only those keys will be added.
295 '''
296- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
297+ raise NotImplementedError('this method must be implemented by a concrete subclass')
298
299 def update_traces(self, id, report, comment=''):
300 '''Update the given report ID for retracing results.
301@@ -376,12 +376,12 @@
302 def set_credentials(self, username, password):
303 '''Set username and password.'''
304
305- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
306+ raise NotImplementedError('this method must be implemented by a concrete subclass')
307
308 def get_distro_release(self, id):
309 '''Get 'DistroRelease: <release>' from the report ID.'''
310
311- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
312+ raise NotImplementedError('this method must be implemented by a concrete subclass')
313
314 def get_unretraced(self):
315 '''Return set of crash IDs which have not been retraced yet.
316@@ -389,7 +389,7 @@
317 This should only include crashes which match the current host
318 architecture.
319 '''
320- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
321+ raise NotImplementedError('this method must be implemented by a concrete subclass')
322
323 def get_dup_unchecked(self):
324 '''Return set of crash IDs which need duplicate checking.
325@@ -398,7 +398,7 @@
326 Python, since they do not need to be retraced. It should not return
327 bugs that are covered by get_unretraced().
328 '''
329- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
330+ raise NotImplementedError('this method must be implemented by a concrete subclass')
331
332 def get_unfixed(self):
333 '''Return an ID set of all crashes which are not yet fixed.
334@@ -409,7 +409,7 @@
335 there are any errors with connecting to the crash database, it should
336 raise an exception (preferably IOError).
337 '''
338- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
339+ raise NotImplementedError('this method must be implemented by a concrete subclass')
340
341 def get_fixed_version(self, id):
342 '''Return the package version that fixes a given crash.
343@@ -423,17 +423,17 @@
344 there are any errors with connecting to the crash database, it should
345 raise an exception (preferably IOError).
346 '''
347- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
348+ raise NotImplementedError('this method must be implemented by a concrete subclass')
349
350 def get_affected_packages(self, id):
351 '''Return list of affected source packages for given ID.'''
352
353- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
354+ raise NotImplementedError('this method must be implemented by a concrete subclass')
355
356 def is_reporter(self, id):
357 '''Check whether the user is the reporter of given ID.'''
358
359- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
360+ raise NotImplementedError('this method must be implemented by a concrete subclass')
361
362 def can_update(self, id):
363 '''Check whether the user is eligible to update a report.
364@@ -443,32 +443,32 @@
365 exact policy and checks should be done according to the particular
366 implementation.
367 '''
368- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
369+ raise NotImplementedError('this method must be implemented by a concrete subclass')
370
371 def duplicate_of(self, id):
372 '''Return master ID for a duplicate bug.
373
374 If the bug is not a duplicate, return None.
375 '''
376- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
377+ raise NotImplementedError('this method must be implemented by a concrete subclass')
378
379 def close_duplicate(self, id, master):
380 '''Mark a crash id as duplicate of given master ID.
381
382 If master is None, id gets un-duplicated.
383 '''
384- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
385+ raise NotImplementedError('this method must be implemented by a concrete subclass')
386
387 def mark_regression(self, id, master):
388 '''Mark a crash id as reintroducing an earlier crash which is
389 already marked as fixed (having ID 'master').'''
390
391- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
392+ raise NotImplementedError('this method must be implemented by a concrete subclass')
393
394 def mark_retraced(self, id):
395 '''Mark crash id as retraced.'''
396
397- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
398+ raise NotImplementedError('this method must be implemented by a concrete subclass')
399
400 def mark_retrace_failed(self, id, invalid_msg=None):
401 '''Mark crash id as 'failed to retrace'.
402@@ -478,14 +478,14 @@
403
404 This can be a no-op if you are not interested in this.
405 '''
406- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
407+ raise NotImplementedError('this method must be implemented by a concrete subclass')
408
409 def _mark_dup_checked(self, id, report):
410 '''Mark crash id as checked for being a duplicate
411
412 This is an internal method that should not be called from outside.
413 '''
414- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
415+ raise NotImplementedError('this method must be implemented by a concrete subclass')
416
417 #
418 # factory
419@@ -525,9 +525,9 @@
420 if os.path.isfile(cfpath) and cf.endswith('.conf'):
421 try:
422 execfile(cfpath, settings['databases'])
423- except Exception, e:
424+ except Exception as e:
425 # ignore broken files
426- print >> sys.stderr, 'Invalid file %s: %s' % (cfpath, str(e))
427+ sys.stderr.write('Invalid file %s: %s\n' % (cfpath, str(e)))
428 pass
429
430 if not name:
431
432=== modified file 'apport/crashdb_impl/launchpad.py'
433--- apport/crashdb_impl/launchpad.py 2010-06-16 13:50:47 +0000
434+++ apport/crashdb_impl/launchpad.py 2011-02-14 19:45:28 +0000
435@@ -10,12 +10,15 @@
436 # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
437 # the full text of the license.
438
439-import urllib, tempfile, shutil, os.path, re, gzip, sys, socket, ConfigParser
440+import urllib, tempfile, os.path, re, gzip, sys
441 import email
442-from cStringIO import StringIO
443+try:
444+ from cStringIO import StringIO
445+except ImportError:
446+ from io import StringIO
447
448 from launchpadlib.errors import HTTPError
449-from launchpadlib.launchpad import Launchpad, STAGING_SERVICE_ROOT, EDGE_SERVICE_ROOT
450+from launchpadlib.launchpad import Launchpad
451
452 import apport.crashdb
453 import apport
454@@ -26,8 +29,8 @@
455 for attachment in attachments:
456 try:
457 f = attachment.data.open()
458- except HTTPError, e:
459- print >> sys.stderr, 'ERROR: Broken attachment on bug, ignoring'
460+ except HTTPError:
461+ apport.error('Broken attachment on bug, ignoring')
462 continue
463 name = f.filename
464 if name.endswith('.txt') or name.endswith('.gz'):
465@@ -52,8 +55,9 @@
466 - distro: Name of the distribution in Launchpad
467 - project: Name of the project in Launchpad
468 (Note that exactly one of "distro" or "project" must be given.)
469- - staging: If set, this uses staging instead of production (optional).
470- This can be overriden or set by $APPORT_STAGING environment.
471+ - launchpad_instance: If set, this uses the given launchpad instance
472+ instead of production (optional). This can be overriden or set by
473+ $APPORT_LAUNCHPAD_INSTANCE environment.
474 - cache_dir: Path to a permanent cache directory; by default it uses a
475 temporary one. (optional). This can be overridden or set by
476 $APPORT_LAUNCHPAD_CACHE environment.
477@@ -62,11 +66,13 @@
478 - escalation_tag: This adds the given tag to a bug once it gets more
479 than 10 duplicates.
480 '''
481- if os.getenv('APPORT_STAGING'):
482- options['staging'] = True
483+ if os.getenv('APPORT_LAUNCHPAD_INSTANCE'):
484+ options['launchpad_instance'] = os.getenv(
485+ 'APPORT_LAUNCHPAD_INSTANCE')
486 if not auth:
487- if options.get('staging'):
488- auth = default_credentials_path + '.staging'
489+ lp_instance = options.get('launchpad_instance')
490+ if lp_instance:
491+ auth = default_credentials_path + '.' + lp_instance.split('://', 1)[-1]
492 else:
493 auth = default_credentials_path
494 apport.crashdb.CrashDatabase.__init__(self, auth,
495@@ -94,10 +100,10 @@
496 if self.__launchpad:
497 return self.__launchpad
498
499- if self.options.get('staging'):
500- launchpad_instance = STAGING_SERVICE_ROOT
501+ if self.options.get('launchpad_instance'):
502+ launchpad_instance = self.options.get('launchpad_instance')
503 else:
504- launchpad_instance = EDGE_SERVICE_ROOT
505+ launchpad_instance = 'production'
506
507 auth_dir = os.path.dirname(self.auth)
508 if auth_dir and not os.path.isdir(auth_dir):
509@@ -109,12 +115,12 @@
510 allow_access_levels=['WRITE_PRIVATE'],
511 credentials_file = self.auth,
512 version='1.0')
513- except Exception, e:
514+ except Exception as e:
515 if hasattr(e, 'content'):
516 msg = e.content
517 else:
518 msg = str(e)
519- print >> sys.stderr, 'Error connecting to Launchpad: %s\nYou can reset the credentials by removing the file "%s"' % (msg, self.auth)
520+ apport.error('connecting to Launchpad failed: %s\nYou can reset the credentials by removing the file "%s"', msg, self.auth)
521 sys.exit(99) # transient error
522
523 return self.__launchpad
524@@ -185,10 +191,23 @@
525 mime.seek(0)
526
527 ticket = upload_blob(mime, progress_callback,
528- staging=self.options.get('staging', False))
529+ hostname=self.get_hostname())
530 assert ticket
531 return ticket
532
533+ def get_hostname(self):
534+ '''Return the hostname for the Launchpad instance.'''
535+
536+ launchpad_instance = self.options.get('launchpad_instance')
537+ if launchpad_instance:
538+ if launchpad_instance == 'staging':
539+ hostname = 'staging.launchpad.net'
540+ else:
541+ hostname = 'launchpad.dev'
542+ else:
543+ hostname = 'launchpad.net'
544+ return hostname
545+
546 def get_comment_url(self, report, handle):
547 '''Return an URL that should be opened after report has been uploaded
548 and upload() returned handle.
549@@ -202,13 +221,10 @@
550 if title:
551 args['field.title'] = title
552
553- if self.options.get('staging'):
554- hostname = 'staging.launchpad.net'
555- else:
556- hostname = 'launchpad.net'
557+ hostname = self.get_hostname()
558
559 project = self.options.get('project')
560-
561+
562 if not project:
563 if report.has_key('SourcePackage'):
564 return 'https://bugs.%s/%s/+source/%s/+filebug/%s?%s' % (
565@@ -269,7 +285,7 @@
566 elif 'apport-package' in b.tags:
567 report['ProblemType'] = 'Package'
568 else:
569- raise ValueError, 'cannot determine ProblemType from tags: ' + str(b.tags)
570+ raise ValueError('cannot determine ProblemType from tags: ' + str(b.tags))
571
572 report['Tags'] = ' '.join(b.tags)
573
574@@ -290,7 +306,7 @@
575 elif ext == '.gz':
576 try:
577 report[key] = gzip.GzipFile(fileobj=attachment).read()
578- except IOError, e:
579+ except IOError as e:
580 # some attachments are only called .gz, but are
581 # uncompressed (LP #574360)
582 if 'Not a gzip' not in str(e):
583@@ -298,7 +314,7 @@
584 attachment.seek(0)
585 report[key] = attachment.read()
586 else:
587- raise Exception, 'Unknown attachment type: ' + attachment.filename
588+ raise Exception('Unknown attachment type: ' + attachment.filename)
589 return report
590
591 def update(self, id, report, comment, change_description=False,
592@@ -350,6 +366,9 @@
593 # with apport-collect
594 x = bug.tags[:] # LP#254901 workaround
595 x.append('apport-collected')
596+ # add any tags (like the release) to the bug
597+ if report.has_key('Tags'):
598+ x += report['Tags'].split()
599 bug.tags = x
600 bug.lp_save()
601 bug = self.launchpad.bugs[id] # fresh bug object, LP#336866 workaround
602@@ -412,7 +431,7 @@
603 m = re.search('DistroRelease: ([-a-zA-Z0-9.+/ ]+)', bug.description)
604 if m:
605 return m.group(1)
606- raise ValueError, 'URL does not contain DistroRelease: field'
607+ raise ValueError('URL does not contain DistroRelease: field')
608
609 def get_affected_packages(self, id):
610 '''Return list of affected source packages for given ID.'''
611@@ -544,7 +563,7 @@
612 return ''
613
614 if len(fixed_tasks) > 1:
615- print >> sys.stderr, 'WARNING: There is more than one task fixed in %s %s, using first one to determine fixed version' % (self.distro, id)
616+ apport.warning('There is more than one task fixed in %s %s, using first one to determine fixed version', self.distro, id)
617 return ''
618
619 if fixed_tasks:
620@@ -780,7 +799,7 @@
621 def https_open(self, req):
622 return self.do_open(HTTPSProgressConnection, req)
623
624-def upload_blob(blob, progress_callback = None, staging=False):
625+def upload_blob(blob, progress_callback = None, hostname='launchpad.net'):
626 '''Upload blob (file-like object) to Launchpad.
627
628 progress_callback can be set to a function(sent, total) which is regularly
629@@ -790,8 +809,9 @@
630
631 Return None on error, or the ticket number on success.
632
633- By default this uses the production Launchpad instance. Set staging=True to
634- use staging.launchpad.net (for testing).
635+ By default this uses the production Launchpad hostname. Set
636+ hostname to 'launchpad.dev' or 'staging.launchpad.net' to use another
637+ instance for testing.
638 '''
639 ticket = None
640
641@@ -799,10 +819,7 @@
642 _https_upload_callback = progress_callback
643
644 opener = urllib2.build_opener(HTTPSProgressHandler, multipartpost_handler.MultipartPostHandler)
645- if staging:
646- url = 'https://staging.launchpad.net/+storeblob'
647- else:
648- url = 'https://launchpad.net/+storeblob'
649+ url = 'https://%s/+storeblob' % hostname
650 result = opener.open(url,
651 { 'FORM_SUBMIT': '1', 'field.blob': blob })
652 ticket = result.info().get('X-Launchpad-Blob-Token')
653@@ -814,7 +831,7 @@
654 #
655
656 if __name__ == '__main__':
657- import unittest, urllib2, cookielib
658+ import unittest, urllib2
659
660 crashdb = None
661 segv_report = None
662@@ -825,8 +842,6 @@
663 # binary package 'coreutils'
664 test_package = 'coreutils'
665 test_srcpackage = 'coreutils'
666- known_test_id = 302779
667- known_test_id2 = 89040
668
669 #
670 # Generic tests, should work for all CrashDB implementations
671@@ -845,6 +860,54 @@
672 self.ref_report.add_user_info()
673 self.ref_report['SourcePackage'] = 'coreutils'
674
675+ # Objects tests rely on.
676+ self.uncommon_description_bug = self._file_uncommon_description_bug()
677+ self._create_project('langpack-o-matic')
678+
679+ # XXX Should create new bug reports, not reuse those.
680+ self.known_test_id = self.uncommon_description_bug.id
681+ self.known_test_id2 = self._file_uncommon_description_bug().id
682+
683+ def _create_project(self, name):
684+ '''Create a project using launchpadlib to be used by tests.'''
685+
686+ project = self.crashdb.launchpad.projects[name]
687+ if not project:
688+ self.crashdb.launchpad.projects.new_project(
689+ description=name + 'description',
690+ display_name=name,
691+ name=name,
692+ summary=name + 'summary',
693+ title=name + 'title')
694+
695+ def _file_uncommon_description_bug(self):
696+ '''File a bug report with an uncommon description.
697+
698+ Example taken from real LP bug 269539. It contains only
699+ ProblemType/Architecture/DistroRelease in the description, and has
700+ free-form description text after the Apport data.
701+ '''
702+ desc = '''problem
703+
704+ProblemType: Package
705+Architecture: amd64
706+DistroRelease: Ubuntu 8.10
707+
708+more text
709+
710+and more
711+'''
712+ return self.crashdb.launchpad.bugs.createBug(
713+ title=u'mixed description bug',
714+ description=desc,
715+ target=self.crashdb.lp_distro)
716+
717+ @property
718+ def hostname(self):
719+ '''Get the Launchpad hostname for the given crashdb.'''
720+
721+ return self.crashdb.get_hostname()
722+
723 def _file_segv_report(self):
724 '''File a SEGV crash report.
725
726@@ -862,12 +925,12 @@
727 r['LongGibberish'] = 'a\nb\nc\nd\ne\n\xff\xff\xff\n\f'
728
729 handle = self.crashdb.upload(r)
730- self.assert_(handle)
731- url = self.crashdb.get_comment_url(r, handle)
732- self.assert_(url)
733+ self.assertTrue(handle)
734+ bug_target = self._get_bug_target(self.crashdb, r)
735+ self.assertTrue(bug_target)
736
737- id = self._fill_bug_form(url)
738- self.assert_(id > 0)
739+ id = self._file_bug(bug_target, r, handle)
740+ self.assertTrue(id > 0)
741 return id
742
743 def test_1_report_segv(self):
744@@ -878,7 +941,7 @@
745 global segv_report
746 id = self._file_segv_report()
747 segv_report = id
748- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
749+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
750
751 def test_1_report_python(self):
752 '''upload() and get_comment_url() for Python crash
753@@ -889,24 +952,25 @@
754 r['ExecutablePath'] = '/bin/foo'
755 r['Traceback'] = '''Traceback (most recent call last):
756 File "/bin/foo", line 67, in fuzz
757- print weird
758+ print(weird)
759 NameError: global name 'weird' is not defined'''
760 r['Tags'] = 'boogus pybogus'
761 r.add_package_info(self.test_package)
762 r.add_os_info()
763 r.add_user_info()
764- self.assertEqual(r.standard_title(), 'foo crashed with NameError in fuzz()')
765+ self.assertEqual(r.standard_title(),
766+ "foo crashed with NameError in fuzz(): global name 'weird' is not defined")
767
768 handle = self.crashdb.upload(r)
769- self.assert_(handle)
770- url = self.crashdb.get_comment_url(r, handle)
771- self.assert_(url)
772+ self.assertTrue(handle)
773+ bug_target = self._get_bug_target(self.crashdb, r)
774+ self.assertTrue(bug_target)
775
776- id = self._fill_bug_form(url)
777- self.assert_(id > 0)
778+ id = self._file_bug(bug_target, r, handle)
779+ self.assertTrue(id > 0)
780 global python_report
781 python_report = id
782- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
783+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
784
785 def test_2_download(self):
786 '''download()'''
787@@ -925,16 +989,16 @@
788 apport.packaging.get_system_architecture()]))
789
790 self.assertEqual(r['Signal'], '11')
791- self.assert_(r['ExecutablePath'].endswith('/crash'))
792+ self.assertTrue(r['ExecutablePath'].endswith('/crash'))
793 self.assertEqual(r['SourcePackage'], self.test_srcpackage)
794- self.assert_(r['Package'].startswith(self.test_package + ' '))
795- self.assert_('f (x=42)' in r['Stacktrace'])
796- self.assert_('f (x=42)' in r['StacktraceTop'])
797- self.assert_('f (x=42)' in r['ThreadStacktrace'])
798- self.assert_(len(r['CoreDump']) > 1000)
799- self.assert_('Dependencies' in r)
800- self.assert_('Disassembly' in r)
801- self.assert_('Registers' in r)
802+ self.assertTrue(r['Package'].startswith(self.test_package + ' '))
803+ self.assertTrue('f (x=42)' in r['Stacktrace'])
804+ self.assertTrue('f (x=42)' in r['StacktraceTop'])
805+ self.assertTrue('f (x=42)' in r['ThreadStacktrace'])
806+ self.assertTrue(len(r['CoreDump']) > 1000)
807+ self.assertTrue('Dependencies' in r)
808+ self.assertTrue('Disassembly' in r)
809+ self.assertTrue('Registers' in r)
810
811 # check tags
812 r = self.crashdb.download(python_report)
813@@ -946,12 +1010,12 @@
814 '''update_traces()'''
815
816 r = self.crashdb.download(segv_report)
817- self.assert_('CoreDump' in r)
818- self.assert_('Dependencies' in r)
819- self.assert_('Disassembly' in r)
820- self.assert_('Registers' in r)
821- self.assert_('Stacktrace' in r)
822- self.assert_('ThreadStacktrace' in r)
823+ self.assertTrue('CoreDump' in r)
824+ self.assertTrue('Dependencies' in r)
825+ self.assertTrue('Disassembly' in r)
826+ self.assertTrue('Registers' in r)
827+ self.assertTrue('Stacktrace' in r)
828+ self.assertTrue('ThreadStacktrace' in r)
829
830 # updating with an useless stack trace retains core dump
831 r['StacktraceTop'] = '?? ()'
832@@ -960,16 +1024,16 @@
833 r['FooBar'] = 'bogus'
834 self.crashdb.update_traces(segv_report, r, 'I can has a better retrace?')
835 r = self.crashdb.download(segv_report)
836- self.assert_('CoreDump' in r)
837- self.assert_('Dependencies' in r)
838- self.assert_('Disassembly' in r)
839- self.assert_('Registers' in r)
840- self.assert_('Stacktrace' in r) # TODO: ascertain that it's the updated one
841- self.assert_('ThreadStacktrace' in r)
842+ self.assertTrue('CoreDump' in r)
843+ self.assertTrue('Dependencies' in r)
844+ self.assertTrue('Disassembly' in r)
845+ self.assertTrue('Registers' in r)
846+ self.assertTrue('Stacktrace' in r) # TODO: ascertain that it's the updated one
847+ self.assertTrue('ThreadStacktrace' in r)
848 self.failIf('FooBar' in r)
849
850 tags = self.crashdb.launchpad.bugs[segv_report].tags
851- self.assert_('apport-crash' in tags)
852+ self.assertTrue('apport-crash' in tags)
853 self.failIf('apport-collected' in tags)
854
855 # updating with an useful stack trace removes core dump
856@@ -979,11 +1043,11 @@
857 self.crashdb.update_traces(segv_report, r, 'good retrace!')
858 r = self.crashdb.download(segv_report)
859 self.failIf('CoreDump' in r)
860- self.assert_('Dependencies' in r)
861- self.assert_('Disassembly' in r)
862- self.assert_('Registers' in r)
863- self.assert_('Stacktrace' in r)
864- self.assert_('ThreadStacktrace' in r)
865+ self.assertTrue('Dependencies' in r)
866+ self.assertTrue('Disassembly' in r)
867+ self.assertTrue('Registers' in r)
868+ self.assertTrue('Stacktrace' in r)
869+ self.assertTrue('ThreadStacktrace' in r)
870 self.failIf('FooBar' in r)
871
872 # test various situations which caused crashes
873@@ -995,11 +1059,14 @@
874 def test_update_description(self):
875 '''update() with changing description'''
876
877- id = self._fill_bug_form(
878- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
879- 'field.title=testbug&field.actions.search=' % self.crashdb.distro)
880- self.assert_(id > 0)
881- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
882+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
883+ bug = self.crashdb.launchpad.bugs.createBug(
884+ description='test description for test bug.',
885+ target=bug_target,
886+ title='testbug')
887+ id = bug.id
888+ self.assertTrue(id > 0)
889+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
890
891 r = apport.Report('Bug')
892
893@@ -1024,14 +1091,16 @@
894 def test_update_comment(self):
895 '''update() with appending comment'''
896
897+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
898 # we need to fake an apport description separator here, since we
899 # want to be lazy and use download() for checking the result
900- id = self._fill_bug_form(
901- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
902- 'field.title=testbug&field.actions.search=' %
903- self.crashdb.distro, comment='Pr0blem\n\n--- \nProblemType: Bug')
904- self.assert_(id > 0)
905- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
906+ bug = self.crashdb.launchpad.bugs.createBug(
907+ description='Pr0blem\n\n--- \nProblemType: Bug',
908+ target=bug_target,
909+ title='testbug')
910+ id = bug.id
911+ self.assertTrue(id > 0)
912+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
913
914 r = apport.Report('Bug')
915
916@@ -1057,11 +1126,14 @@
917 def test_update_filter(self):
918 '''update() with a key filter'''
919
920- id = self._fill_bug_form(
921- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
922- 'field.title=testbug&field.actions.search=' % self.crashdb.distro)
923- self.assert_(id > 0)
924- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
925+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
926+ bug = self.crashdb.launchpad.bugs.createBug(
927+ description='test description for test bug',
928+ target=bug_target,
929+ title='testbug')
930+ id = bug.id
931+ self.assertTrue(id > 0)
932+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
933
934 r = apport.Report('Bug')
935
936@@ -1099,13 +1171,13 @@
937 def test_is_reporter(self):
938 '''is_reporter()'''
939
940- self.assert_(self.crashdb.is_reporter(segv_report))
941+ self.assertTrue(self.crashdb.is_reporter(segv_report))
942 self.failIf(self.crashdb.is_reporter(1))
943
944 def test_can_update(self):
945 '''can_update()'''
946
947- self.assert_(self.crashdb.can_update(segv_report))
948+ self.assertTrue(self.crashdb.can_update(segv_report))
949 self.failIf(self.crashdb.can_update(1))
950
951 def test_duplicates(self):
952@@ -1160,7 +1232,7 @@
953
954 # mark_retraced()
955 unretraced_before = self.crashdb.get_unretraced()
956- self.assert_(segv_report in unretraced_before)
957+ self.assertTrue(segv_report in unretraced_before)
958 self.failIf(python_report in unretraced_before)
959 self.crashdb.mark_retraced(segv_report)
960 unretraced_after = self.crashdb.get_unretraced()
961@@ -1194,7 +1266,7 @@
962 '''processing status markings for interpreter crashes'''
963
964 unchecked_before = self.crashdb.get_dup_unchecked()
965- self.assert_(python_report in unchecked_before)
966+ self.assertTrue(python_report in unchecked_before)
967 self.failIf(segv_report in unchecked_before)
968 self.crashdb._mark_dup_checked(python_report, self.ref_report)
969 unchecked_after = self.crashdb.get_dup_unchecked()
970@@ -1211,7 +1283,7 @@
971 invalidated by marking it as a duplicate.
972 '''
973 id = self._file_segv_report()
974- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
975+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
976
977 r = self.crashdb.download(id)
978
979@@ -1235,7 +1307,7 @@
980 self._mark_report_fixed(segv_report)
981 fixed_ver = self.crashdb.get_fixed_version(segv_report)
982 self.assertNotEqual(fixed_ver, None)
983- self.assert_(fixed_ver[0].isdigit())
984+ self.assertTrue(fixed_ver[0].isdigit())
985 self._mark_report_new(segv_report)
986 self.assertEqual(self.crashdb.get_fixed_version(segv_report), None)
987
988@@ -1247,119 +1319,99 @@
989 def _get_instance(klass):
990 '''Create a CrashDB instance'''
991
992- return CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
993- {'distro': 'ubuntu', 'staging': True})
994-
995- def _fill_bug_form(self, url, comment=None):
996- '''Fill form for a distro bug and commit the bug.
997-
998- Return the report ID.
999- '''
1000- cj = cookielib.MozillaCookieJar()
1001- cj.load(os.path.expanduser('~/.lpcookie.txt'))
1002- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
1003- opener.addheaders = [('Referer', url)]
1004-
1005- re_pkg = re.compile('\+source/([\w]+)/')
1006- re_title = re.compile('<input.*id="field.title".*value="([^"]+)"')
1007- re_tags = re.compile('<input.*id="field.tags".*value="([^"]*)"')
1008-
1009- # parse default field values from reporting page
1010- while True:
1011- res = opener.open(url)
1012- try:
1013- self.assertEqual(res.getcode(), 200)
1014- except AttributeError:
1015- pass # getcode() is new in Python 2.6
1016- content = res.read()
1017-
1018- if 'Please wait while bug data is processed' in content:
1019- print '.',
1020- sys.stdout.flush()
1021- time.sleep(5)
1022- continue
1023-
1024- break
1025-
1026- m_pkg = re_pkg.search(url)
1027- m_title = re_title.search(content)
1028- m_tags = re_tags.search(content)
1029-
1030- # strip off GET arguments from URL
1031- url = url.split('?')[0]
1032-
1033- # create request to file bug
1034- args = {
1035- 'packagename_option': 'choose',
1036- 'field.packagename': m_pkg.group(1),
1037- 'field.title': m_title.group(1),
1038- 'field.tags': m_tags.group(1),
1039- 'field.comment': comment or 'ZOMG!',
1040- 'field.actions.submit_bug': '1',
1041- }
1042-
1043- res = opener.open(url, data=urllib.urlencode(args))
1044- try:
1045- self.assertEqual(res.getcode(), 200)
1046- except AttributeError:
1047- pass # getcode() is new in Python 2.6
1048- self.assert_('+source/%s/+bug/' % m_pkg.group(1) in res.geturl())
1049- id = res.geturl().split('/')[-1]
1050- return int(id)
1051-
1052- def _fill_bug_form_project(self, url):
1053- '''Fill form for a project bug and commit the bug.
1054-
1055- Return the report ID.
1056- '''
1057- cj = cookielib.MozillaCookieJar()
1058- cj.load(os.path.expanduser('~/.lpcookie.txt'))
1059- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
1060- opener.addheaders = [('Referer', url)]
1061-
1062- m = re.search('launchpad.net/([^/]+)/\+filebug', url)
1063- assert m
1064- project = m.group(1)
1065-
1066- re_title = re.compile('<input.*id="field.title".*value="([^"]+)"')
1067- re_tags = re.compile('<input.*id="field.tags".*value="([^"]+)"')
1068-
1069- # parse default field values from reporting page
1070- while True:
1071- res = opener.open(url)
1072- try:
1073- self.assertEqual(res.getcode(), 200)
1074- except AttributeError:
1075- pass # getcode() is new in Python 2.6
1076- content = res.read()
1077-
1078- if 'Please wait while bug data is processed' in content:
1079- print '.',
1080- sys.stdout.flush()
1081- time.sleep(5)
1082- continue
1083-
1084- break
1085-
1086- m_title = re_title.search(content)
1087- m_tags = re_tags.search(content)
1088-
1089- # strip off GET arguments from URL
1090- url = url.split('?')[0]
1091-
1092- # create request to file bug
1093- args = {
1094- 'field.title': m_title.group(1),
1095- 'field.tags': m_tags.group(1),
1096- 'field.comment': 'ZOMG!',
1097- 'field.actions.submit_bug': '1',
1098- }
1099-
1100- res = opener.open(url, data=urllib.urlencode(args))
1101- self.assertEqual(res.getcode(), 200)
1102- self.assert_(('launchpad.net/%s/+bug' % project) in res.geturl())
1103- id = res.geturl().split('/')[-1]
1104- return int(id)
1105+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
1106+
1107+ return CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
1108+ {'distro': 'ubuntu',
1109+ 'launchpad_instance': launchpad_instance})
1110+
1111+ @classmethod
1112+ def _get_bug_target(klass, db, report):
1113+ '''Return the bug_target for this report.'''
1114+
1115+ project = db.options.get('project')
1116+ if report.has_key('SourcePackage'):
1117+ return db.lp_distro.getSourcePackage(name=report['SourcePackage'])
1118+ elif project:
1119+ return db.launchpad.projects[project]
1120+ else:
1121+ return self.lp_distro
1122+
1123+ def _get_librarian_hostname(self):
1124+ '''Return the librarian hostname according to the LP hostname used.'''
1125+
1126+ hostname = self.crashdb.get_hostname()
1127+ if 'staging' in hostname:
1128+ return 'staging.launchpadlibrarian.net'
1129+ else:
1130+ return 'launchpad.dev:58080'
1131+
1132+ def _file_bug(self, bug_target, report, handle, comment=None):
1133+ '''File a bug report.'''
1134+
1135+ bug_title = report.get('Title', report.standard_title())
1136+
1137+ blob_info = self.crashdb.launchpad.temporary_blobs.fetch(
1138+ token=handle)
1139+ # XXX 2010-08-03 matsubara bug=612990:
1140+ # Can't fetch the blob directly, so let's load it from the
1141+ # representation.
1142+ blob = self.crashdb.launchpad.load(blob_info['self_link'])
1143+ #XXX Need to find a way to trigger the job that process the blob
1144+ # rather polling like this. This makes the test suite take forever
1145+ # to run.
1146+ while not blob.hasBeenProcessed():
1147+ time.sleep(1)
1148+
1149+ # processed_blob contains info about privacy, additional comments
1150+ # and attachments.
1151+ processed_blob = blob.getProcessedData()
1152+
1153+ bug = self.crashdb.launchpad.bugs.createBug(
1154+ description=processed_blob['extra_description'],
1155+ private=processed_blob['private'],
1156+ tags=processed_blob['initial_tags'],
1157+ target=bug_target,
1158+ title=bug_title)
1159+
1160+ for comment in processed_blob['comments']:
1161+ bug.newMessage(content=comment)
1162+
1163+ # Ideally, one would be able to retrieve the attachment content
1164+ # from the ProblemReport object or from the processed_blob.
1165+ # Unfortunately the processed_blob only give us the Launchpad
1166+ # librarian file_alias_id, so that's why we need to
1167+ # download it again and upload to the bug report. It'd be even
1168+ # better if addAttachment could work like linkAttachment, the LP
1169+ # api used in the +filebug web UI, but there are security concerns
1170+ # about the way linkAttachment works.
1171+ librarian_url = 'http://%s' % self._get_librarian_hostname()
1172+ for attachment in processed_blob['attachments']:
1173+ filename = description = attachment['description']
1174+ # Download the attachment data.
1175+ data = urllib.urlopen(urllib.basejoin(librarian_url,
1176+ str(attachment['file_alias_id']) + '/' + filename)).read()
1177+ # Add the attachment to the newly created bug report.
1178+ bug.addAttachment(
1179+ comment=filename,
1180+ data=data,
1181+ filename=filename,
1182+ description=description)
1183+
1184+ for subscriber in processed_blob['subscribers']:
1185+ sub = self.crashdb.launchpad.people[subscriber]
1186+ if sub:
1187+ bug.subscribe(person=sub)
1188+
1189+ for submission_key in processed_blob['hwdb_submission_keys']:
1190+ # XXX 2010-08-04 matsubara bug=628889:
1191+ # Can't fetch the submission directly, so let's load it
1192+ # from the representation.
1193+ submission = self.crashdb.launchpad.load(
1194+ 'https://api.%s/beta/+hwdb/+submission/%s'
1195+ % (self.crashdb.get_hostname(), submission_key))
1196+ bug.linkHWSubmission(submission=submission)
1197+ return int(bug.id)
1198
1199 def _mark_needs_retrace(self, id):
1200 '''Mark a report ID as needing retrace.'''
1201@@ -1401,15 +1453,17 @@
1202 '''Verify that report ID is marked as regression.'''
1203
1204 bug = self.crashdb.launchpad.bugs[id]
1205- self.assert_('regression-retracer' in bug.tags)
1206+ self.assertTrue('regression-retracer' in bug.tags)
1207
1208 def test_project(self):
1209 '''reporting crashes against a project instead of a distro'''
1210
1211+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
1212 # crash database for langpack-o-matic project (this does not have
1213 # packages in any distro)
1214- crashdb = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
1215- {'project': 'langpack-o-matic', 'staging': True})
1216+ crashdb = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
1217+ {'project': 'langpack-o-matic',
1218+ 'launchpad_instance': launchpad_instance})
1219 self.assertEqual(crashdb.distro, None)
1220
1221 # create Python crash report
1222@@ -1417,21 +1471,22 @@
1223 r['ExecutablePath'] = '/bin/foo'
1224 r['Traceback'] = '''Traceback (most recent call last):
1225 File "/bin/foo", line 67, in fuzz
1226- print weird
1227+ print(weird)
1228 NameError: global name 'weird' is not defined'''
1229 r.add_os_info()
1230 r.add_user_info()
1231- self.assertEqual(r.standard_title(), 'foo crashed with NameError in fuzz()')
1232+ self.assertEqual(r.standard_title(),
1233+ "foo crashed with NameError in fuzz(): global name 'weird' is not defined")
1234
1235 # file it
1236 handle = crashdb.upload(r)
1237- self.assert_(handle)
1238- url = crashdb.get_comment_url(r, handle)
1239- self.assert_('launchpad.net/langpack-o-matic/+filebug' in url)
1240+ self.assertTrue(handle)
1241+ bug_target = self._get_bug_target(crashdb, r)
1242+ self.assertEqual(bug_target.name, 'langpack-o-matic')
1243
1244- id = self._fill_bug_form_project(url)
1245- self.assert_(id > 0)
1246- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
1247+ id = self._file_bug(bug_target, r, handle)
1248+ self.assertTrue(id > 0)
1249+ sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
1250
1251 # update
1252 r = crashdb.download(id)
1253@@ -1454,21 +1509,22 @@
1254 '''download() of uncommon description formats'''
1255
1256 # only ProblemType/Architecture/DistroRelease in description
1257- r = self.crashdb.download(269539)
1258+ r = self.crashdb.download(self.uncommon_description_bug.id)
1259 self.assertEqual(r['ProblemType'], 'Package')
1260 self.assertEqual(r['Architecture'], 'amd64')
1261- self.assert_(r['DistroRelease'].startswith('Ubuntu '))
1262- self.assert_('DpkgTerminalLog' in r)
1263+ self.assertTrue(r['DistroRelease'].startswith('Ubuntu '))
1264
1265 def test_escalation(self):
1266 '''Escalating bugs with more than 10 duplicates'''
1267
1268 assert segv_report, 'you need to run test_1_report_segv() first'
1269
1270- db = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
1271- {'distro': 'ubuntu', 'staging': True,
1272- 'escalation_tag': 'omgkittens',
1273- 'escalation_subscription': 'apport-hackers'})
1274+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
1275+ db = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
1276+ {'distro': 'ubuntu',
1277+ 'launchpad_instance': launchpad_instance,
1278+ 'escalation_tag': 'omgkittens',
1279+ 'escalation_subscription': 'apport-hackers'})
1280
1281 count = 0
1282 p = db.launchpad.people[db.options['escalation_subscription']].self_link
1283@@ -1476,38 +1532,36 @@
1284 try:
1285 for b in range(first_dup, first_dup+13):
1286 count += 1
1287- print b,
1288- sys.stdout.flush()
1289+ sys.stderr.write('%i ' % b)
1290 db.close_duplicate(b, segv_report)
1291 b = db.launchpad.bugs[segv_report]
1292 has_escalation_tag = db.options['escalation_tag'] in b.tags
1293- has_escalation_subsciption = any([s.person_link == p for s in b.subscriptions])
1294+ has_escalation_subscription = any([s.person_link == p for s in b.subscriptions])
1295 if count <= 10:
1296 self.failIf(has_escalation_tag)
1297- self.failIf(has_escalation_subsciption)
1298+ self.failIf(has_escalation_subscription)
1299 else:
1300- self.assert_(has_escalation_tag)
1301- self.assert_(has_escalation_subsciption)
1302+ self.assertTrue(has_escalation_tag)
1303+ self.assertTrue(has_escalation_subscription)
1304 finally:
1305 for b in range(first_dup, first_dup+count):
1306- print 'R%i' % b,
1307- sys.stdout.flush()
1308+ sys.stderr.write('R%i ' % b)
1309 db.close_duplicate(b, None)
1310- print
1311+ sys.stderr.write('\n')
1312
1313 def test_marking_python_task_mangle(self):
1314 '''source package task fixup for marking interpreter crashes'''
1315
1316 self._mark_needs_dupcheck(python_report)
1317 unchecked_before = self.crashdb.get_dup_unchecked()
1318- self.assert_(python_report in unchecked_before)
1319+ self.assertTrue(python_report in unchecked_before)
1320
1321 # add an upstream task, and remove the package name from the
1322 # package task; _mark_dup_checked is supposed to restore the
1323 # package name
1324 b = self.crashdb.launchpad.bugs[python_report]
1325 t = b.bug_tasks[0]
1326- t.target = self.crashdb.launchpad.distributions['ubuntu'].getSourcePackage(name='pmount')
1327+ t.target = self.crashdb.launchpad.distributions['ubuntu'].getSourcePackage(name='bash')
1328 t.status = 'Invalid'
1329 t.lp_save()
1330 b.addTask(target=self.crashdb.launchpad.projects['coreutils'])
1331@@ -1526,12 +1580,12 @@
1332 self.assertEqual(b.bug_tasks[0].status, 'New')
1333
1334 # package-less distro task should have package name fixed
1335- self.assertEqual(b.bug_tasks[1].bug_target_name, 'coreutils (Ubuntu)')
1336- self.assertEqual(b.bug_tasks[1].status, 'New')
1337+ self.assertEqual(b.bug_tasks[2].bug_target_name, 'coreutils (Ubuntu)')
1338+ self.assertEqual(b.bug_tasks[2].status, 'New')
1339
1340- # invalid pmount task should be unmodified
1341- self.assertEqual(b.bug_tasks[2].bug_target_name, 'pmount (Ubuntu)')
1342- self.assertEqual(b.bug_tasks[2].status, 'Invalid')
1343+ # invalid bash task should be unmodified
1344+ self.assertEqual(b.bug_tasks[1].bug_target_name, 'bash (Ubuntu)')
1345+ self.assertEqual(b.bug_tasks[1].status, 'Invalid')
1346
1347 # the invalid task should not confuse get_fixed_version()
1348 self.assertEqual(self.crashdb.get_fixed_version(python_report),
1349
1350=== modified file 'apport/crashdb_impl/memory.py'
1351--- apport/crashdb_impl/memory.py 2010-06-28 14:17:38 +0000
1352+++ apport/crashdb_impl/memory.py 2011-02-14 19:45:28 +0000
1353@@ -639,7 +639,7 @@
1354 if pid == 0:
1355 try:
1356 self.crashes.duplicate_db_consolidate()
1357- except Exception, e:
1358+ except Exception as e:
1359 if 'database is locked' in str(e):
1360 os._exit(42)
1361 else:
1362@@ -648,7 +648,7 @@
1363
1364 try:
1365 self.crashes.duplicate_db_consolidate()
1366- except Exception, e:
1367+ except Exception as e:
1368 if 'database is locked' in str(e):
1369 locked_exceptions += 1
1370 else:
1371@@ -656,7 +656,7 @@
1372
1373 # wait on child, examine status
1374 status = os.wait()[1]
1375- self.assert_(os.WIFEXITED(status))
1376+ self.assertTrue(os.WIFEXITED(status))
1377 status = os.WEXITSTATUS(status)
1378 if status == 42:
1379 locked_exceptions += 1
1380@@ -710,7 +710,7 @@
1381 # neither for a ten second one (check timezone offset errors)
1382 self.failIf(self.crashes.duplicate_db_needs_consolidation(10))
1383 # but for an one second interval
1384- self.assert_(self.crashes.duplicate_db_needs_consolidation(1))
1385+ self.assertTrue(self.crashes.duplicate_db_needs_consolidation(1))
1386
1387 self.crashes.duplicate_db_consolidate()
1388
1389@@ -771,7 +771,7 @@
1390 f.close()
1391
1392 self.crashes = CrashDatabase(None, None, {})
1393- self.assertRaises(SystemError, self.crashes.init_duplicate_db, db)
1394+ self.assertRaises(StandardError, self.crashes.init_duplicate_db, db)
1395
1396 finally:
1397 os.unlink(db)
1398
1399=== modified file 'apport/crashdb_impl/multipartpost_handler.py'
1400--- apport/crashdb_impl/multipartpost_handler.py 2009-04-11 17:11:06 +0000
1401+++ apport/crashdb_impl/multipartpost_handler.py 2011-02-14 19:45:28 +0000
1402@@ -67,7 +67,7 @@
1403 v_vars.append((key, value))
1404 except TypeError:
1405 systype, value, traceback = sys.exc_info()
1406- raise TypeError, "not a valid non-string sequence or mapping object", traceback
1407+ raise TypeError("not a valid non-string sequence or mapping object").with_traceback(traceback)
1408
1409 if len(v_files) == 0:
1410 data = urllib.urlencode(v_vars, doseq)
1411
1412=== modified file 'apport/fileutils.py'
1413--- apport/fileutils.py 2009-12-23 11:01:21 +0000
1414+++ apport/fileutils.py 2011-02-14 19:45:28 +0000
1415@@ -9,10 +9,17 @@
1416 # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
1417 # the full text of the license.
1418
1419-import os, glob, subprocess, os.path, ConfigParser
1420+import os, glob, subprocess, os.path
1421+
1422+try:
1423+ from configparser import ConfigParser, NoOptionError, NoSectionError
1424+except ImportError:
1425+ # Python 2
1426+ from ConfigParser import ConfigParser, NoOptionError, NoSectionError
1427+
1428 from problem_report import ProblemReport
1429
1430-from packaging_impl import impl as packaging
1431+from apport.packaging_impl import impl as packaging
1432
1433 report_dir = os.environ.get('APPORT_REPORT_DIR', '/var/crash')
1434
1435@@ -196,7 +203,7 @@
1436 elif report.has_key('Package'):
1437 subject = report['Package'].split(None, 1)[0]
1438 else:
1439- raise ValueError, 'report has neither ExecutablePath nor Package attribute'
1440+ raise ValueError('report has neither ExecutablePath nor Package attribute')
1441
1442 if not uid:
1443 uid = os.getuid()
1444@@ -234,7 +241,7 @@
1445 interpreted as a boolean.
1446 '''
1447 if not get_config.config:
1448- get_config.config = ConfigParser.ConfigParser()
1449+ get_config.config = ConfigParser()
1450 get_config.config.read(os.path.expanduser(_config_file))
1451
1452 try:
1453@@ -242,7 +249,7 @@
1454 return get_config.config.getboolean(section, setting)
1455 else:
1456 return get_config.config.get(section, setting)
1457- except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
1458+ except (NoOptionError, NoSectionError):
1459 return default
1460
1461 get_config.config = None
1462@@ -252,7 +259,10 @@
1463 #
1464
1465 import unittest, tempfile, os, shutil, sys, time
1466-from cStringIO import StringIO
1467+try:
1468+ from cStringIO import StringIO
1469+except ImportError:
1470+ from io import StringIO
1471
1472 class _T(unittest.TestCase):
1473 def setUp(self):
1474@@ -278,8 +288,8 @@
1475
1476 open(r1, 'w').write('report 1')
1477 open(r2, 'w').write('report 2')
1478- os.chmod(r1, 0600)
1479- os.chmod(r2, 0600)
1480+ os.chmod(r1, 0o600)
1481+ os.chmod(r2, 0o600)
1482 if create_inaccessible:
1483 ri = os.path.join(report_dir, 'inaccessible.crash')
1484 open(ri, 'w').write('inaccessible')
1485@@ -321,8 +331,8 @@
1486 if onedesktop:
1487 d = find_package_desktopfile(onedesktop)
1488 self.assertNotEqual(d, None, 'one-desktop package %s' % onedesktop)
1489- self.assert_(os.path.exists(d))
1490- self.assert_(d.endswith('.desktop'))
1491+ self.assertTrue(os.path.exists(d))
1492+ self.assertTrue(d.endswith('.desktop'))
1493
1494 def test_likely_packaged(self):
1495 '''likely_packaged().'''
1496@@ -443,9 +453,9 @@
1497 self.assertRaises(ValueError, make_report_path, pr)
1498
1499 pr['Package'] = 'bash 1'
1500- self.assert_(make_report_path(pr).startswith('%s/bash' % report_dir))
1501+ self.assertTrue(make_report_path(pr).startswith('%s/bash' % report_dir))
1502 pr['ExecutablePath'] = '/bin/bash';
1503- self.assert_(make_report_path(pr).startswith('%s/_bin_bash' % report_dir))
1504+ self.assertTrue(make_report_path(pr).startswith('%s/_bin_bash' % report_dir))
1505
1506 def test_check_files_md5(self):
1507 '''check_files_md5().'''
1508
1509=== modified file 'apport/hookutils.py'
1510--- apport/hookutils.py 2010-06-24 13:37:14 +0000
1511+++ apport/hookutils.py 2011-02-14 19:45:28 +0000
1512@@ -51,7 +51,7 @@
1513 '''
1514 try:
1515 return open(path).read().strip()
1516- except Exception, e:
1517+ except Exception as e:
1518 return 'Error: ' + str(e)
1519
1520 def attach_file(report, path, key=None):
1521@@ -63,6 +63,9 @@
1522 if not key:
1523 key = path_to_key(path)
1524
1525+ # Do not clobber existing keys
1526+ while report.has_key(key):
1527+ key += "_"
1528 report[key] = read_file(path)
1529
1530 def attach_conffiles(report, package, conffiles=None):
1531@@ -105,10 +108,17 @@
1532 report[key] = '[deleted]'
1533
1534 def attach_dmesg(report):
1535- '''Attach information from the kernel ring buffer (dmesg).'''
1536+ '''Attach information from the kernel ring buffer (dmesg).
1537
1538- report['BootDmesg'] = open('/var/log/dmesg').read()
1539- report['CurrentDmesg'] = command_output(['sh', '-c', 'dmesg | comm -13 --nocheck-order /var/log/dmesg -'])
1540+ This won't overwite already existing information.
1541+ '''
1542+ try:
1543+ if not report.get('BootDmesg', '').strip():
1544+ report['BootDmesg'] = open('/var/log/dmesg').read()
1545+ except IOError:
1546+ pass
1547+ if not report.get('CurrentDmesg', '').strip():
1548+ report['CurrentDmesg'] = command_output(['sh', '-c', 'dmesg | comm -13 --nocheck-order /var/log/dmesg -'])
1549
1550 def attach_dmi(report):
1551 dmi_dir = '/sys/class/dmi/id'
1552@@ -148,7 +158,7 @@
1553
1554 attach_file(report, '/proc/interrupts', 'ProcInterrupts')
1555 attach_file(report, '/proc/cpuinfo', 'ProcCpuinfo')
1556- attach_file(report, '/proc/cmdline', 'ProcCmdLine')
1557+ attach_file(report, '/proc/cmdline', 'ProcKernelCmdLine')
1558 attach_file(report, '/proc/modules', 'ProcModules')
1559 attach_file(report, '/var/log/udev', 'UdevLog')
1560
1561@@ -202,10 +212,11 @@
1562 report['PciMultimedia'] = pci_devices(PCI_MULTIMEDIA)
1563
1564 cards = []
1565- for line in open('/proc/asound/cards'):
1566- if ']:' in line:
1567- fields = line.lstrip().split()
1568- cards.append(int(fields[0]))
1569+ if os.path.exists('/proc/asound/cards'):
1570+ for line in open('/proc/asound/cards'):
1571+ if ']:' in line:
1572+ fields = line.lstrip().split()
1573+ cards.append(int(fields[0]))
1574
1575 for card in cards:
1576 key = 'Card%d.Amixer.info' % card
1577@@ -260,7 +271,7 @@
1578 try:
1579 sp = subprocess.Popen(command, stdout=subprocess.PIPE,
1580 stderr=stderr, close_fds=True, env=env)
1581- except OSError, e:
1582+ except OSError as e:
1583 return 'Error: ' + str(e)
1584
1585 out = sp.communicate(input)[0]
1586@@ -303,9 +314,12 @@
1587 pattern should be a "re" object.
1588 '''
1589 lines = ''
1590- for line in open('/var/log/syslog'):
1591- if pattern.search(line):
1592- lines += line
1593+ try:
1594+ for line in open('/var/log/syslog'):
1595+ if pattern.search(line):
1596+ lines += line
1597+ except IOError:
1598+ return []
1599 return lines
1600
1601 def xsession_errors(pattern):
1602@@ -485,20 +499,26 @@
1603
1604 Arguments may be package names or globs, e. g. "foo*"
1605 '''
1606- versions = ''
1607+ versions = []
1608 for package_pattern in packages:
1609 if not package_pattern:
1610 continue
1611- for package in packaging.package_name_glob(package_pattern):
1612+
1613+ packages = packaging.package_name_glob(package_pattern)
1614+
1615+ if not packages:
1616+ versions.append((package_pattern, 'N/A'))
1617+
1618+ for package in packages:
1619 try:
1620 version = packaging.get_version(package)
1621 except ValueError:
1622 version = 'N/A'
1623 if version is None:
1624 version = 'N/A'
1625- versions += '%s %s\n' % (package, version)
1626+ versions.append((package,version))
1627
1628- return versions
1629+ return '\n'.join(['%s %s' % v for v in versions])
1630
1631 def shared_libraries(path):
1632 '''Returns a list of strings containing the sonames of shared libraries
1633@@ -661,10 +681,10 @@
1634 # - fake BAD module
1635
1636 # direct license check
1637- self.assert_('GPL' in _get_module_license('isofs'))
1638+ self.assertTrue('GPL' in _get_module_license('isofs'))
1639 self.assertEqual(_get_module_license('does-not-exist'), 'invalid')
1640- self.assert_('GPL' in _get_module_license(good_ko.name))
1641- self.assert_('BAD' in _get_module_license(bad_ko.name))
1642+ self.assertTrue('GPL' in _get_module_license(good_ko.name))
1643+ self.assertTrue('BAD' in _get_module_license(bad_ko.name))
1644
1645 # check via nonfree_kernel_modules logic
1646 f = tempfile.NamedTemporaryFile()
1647@@ -673,8 +693,34 @@
1648 f.flush()
1649 nonfree = nonfree_kernel_modules(f.name)
1650 self.failIf('isofs' in nonfree)
1651- self.assert_('does-not-exist' in nonfree)
1652+ self.assertTrue('does-not-exist' in nonfree)
1653 self.failIf(good_ko.name in nonfree)
1654- self.assert_(bad_ko.name in nonfree)
1655+ self.assertTrue(bad_ko.name in nonfree)
1656+
1657+ def test_attach_dmesg(self):
1658+ '''attach_dmesg() does not overwrite already existing data'''
1659+
1660+ report = {}
1661+
1662+ attach_dmesg(report)
1663+ self.assertTrue(report['BootDmesg'].startswith('['))
1664+ self.assertTrue(len(report['BootDmesg']) > 500)
1665+ self.assertTrue(report['CurrentDmesg'].startswith('['))
1666+
1667+ def test_dmesg_overwrite(self):
1668+ '''attach_dmesg() does not overwrite already existing data'''
1669+
1670+ report = {'BootDmesg': 'existingboot'}
1671+
1672+ attach_dmesg(report)
1673+ self.assertEqual(report['BootDmesg'][:50], 'existingboot')
1674+ self.assertTrue(report['CurrentDmesg'].startswith('['))
1675+
1676+ report = {'BootDmesg': 'existingboot', 'CurrentDmesg': 'existingcurrent' }
1677+
1678+ attach_dmesg(report)
1679+ self.assertEqual(report['BootDmesg'], 'existingboot')
1680+ self.assertEqual(report['CurrentDmesg'], 'existingcurrent')
1681+
1682
1683 unittest.main()
1684
1685=== modified file 'apport/packaging.py'
1686--- apport/packaging.py 2009-12-23 10:55:52 +0000
1687+++ apport/packaging.py 2011-02-14 19:45:28 +0000
1688@@ -15,26 +15,26 @@
1689
1690 Throw ValueError if package does not exist.
1691 '''
1692- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1693+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1694
1695 def get_available_version(self, package):
1696 '''Return the latest available version of a package.
1697
1698 Throw ValueError if package does not exist.
1699 '''
1700- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1701+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1702
1703 def get_dependencies(self, package):
1704 '''Return a list of packages a package depends on.'''
1705
1706- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1707+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1708
1709 def get_source(self, package):
1710 '''Return the source package name for a package.
1711
1712 Throw ValueError if package does not exist.
1713 '''
1714- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1715+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1716
1717 def is_distro_package(self, package):
1718 '''Check package origin.
1719@@ -44,7 +44,7 @@
1720
1721 Throw ValueError if package does not exist.
1722 '''
1723- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1724+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1725
1726 def get_architecture(self, package):
1727 '''Return the architecture of a package.
1728@@ -52,19 +52,19 @@
1729 This might differ on multiarch architectures (e. g. an i386 Firefox
1730 package on a x86_64 system)
1731 '''
1732- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1733+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1734
1735 def get_files(self, package):
1736 '''Return list of files shipped by a package.
1737
1738 Throw ValueError if package does not exist.
1739 '''
1740- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1741+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1742
1743 def get_modified_files(self, package):
1744 '''Return list of all modified files of a package.'''
1745
1746- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1747+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1748
1749 def get_file_package(self, file, uninstalled=False, map_cachedir=None):
1750 '''Return the package a file belongs to.
1751@@ -77,14 +77,14 @@
1752 an existing directory which will be used to permanently store the
1753 downloaded maps. If it is not set, a temporary directory will be used.
1754 '''
1755- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1756+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1757
1758 def get_system_architecture(self):
1759 '''Return the architecture of the system.
1760
1761 This should use the notation of the particular distribution.
1762 '''
1763- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1764+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1765
1766 def set_mirror(self, url):
1767 '''Explicitly set a distribution mirror URL.
1768@@ -95,7 +95,7 @@
1769 By default, the mirror will be read from the system configuration
1770 files.
1771 '''
1772- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1773+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1774
1775 def get_source_tree(self, srcpackage, dir, version=None):
1776 '''Download a source package and unpack it into dir..
1777@@ -112,14 +112,14 @@
1778 (which might be a subdirectory of dir). Return None if the source is
1779 not available.
1780 '''
1781- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1782+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1783
1784 def compare_versions(self, ver1, ver2):
1785 '''Compare two package versions.
1786
1787 Return -1 for ver < ver2, 0 for ver1 == ver2, and 1 for ver1 > ver2.
1788 '''
1789- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1790+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1791
1792 def enabled(self):
1793 '''Return whether Apport should generate crash reports.
1794@@ -132,14 +132,14 @@
1795 Implementations should parse the configuration file which controls
1796 Apport (such as /etc/default/apport in Debian/Ubuntu).
1797 '''
1798- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1799+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1800
1801 def get_kernel_package(self):
1802 '''Return the actual Linux kernel package name.
1803
1804 This is used when the user reports a bug against the "linux" package.
1805 '''
1806- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1807+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1808
1809 def install_retracing_packages(self, report, verbosity=0,
1810 unpack_only=False, no_pkg=False, extra_packages=[]):
1811@@ -157,7 +157,7 @@
1812
1813 Return a tuple (list of installed packages, string with outdated packages).
1814 '''
1815- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1816+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1817
1818 def remove_packages(self, packages, verbosity=0):
1819 '''Remove packages.
1820@@ -165,11 +165,11 @@
1821 This is called after install_retracing_packages() to clean up again
1822 afterwards. packages is a list of package names.
1823 '''
1824- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1825+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1826
1827 def package_name_glob(self, glob):
1828 '''Return known package names which match given glob.'''
1829
1830- raise NotImplementedError, 'this method must be implemented by a concrete subclass'
1831+ raise NotImplementedError('this method must be implemented by a concrete subclass')
1832
1833-import packaging_impl
1834+import apport.packaging_impl
1835
1836=== modified file 'apport/report.py'
1837--- apport/report.py 2010-06-16 13:50:47 +0000
1838+++ apport/report.py 2011-02-14 19:45:28 +0000
1839@@ -16,8 +16,9 @@
1840 from xml.parsers.expat import ExpatError
1841
1842 from problem_report import ProblemReport
1843-import fileutils
1844-from packaging_impl import impl as packaging
1845+import apport
1846+import apport.fileutils
1847+from apport.packaging_impl import impl as packaging
1848
1849 _data_dir = os.environ.get('APPORT_DATA_DIR','/usr/share/apport')
1850 _hook_dir = '%s/package-hooks/' % (_data_dir)
1851@@ -56,7 +57,7 @@
1852 '''
1853 try:
1854 return open(path).read().strip()
1855- except (OSError, IOError), e:
1856+ except (OSError, IOError) as e:
1857 return 'Error: ' + str(e)
1858
1859 def _read_maps(pid):
1860@@ -67,8 +68,8 @@
1861 '''
1862 maps = 'Error: unable to read /proc maps file'
1863 try:
1864- maps = file('/proc/%d/maps' % pid).read().strip()
1865- except (OSError,IOError), e:
1866+ maps = open('/proc/%d/maps' % pid).read().strip()
1867+ except (OSError,IOError) as e:
1868 return 'Error: ' + str(e)
1869 return maps
1870
1871@@ -84,8 +85,8 @@
1872 if sp.returncode == 0:
1873 return out
1874 else:
1875- raise OSError, 'Error: command %s failed with exit code %i: %s' % (
1876- str(command), sp.returncode, err)
1877+ raise OSError('Error: command %s failed with exit code %i: %s' % (
1878+ str(command), sp.returncode, err))
1879
1880 def _check_bug_pattern(report, pattern):
1881 '''Check if given report matches the given bug pattern XML DOM node.
1882@@ -177,7 +178,7 @@
1883 self['ProblemType'] == 'KernelCrash'):
1884 package = self['Package']
1885 else:
1886- package = fileutils.find_file_package(self['ExecutablePath'])
1887+ package = apport.fileutils.find_file_package(self['ExecutablePath'])
1888 if not package:
1889 return
1890
1891@@ -333,9 +334,9 @@
1892 self['ProcMaps'] = _read_maps(int(pid))
1893 try:
1894 self['ExecutablePath'] = os.readlink('/proc/' + pid + '/exe')
1895- except OSError, e:
1896+ except OSError as e:
1897 if e.errno == errno.ENOENT:
1898- raise ValueError, 'invalid process'
1899+ raise ValueError('invalid process')
1900 else:
1901 raise
1902 for p in ('rofs', 'rwfs', 'squashmnt', 'persistmnt'):
1903@@ -599,7 +600,7 @@
1904 except StopIteration:
1905 return True
1906 except:
1907- print >> sys.stderr, 'hook %s crashed:' % hook
1908+ apport.error('hook %s crashed:', hook)
1909 traceback.print_exc()
1910 pass
1911
1912@@ -620,7 +621,7 @@
1913 except StopIteration:
1914 return True
1915 except:
1916- print >> sys.stderr, 'hook %s crashed:' % hook
1917+ apport.error('hook %s crashed:', hook)
1918 traceback.print_exc()
1919 pass
1920
1921@@ -641,7 +642,7 @@
1922 except StopIteration:
1923 return True
1924 except:
1925- print >> sys.stderr, 'hook %s crashed:' % hook
1926+ apport.error('hook %s crashed:', hook)
1927 traceback.print_exc()
1928 pass
1929
1930@@ -714,8 +715,8 @@
1931 else:
1932 try:
1933 dom = xml.dom.minidom.parse(ifpath)
1934- except ExpatError, e:
1935- raise ValueError, '%s has invalid format: %s' % (_ignore_file, str(e))
1936+ except ExpatError as e:
1937+ raise ValueError('%s has invalid format: %s' % (_ignore_file, str(e)))
1938
1939 # remove whitespace so that writing back the XML does not accumulate
1940 # whitespace
1941@@ -880,22 +881,46 @@
1942 os.path.basename(self['ExecutablePath']),
1943 trace[0])
1944
1945- trace_re = re.compile('^\s*File.* in (.+)$')
1946+ trace_re = re.compile('^\s*File\s*"(\S+)".* in (.+)$')
1947 i = len(trace)-1
1948 function = 'unknown'
1949 while i >= 0:
1950 m = trace_re.match(trace[i])
1951 if m:
1952- function = m.group(1)
1953+ module_path = m.group(1)
1954+ function = m.group(2)
1955 break
1956 i -= 1
1957
1958- return '%s crashed with %s in %s()' % (
1959- os.path.basename(self['ExecutablePath']),
1960- trace[-1].split(':')[0],
1961- function
1962+ path = os.path.basename(self['ExecutablePath'])
1963+ last_line = trace[-1]
1964+ exception = last_line.split(':')[0]
1965+ m = re.match('^%s: (.+)$' % exception, last_line)
1966+ if m:
1967+ message = m.group(1)
1968+ else:
1969+ message = None
1970+
1971+ if function == '<module>':
1972+ if module_path == self['ExecutablePath']:
1973+ context = '__main__'
1974+ else:
1975+ # Maybe use os.path.basename?
1976+ context = module_path
1977+ else:
1978+ context = '%s()' % function
1979+
1980+ title = '%s crashed with %s in %s' % (
1981+ path,
1982+ exception,
1983+ context
1984 )
1985
1986+ if message:
1987+ title += ': %s' % message
1988+
1989+ return title
1990+
1991 # package problem
1992 if self.get('ProblemType') == 'Package' and \
1993 self.has_key('Package'):
1994@@ -1067,7 +1092,10 @@
1995 #
1996
1997 import unittest, shutil, signal, time
1998-from cStringIO import StringIO
1999+try:
2000+ from cStringIO import StringIO
2001+except ImportError:
2002+ from io import StringIO
2003
2004 class _T(unittest.TestCase):
2005 def test_add_package_info(self):
2006@@ -1082,7 +1110,7 @@
2007 pr.add_package_info('bash')
2008 self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
2009 self.assertEqual(pr['SourcePackage'], 'bash')
2010- self.assert_('libc' in pr['Dependencies'])
2011+ self.assertTrue('libc' in pr['Dependencies'])
2012
2013 # test without specifying a package, but with ExecutablePath
2014 pr = Report()
2015@@ -1091,36 +1119,36 @@
2016 pr.add_package_info()
2017 self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
2018 self.assertEqual(pr['SourcePackage'], 'bash')
2019- self.assert_('libc' in pr['Dependencies'])
2020+ self.assertTrue('libc' in pr['Dependencies'])
2021 # check for stray empty lines
2022- self.assert_('\n\n' not in pr['Dependencies'])
2023- self.assert_(pr.has_key('PackageArchitecture'))
2024+ self.assertTrue('\n\n' not in pr['Dependencies'])
2025+ self.assertTrue(pr.has_key('PackageArchitecture'))
2026
2027 pr = Report()
2028 pr['ExecutablePath'] = '/nonexisting'
2029 pr.add_package_info()
2030- self.assert_(not pr.has_key('Package'))
2031+ self.assertTrue(not pr.has_key('Package'))
2032
2033 def test_add_os_info(self):
2034 '''add_os_info().'''
2035
2036 pr = Report()
2037 pr.add_os_info()
2038- self.assert_(pr['Uname'].startswith('Linux'))
2039- self.assert_(type(pr['DistroRelease']) == type(''))
2040- self.assert_(pr['Architecture'])
2041+ self.assertTrue(pr['Uname'].startswith('Linux'))
2042+ self.assertTrue(type(pr['DistroRelease']) == type(''))
2043+ self.assertTrue(pr['Architecture'])
2044
2045 def test_add_user_info(self):
2046 '''add_user_info().'''
2047
2048 pr = Report()
2049 pr.add_user_info()
2050- self.assert_(pr.has_key('UserGroups'))
2051+ self.assertTrue(pr.has_key('UserGroups'))
2052
2053 # double-check that user group names are removed
2054 for g in pr['UserGroups'].split():
2055- self.assert_(grp.getgrnam(g).gr_gid < 1000)
2056- self.assert_(grp.getgrgid(os.getgid()).gr_name not in pr['UserGroups'])
2057+ self.assertTrue(grp.getgrnam(g).gr_gid < 1000)
2058+ self.assertTrue(grp.getgrgid(os.getgid()).gr_name not in pr['UserGroups'])
2059
2060 def test_add_proc_info(self):
2061 '''add_proc_info().'''
2062@@ -1135,26 +1163,26 @@
2063 self.assertEqual(pr.pid, None)
2064 pr.add_proc_info()
2065 self.assertEqual(pr.pid, os.getpid())
2066- self.assert_(set(['ProcEnviron', 'ProcMaps', 'ProcCmdline',
2067+ self.assertTrue(set(['ProcEnviron', 'ProcMaps', 'ProcCmdline',
2068 'ProcMaps']).issubset(set(pr.keys())), 'report has required fields')
2069- self.assert_('LANG='+os.environ['LANG'] in pr['ProcEnviron'])
2070- self.assert_('USER' not in pr['ProcEnviron'])
2071- self.assert_('PWD' not in pr['ProcEnviron'])
2072+ self.assertTrue('LANG='+os.environ['LANG'] in pr['ProcEnviron'])
2073+ self.assertTrue('USER' not in pr['ProcEnviron'])
2074+ self.assertTrue('PWD' not in pr['ProcEnviron'])
2075
2076 # check with one additional safe environment variable
2077 pr = Report()
2078 pr.add_proc_info(extraenv=['PWD'])
2079- self.assert_('USER' not in pr['ProcEnviron'])
2080- self.assert_('PWD='+os.environ['PWD'] in pr['ProcEnviron'])
2081+ self.assertTrue('USER' not in pr['ProcEnviron'])
2082+ self.assertTrue('PWD='+os.environ['PWD'] in pr['ProcEnviron'])
2083
2084 # check process from other user
2085 assert os.getuid() != 0, 'please do not run this test as root for this check.'
2086 pr = Report()
2087 self.assertRaises(OSError, pr.add_proc_info, 1) # EPERM for init process
2088 self.assertEqual(pr.pid, 1)
2089- self.assert_('init' in pr['ProcStatus'], pr['ProcStatus'])
2090- self.assert_(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
2091- self.assert_(not pr.has_key('InterpreterPath'))
2092+ self.assertTrue('init' in pr['ProcStatus'], pr['ProcStatus'])
2093+ self.assertTrue(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
2094+ self.assertTrue(not pr.has_key('InterpreterPath'))
2095
2096 # check escaping of ProcCmdline
2097 p = subprocess.Popen(['cat', '/foo bar', '\\h', '\\ \\', '-'],
2098@@ -1170,7 +1198,7 @@
2099 p.communicate('\n')
2100 self.assertEqual(pr['ProcCmdline'], 'cat /foo\ bar \\\\h \\\\\\ \\\\ -')
2101 self.assertEqual(pr['ExecutablePath'], '/bin/cat')
2102- self.assert_(not pr.has_key('InterpreterPath'))
2103+ self.assertTrue(not pr.has_key('InterpreterPath'))
2104 self.assertTrue('/bin/cat' in pr['ProcMaps'])
2105 self.assertTrue('[stack]' in pr['ProcMaps'])
2106
2107@@ -1199,7 +1227,7 @@
2108 pr = Report()
2109 pr.add_proc_info(pid=p.pid)
2110 p.communicate('\n')
2111- self.assert_(pr['ExecutablePath'].endswith('bin/zgrep'))
2112+ self.assertTrue(pr['ExecutablePath'].endswith('bin/zgrep'))
2113 self.assertEqual(pr['InterpreterPath'],
2114 os.path.realpath(open(pr['ExecutablePath']).readline().strip()[2:]))
2115 self.assertTrue('[stack]' in pr['ProcMaps'])
2116@@ -1211,7 +1239,7 @@
2117 sys.stdin.readline()
2118 ''')
2119 os.close(fd)
2120- os.chmod(testscript, 0755)
2121+ os.chmod(testscript, 0o755)
2122 p = subprocess.Popen([testscript], stdin=subprocess.PIPE,
2123 stderr=subprocess.PIPE, close_fds=True)
2124 assert p.pid
2125@@ -1223,7 +1251,7 @@
2126 p.communicate('\n')
2127 os.unlink(testscript)
2128 self.assertEqual(pr['ExecutablePath'], testscript)
2129- self.assert_('python' in pr['InterpreterPath'])
2130+ self.assertTrue('python' in pr['InterpreterPath'])
2131 self.assertTrue('python' in pr['ProcMaps'])
2132 self.assertTrue('[stack]' in pr['ProcMaps'])
2133
2134@@ -1250,7 +1278,7 @@
2135 r = Report()
2136 r.add_proc_environ(pid=p.pid)
2137 p.communicate('')
2138- self.assert_('PATH=(custom, no user)' in r['ProcEnviron'],
2139+ self.assertTrue('PATH=(custom, no user)' in r['ProcEnviron'],
2140 'PATH is customized without user paths')
2141
2142 # user paths
2143@@ -1260,7 +1288,7 @@
2144 r = Report()
2145 r.add_proc_environ(pid=p.pid)
2146 p.communicate('')
2147- self.assert_('PATH=(custom, user)' in r['ProcEnviron'],
2148+ self.assertTrue('PATH=(custom, user)' in r['ProcEnviron'],
2149 'PATH is customized with user paths')
2150
2151 def test_check_interpreted(self):
2152@@ -1435,21 +1463,21 @@
2153 return pr
2154
2155 def _validate_gdb_fields(self,pr):
2156- self.assert_(pr.has_key('Stacktrace'))
2157- self.assert_(pr.has_key('ThreadStacktrace'))
2158- self.assert_(pr.has_key('StacktraceTop'))
2159- self.assert_(pr.has_key('Registers'))
2160- self.assert_(pr.has_key('Disassembly'))
2161- self.assert_('(no debugging symbols found)' not in pr['Stacktrace'])
2162- self.assert_('Core was generated by' not in pr['Stacktrace'], pr['Stacktrace'])
2163- self.assert_(not re.match(r'(?s)(^|.*\n)#0 [^\n]+\n#0 ',
2164+ self.assertTrue(pr.has_key('Stacktrace'))
2165+ self.assertTrue(pr.has_key('ThreadStacktrace'))
2166+ self.assertTrue(pr.has_key('StacktraceTop'))
2167+ self.assertTrue(pr.has_key('Registers'))
2168+ self.assertTrue(pr.has_key('Disassembly'))
2169+ self.assertTrue('(no debugging symbols found)' not in pr['Stacktrace'])
2170+ self.assertTrue('Core was generated by' not in pr['Stacktrace'], pr['Stacktrace'])
2171+ self.assertTrue(not re.match(r'(?s)(^|.*\n)#0 [^\n]+\n#0 ',
2172 pr['Stacktrace']))
2173- self.assert_('#0 0x' in pr['Stacktrace'])
2174- self.assert_('#1 0x' in pr['Stacktrace'])
2175- self.assert_('#0 0x' in pr['ThreadStacktrace'])
2176- self.assert_('#1 0x' in pr['ThreadStacktrace'])
2177- self.assert_('Thread 1 (' in pr['ThreadStacktrace'])
2178- self.assert_(len(pr['StacktraceTop'].splitlines()) <= 5)
2179+ self.assertTrue('#0 0x' in pr['Stacktrace'])
2180+ self.assertTrue('#1 0x' in pr['Stacktrace'])
2181+ self.assertTrue('#0 0x' in pr['ThreadStacktrace'])
2182+ self.assertTrue('#1 0x' in pr['ThreadStacktrace'])
2183+ self.assertTrue('Thread 1 (' in pr['ThreadStacktrace'])
2184+ self.assertTrue(len(pr['StacktraceTop'].splitlines()) <= 5)
2185
2186 def test_add_gdb_info(self):
2187 '''add_gdb_info() with core dump file reference.'''
2188@@ -1473,7 +1501,7 @@
2189 }
2190 ''')
2191 self._validate_gdb_fields(pr)
2192- self.assert_('Cannot access memory at address 0x0' in pr['Disassembly'], pr['Disassembly'])
2193+ self.assertTrue('Cannot access memory at address 0x0' in pr['Disassembly'], pr['Disassembly'])
2194 self.failIf ('AssertionMessage' in pr)
2195
2196 def test_add_gdb_info_load(self):
2197@@ -1499,22 +1527,22 @@
2198 pr.load(open(rep.name))
2199 pr['Signal'] = '1'
2200 pr.add_hooks_info('fake_ui')
2201- self.assert_('SegvAnalysis' not in pr.keys())
2202+ self.assertTrue('SegvAnalysis' not in pr.keys())
2203
2204 pr = Report()
2205 pr.load(open(rep.name))
2206 pr.add_hooks_info('fake_ui')
2207- self.assert_('Skipped: missing required field "Architecture"' in pr['SegvAnalysis'],
2208+ self.assertTrue('Skipped: missing required field "Architecture"' in pr['SegvAnalysis'],
2209 pr['SegvAnalysis'])
2210
2211 pr.add_os_info()
2212 pr.add_hooks_info('fake_ui')
2213- self.assert_('Skipped: missing required field "ProcMaps"' in pr['SegvAnalysis'],
2214+ self.assertTrue('Skipped: missing required field "ProcMaps"' in pr['SegvAnalysis'],
2215 pr['SegvAnalysis'])
2216
2217 pr.add_proc_info()
2218 pr.add_hooks_info('fake_ui')
2219- self.assert_('not located in a known VMA region' in pr['SegvAnalysis'],
2220+ self.assertTrue('not located in a known VMA region' in pr['SegvAnalysis'],
2221 pr['SegvAnalysis'])
2222
2223 def test_add_gdb_info_script(self):
2224@@ -1532,7 +1560,7 @@
2225 ulimit -c unlimited
2226 kill -SEGV $$
2227 ''')
2228- os.chmod(script, 0755)
2229+ os.chmod(script, 0o755)
2230
2231 # call script and verify that it gives us a proper ELF core dump
2232 assert subprocess.call([script]) != 0
2233@@ -1549,7 +1577,7 @@
2234 os.unlink(script)
2235
2236 self._validate_gdb_fields(pr)
2237- self.assert_('libc.so' in pr['Stacktrace'] or 'in execute_command' in pr['Stacktrace'])
2238+ self.assertTrue('libc.so' in pr['Stacktrace'] or 'in execute_command' in pr['Stacktrace'])
2239
2240 def test_add_gdb_info_abort(self):
2241 '''add_gdb_info() with SIGABRT/assert()
2242@@ -1572,7 +1600,7 @@
2243 ulimit -c unlimited
2244 $0.bin 2>/dev/null
2245 ''')
2246- os.chmod(script, 0755)
2247+ os.chmod(script, 0o755)
2248
2249 # call script and verify that it gives us a proper ELF core dump
2250 assert subprocess.call([script]) != 0
2251@@ -1589,7 +1617,7 @@
2252 os.unlink('core')
2253
2254 self._validate_gdb_fields(pr)
2255- self.assert_("<stdin>:2: main: Assertion `1 < 0' failed." in
2256+ self.assertTrue("<stdin>:2: main: Assertion `1 < 0' failed." in
2257 pr['AssertionMessage'], pr['AssertionMessage'])
2258 self.failIf(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
2259 self.failIf('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
2260@@ -1614,7 +1642,7 @@
2261 ulimit -c unlimited
2262 LIBC_FATAL_STDERR_=1 $0.bin aaaaaaaaaaaaaaaa 2>/dev/null
2263 ''')
2264- os.chmod(script, 0755)
2265+ os.chmod(script, 0o755)
2266
2267 # call script and verify that it gives us a proper ELF core dump
2268 assert subprocess.call([script]) != 0
2269@@ -1631,7 +1659,7 @@
2270 os.unlink('core')
2271
2272 self._validate_gdb_fields(pr)
2273- self.assert_("** buffer overflow detected ***: %s.bin terminated" % (script) in
2274+ self.assertTrue("** buffer overflow detected ***: %s.bin terminated" % (script) in
2275 pr['AssertionMessage'], pr['AssertionMessage'])
2276 self.failIf(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
2277 self.failIf('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
2278@@ -1652,7 +1680,7 @@
2279 ulimit -c unlimited
2280 $0.bin 2>/dev/null
2281 ''')
2282- os.chmod(script, 0755)
2283+ os.chmod(script, 0o755)
2284
2285 # call script and verify that it gives us a proper ELF core dump
2286 assert subprocess.call([script]) != 0
2287@@ -1996,13 +2024,13 @@
2288 self.failIf(r.has_useful_stacktrace())
2289
2290 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
2291- self.assert_(r.has_useful_stacktrace())
2292+ self.assertTrue(r.has_useful_stacktrace())
2293
2294 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()'
2295- self.assert_(r.has_useful_stacktrace())
2296+ self.assertTrue(r.has_useful_stacktrace())
2297
2298 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
2299- self.assert_(r.has_useful_stacktrace())
2300+ self.assertTrue(r.has_useful_stacktrace())
2301
2302 r['StacktraceTop'] = 'read () from /lib/libc.6.so\n?? ()\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
2303 self.failIf(r.has_useful_stacktrace())
2304@@ -2066,7 +2094,7 @@
2305 subprocess.call(['pgrep', '-x',
2306 NameError: global name 'subprocess' is not defined'''
2307 self.assertEqual(report.standard_title(),
2308- 'apport-gtk crashed with NameError in ui_present_crash()')
2309+ "apport-gtk crashed with NameError in ui_present_crash(): global name 'subprocess' is not defined")
2310
2311 # slightly weird Python crash
2312 report = Report()
2313@@ -2097,8 +2125,32 @@
2314 Restarting AWN usually solves this issue'''
2315
2316 t = report.standard_title()
2317- self.assert_(t.startswith('apport-gtk crashed with'))
2318- self.assert_(t.endswith('setup_chooser()'))
2319+ self.assertTrue(t.startswith('apport-gtk crashed with'))
2320+ self.assertTrue(t.endswith('setup_chooser()'))
2321+
2322+ # Python crash at top level in module
2323+ report = Report()
2324+ report['ExecutablePath'] = '/usr/bin/gnome-about'
2325+ report['Traceback'] = '''Traceback (most recent call last):
2326+ File "/usr/bin/gnome-about", line 30, in <module>
2327+ import pygtk
2328+ File "/usr/lib/pymodules/python2.6/pygtk.py", line 28, in <module>
2329+ import nonexistent
2330+ImportError: No module named nonexistent
2331+'''
2332+ self.assertEqual(report.standard_title(),
2333+ "gnome-about crashed with ImportError in /usr/lib/pymodules/python2.6/pygtk.py: No module named nonexistent")
2334+
2335+ # Python crash at top level in main program
2336+ report = Report()
2337+ report['ExecutablePath'] = '/usr/bin/dcut'
2338+ report['Traceback'] = '''Traceback (most recent call last):
2339+ File "/usr/bin/dcut", line 28, in <module>
2340+ import nonexistent
2341+ImportError: No module named nonexistent
2342+'''
2343+ self.assertEqual(report.standard_title(),
2344+ "dcut crashed with ImportError in __main__: No module named nonexistent")
2345
2346 # package install problem
2347 report = Report('Package')
2348@@ -2318,7 +2370,7 @@
2349 del r['Signal']
2350 r['Traceback'] = '''Traceback (most recent call last):
2351 File "test.py", line 7, in <module>
2352- print _f(5)
2353+ print(_f(5))
2354 File "test.py", line 5, in _f
2355 return g_foo00(x+1)
2356 File "test.py", line 2, in g_foo00
2357
2358=== modified file 'apport/ui.py'
2359--- apport/ui.py 2010-07-09 11:18:39 +0000
2360+++ apport/ui.py 2011-02-14 19:45:28 +0000
2361@@ -13,7 +13,7 @@
2362 # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
2363 # the full text of the license.
2364
2365-__version__ = '1.14.1'
2366+__version__ = '1.17.2'
2367
2368 import glob, sys, os.path, optparse, time, traceback, locale, gettext, re
2369 import pwd, errno, urllib, zlib
2370@@ -24,7 +24,8 @@
2371 from apport.crashdb import get_crashdb, NeedsCredentials
2372 from apport import unicode_gettext as _
2373
2374-symptom_script_dir = '/usr/share/apport/symptoms'
2375+symptom_script_dir = os.environ.get('APPORT_SYMPTOMS_DIR',
2376+ '/usr/share/apport/symptoms')
2377
2378 def excstr(exception):
2379 '''Return exception message as unicode.'''
2380@@ -53,13 +54,13 @@
2381 execfile(symptom_script, symb)
2382 package = symb['run'](report, ui)
2383 if not package:
2384- print >> sys.stderr, 'symptom script %s did not determine the affected package' % symptom_script
2385+ apport.error('symptom script %s did not determine the affected package', symptom_script)
2386 return
2387 report['Symptom'] = os.path.splitext(os.path.basename(symptom_script))[0]
2388 except StopIteration:
2389 sys.exit(0)
2390 except:
2391- print >> sys.stderr, 'symptom script %s crashed:' % symptom_script
2392+ apport.error('symptom script %s crashed:', symptom_script)
2393 traceback.print_exc()
2394 sys.exit(0)
2395
2396@@ -67,7 +68,7 @@
2397 if report.has_key('ExecutablePath'):
2398 package = apport.fileutils.find_file_package(report['ExecutablePath'])
2399 else:
2400- raise KeyError, 'called without a package, and report does not have ExecutablePath'
2401+ raise KeyError('called without a package, and report does not have ExecutablePath')
2402 try:
2403 report.add_package_info(package)
2404 except ValueError:
2405@@ -75,7 +76,7 @@
2406 # package
2407 if not ignore_uninstalled:
2408 raise
2409- except SystemError, e:
2410+ except SystemError as e:
2411 report['UnreportableReason'] = excstr(e)
2412 return
2413
2414@@ -124,7 +125,7 @@
2415 report.write(f, only_new=True)
2416 f.close()
2417 apport.fileutils.mark_report_seen(reportfile)
2418- os.chmod (reportfile, 0600)
2419+ os.chmod (reportfile, 0o600)
2420
2421 class UserInterface:
2422 '''Apport user interface API.
2423@@ -144,13 +145,11 @@
2424
2425 try:
2426 self.crashdb = get_crashdb(None)
2427- except ImportError, e:
2428+ except ImportError as e:
2429 # this can happen while upgrading python packages
2430- print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
2431- sys.exit(1)
2432+ apport.fatal('Could not import module, is a package upgrade in progress? Error: %s', str(e))
2433 except KeyError:
2434- print >> sys.stderr, '/etc/apport/crashdb.conf is damaged: No default database'
2435- sys.exit(1)
2436+ apport.fatal('/etc/apport/crashdb.conf is damaged: No default database')
2437
2438 gettext.textdomain(self.gettext_domain)
2439 self.parse_argv()
2440@@ -261,7 +260,7 @@
2441 try:
2442 if 'Dependencies' not in self.report:
2443 self.collect_info()
2444- except (IOError, zlib.error), e:
2445+ except (IOError, zlib.error) as e:
2446 # can happen with broken core dumps
2447 self.report = None
2448 self.ui_error_message(_('Invalid problem report'),
2449@@ -298,7 +297,7 @@
2450 assert response == 'full'
2451
2452 self.file_report()
2453- except IOError, e:
2454+ except IOError as e:
2455 # fail gracefully if file is not readable for us
2456 if e.errno in (errno.EPERM, errno.EACCES):
2457 self.ui_error_message(_('Invalid problem report'),
2458@@ -311,11 +310,10 @@
2459 else:
2460 self.ui_error_message(_('Invalid problem report'), e.strerror)
2461 sys.exit(1)
2462- except OSError, e:
2463+ except OSError as e:
2464 # fail gracefully on ENOMEM
2465 if e.errno == errno.ENOMEM:
2466- print >> sys.stderr, 'Out of memory, aborting'
2467- sys.exit(1)
2468+ apport.fatal('Out of memory, aborting')
2469 else:
2470 raise
2471
2472@@ -348,7 +346,7 @@
2473 self.ui_error_message(_('Invalid PID'),
2474 _('The specified process ID does not belong to a program.'))
2475 return False
2476- except OSError, e:
2477+ except OSError as e:
2478 # silently ignore nonexisting PIDs; the user must not close the
2479 # application prematurely
2480 if e.errno == errno.ENOENT:
2481@@ -372,7 +370,7 @@
2482
2483 try:
2484 self.collect_info(symptom_script)
2485- except ValueError, e:
2486+ except ValueError as e:
2487 if str(e) == 'package does not exist':
2488 if not self.cur_package:
2489 self.ui_error_message(_('Invalid problem report'),
2490@@ -391,6 +389,8 @@
2491 self.report['UnreportableReason'])
2492 return
2493
2494+ self.add_extra_tags()
2495+
2496 if self.handle_duplicate():
2497 return True
2498
2499@@ -402,10 +402,10 @@
2500
2501 if self.options.save:
2502 try:
2503- f = open(self.options.save, 'w')
2504+ f = open(os.path.expanduser(self.options.save), 'w')
2505 self.report.write(f)
2506 f.close()
2507- except (IOError, OSError), e:
2508+ except (IOError, OSError) as e:
2509 self.ui_error_message(_('Cannot create report'), excstr(e))
2510 else:
2511 # show what's being sent
2512@@ -449,7 +449,7 @@
2513
2514 info_collected = False
2515 for p in pkgs:
2516- #print 'Collecting apport information for source package %s...' % p
2517+ #print('Collecting apport information for source package %s...' % p)
2518 self.cur_package = p
2519 self.report['SourcePackage'] = p
2520 self.report['Package'] = p # no way to find this out
2521@@ -460,7 +460,7 @@
2522 apport.packaging.get_version(p)
2523 except ValueError:
2524 if not os.path.exists(os.path.join(apport.report._hook_dir, 'source_%s.py' % p)):
2525- print 'Package %s not installed and no hook available, ignoring' % p
2526+ print('Package %s not installed and no hook available, ignoring' % p)
2527 continue
2528 self.collect_info(ignore_uninstalled=True)
2529 info_collected = True
2530@@ -472,6 +472,7 @@
2531
2532 self.report.add_user_info()
2533 self.report.add_proc_environ()
2534+ self.add_extra_tags()
2535
2536 # delete the uninteresting keys
2537 del self.report['ProblemType']
2538@@ -506,19 +507,29 @@
2539 symptom_names = []
2540 symptom_descriptions = []
2541 for script in scripts:
2542+ # scripts with an underscore can be used for private libraries
2543+ if os.path.basename(script).startswith('_'):
2544+ continue
2545 symb = {}
2546 try:
2547 execfile(script, symb)
2548 except:
2549- print >> sys.stderr, 'symptom script %s is invalid' % script
2550+ apport.error('symptom script %s is invalid', script)
2551 traceback.print_exc()
2552 continue
2553+ if 'run' not in symb:
2554+ apport.error('symptom script %s does not define run() function', script)
2555+ continue
2556 symptom_names.append(os.path.splitext(os.path.basename(script))[0])
2557 symptom_descriptions.append(symb.get('description', symptom_names[-1]))
2558
2559 if not symptom_names:
2560 return False
2561
2562+ symptom_descriptions, symptom_names = \
2563+ zip(*sorted(zip(symptom_descriptions, symptom_names)))
2564+ symptom_descriptions = list(symptom_descriptions)
2565+ symptom_names = list(symptom_names)
2566 symptom_names.append(None)
2567 symptom_descriptions.append('Other problem')
2568
2569@@ -559,12 +570,12 @@
2570 elif self.options.update_report:
2571 return self.run_update_report()
2572 elif self.options.version:
2573- print __version__
2574+ print(__version__)
2575 return True
2576 elif self.options.crash_file:
2577 try:
2578 self.run_crash(self.options.crash_file, False)
2579- except OSError, e:
2580+ except OSError as e:
2581 self.ui_error_message(_('Invalid problem report'), excstr(e))
2582 return True
2583 else:
2584@@ -581,8 +592,9 @@
2585 '''
2586 optparser = optparse.OptionParser(_('%prog <report number>'))
2587 optparser.add_option('-p', '--package',
2588- help=_('Specify package name.)'),
2589- dest='package', default=None)
2590+ help=_('Specify package name.)'))
2591+ optparser.add_option('--tag', action='append', default=[],
2592+ help=_('Add an extra tag to the report. Can be specified multiple times.'))
2593 (self.options, self.args) = optparser.parse_args()
2594
2595 if len(self.args) != 1 or not self.args[0].isdigit():
2596@@ -613,30 +625,29 @@
2597 return
2598
2599 optparser = optparse.OptionParser(_('%prog [options] [symptom|pid|package|program path|.apport/.crash file]'))
2600- optparser.add_option('-f', '--file-bug',
2601- help=_('Start in bug filing mode. Requires --package and an optional --pid, or just a --pid. If neither is given, display a list of known symptoms. (Implied if a single argument is given.)'),
2602- action='store_true', dest='filebug', default=False)
2603- optparser.add_option('-u', '--update-bug', type='int',
2604- help=_('Start in bug updating mode. Can take an optional --package.'),
2605- dest='update_report')
2606+ optparser.add_option('-f', '--file-bug', action='store_true',
2607+ dest='filebug', default=False,
2608+ help=_('Start in bug filing mode. Requires --package and an optional --pid, or just a --pid. If neither is given, display a list of known symptoms. (Implied if a single argument is given.)'))
2609+ optparser.add_option('-u', '--update-bug', type='int', dest='update_report',
2610+ help=_('Start in bug updating mode. Can take an optional --package.'))
2611 optparser.add_option('-s', '--symptom', metavar='SYMPTOM',
2612- help=_('File a bug report about a symptom. (Implied if symptom name is given as only argument.)'),
2613- dest='symptom')
2614+ help=_('File a bug report about a symptom. (Implied if symptom name is given as only argument.)'))
2615 optparser.add_option('-p', '--package',
2616- help=_('Specify package name in --file-bug mode. This is optional if a --pid is specified. (Implied if package name is given as only argument.)'),
2617- action='store', type='string', dest='package', default=None)
2618- optparser.add_option('-P', '--pid',
2619- help=_('Specify a running program in --file-bug mode. If this is specified, the bug report will contain more information. (Implied if pid is given as only argument.)'),
2620- action='store', type='int', dest='pid', default=None)
2621- optparser.add_option('-c', '--crash-file',
2622- help=_('Report the crash from given .apport or .crash file instead of the pending ones in %s. (Implied if file is given as only argument.)') % apport.fileutils.report_dir,
2623- action='store', type='string', dest='crash_file', default=None, metavar='PATH')
2624- optparser.add_option('--save',
2625- help=_('In --file-bug mode, save the collected information into a file instead of reporting it. This file can then be reported with --crash-file later on.'),
2626- type='string', dest='save', default=None, metavar='PATH')
2627- optparser.add_option('-v', '--version',
2628- help=_('Print the Apport version number.'),
2629- action='store_true', dest='version', default=None)
2630+ help=_('Specify package name in --file-bug mode. This is optional if a --pid is specified. (Implied if package name is given as only argument.)'))
2631+ optparser.add_option('-P', '--pid', type='int',
2632+ help=_('Specify a running program in --file-bug mode. If this is specified, the bug report will contain more information. (Implied if pid is given as only argument.)'))
2633+ optparser.add_option('-c', '--crash-file', metavar='PATH',
2634+ help=_('Report the crash from given .apport or .crash file instead of the pending ones in %s. (Implied if file is given as only argument.)') % apport.fileutils.report_dir)
2635+ optparser.add_option('--save', metavar='PATH',
2636+ help=_('In bug filing mode, save the collected information into a file instead of reporting it. This file can then be reported later on from a different machine.'))
2637+ optparser.add_option('--tag', action='append', default=[],
2638+ help=_('Add an extra tag to the report. Can be specified multiple times.'))
2639+ optparser.add_option('-v', '--version', action='store_true',
2640+ help=_('Print the Apport version number.'))
2641+
2642+ if len(sys.argv) > 0 and cmd.endswith('-bug'):
2643+ for o in ('-f', '-u', '-s', '-p', '-P', '-c'):
2644+ optparser.get_option(o).help = optparse.SUPPRESS_HELP
2645
2646 (self.options, self.args) = optparser.parse_args()
2647
2648@@ -692,11 +703,11 @@
2649 def format_filesize(self, size):
2650 '''Format the given integer as humanly readable and i18n'ed file size.'''
2651
2652- if size < 1048576:
2653- return locale.format('%.1f KiB', size/1024.)
2654- if size < 1024 * 1048576:
2655- return locale.format('%.1f MiB', size / 1048576.)
2656- return locale.format('%.1f GiB', size / float(1024 * 1048576))
2657+ if size < 1000000:
2658+ return locale.format('%.1f', size/1000.) + ' KB'
2659+ if size < 1000000000:
2660+ return locale.format('%.1f', size / 1000000.) + ' MB'
2661+ return locale.format('%.1f', size / float(1000000000)) + ' GB'
2662
2663 def get_complete_size(self):
2664 '''Return the size of the complete report.'''
2665@@ -893,7 +904,7 @@
2666 webbrowser.open(url, new=True, autoraise=True)
2667 sys.exit(0)
2668
2669- except Exception, e:
2670+ except Exception as e:
2671 os.write(w, str(e))
2672 sys.exit(1)
2673
2674@@ -925,7 +936,7 @@
2675 upthread.exc_raise()
2676 except KeyboardInterrupt:
2677 sys.exit(1)
2678- except NeedsCredentials, e:
2679+ except NeedsCredentials as e:
2680 message = _('Please enter your account information for the '
2681 '%s bug tracking system')
2682 data = self.ui_question_userpass(message % excstr(e))
2683@@ -936,7 +947,7 @@
2684 args=(self.report,
2685 progress_callback))
2686 upthread.start()
2687- except Exception, e:
2688+ except Exception as e:
2689 self.ui_error_message(_('Network problem'),
2690 '%s\n\n%s' % (
2691 _('Cannot connect to crash database, please check your Internet connection.'),
2692@@ -961,17 +972,17 @@
2693 self.report = apport.Report()
2694 self.report.load(open(path), binary='compressed')
2695 if 'ProblemType' not in self.report:
2696- raise ValueError, 'Report does not contain "ProblemType" field'
2697+ raise ValueError('Report does not contain "ProblemType" field')
2698 except MemoryError:
2699 self.report = None
2700 self.ui_error_message(_('Memory exhaustion'),
2701 _('Your system does not have enough memory to process this crash report.'))
2702 return False
2703- except IOError, e:
2704+ except IOError as e:
2705 self.report = None
2706 self.ui_error_message(_('Invalid problem report'), e.strerror)
2707 return False
2708- except (TypeError, ValueError, zlib.error), e:
2709+ except (TypeError, ValueError, zlib.error) as e:
2710 self.report = None
2711 self.ui_error_message(_('Invalid problem report'),
2712 '%s\n\n%s' % (
2713@@ -1037,6 +1048,16 @@
2714 self.open_url(self.report['BugPatternURL'])
2715 return True
2716
2717+ def add_extra_tags(self):
2718+ '''Add extra tags to report specified with --tags on CLI.'''
2719+
2720+ assert self.report
2721+ if self.options.tag:
2722+ tags = self.report.get('Tags', '')
2723+ if tags:
2724+ tags += ' '
2725+ self.report['Tags'] = tags + ' '.join(self.options.tag)
2726+
2727 #
2728 # abstract UI methods that must be implemented in derived classes
2729 #
2730@@ -1058,7 +1079,7 @@
2731 - Valid values for the 'blacklist' key: True or False (True will cause
2732 the invocation of report.mark_ignore()).
2733 '''
2734- raise NotImplementedError, 'this function must be overridden by subclasses'
2735+ raise NotImplementedError('this function must be overridden by subclasses')
2736
2737 def ui_present_package_error(self, desktopentry):
2738 '''Ask what to do with a package failure.
2739@@ -1069,7 +1090,7 @@
2740 Return the action: ignore ('cancel'), or report a bug about the problem
2741 ('report').
2742 '''
2743- raise NotImplementedError, 'this function must be overridden by subclasses'
2744+ raise NotImplementedError('this function must be overridden by subclasses')
2745
2746 def ui_present_kernel_error(self, desktopentry):
2747 '''Ask what to do with a kernel error.
2748@@ -1080,7 +1101,7 @@
2749 Return the action: ignore ('cancel'), or report a bug about the problem
2750 ('report').
2751 '''
2752- raise NotImplementedError, 'this function must be overridden by subclasses'
2753+ raise NotImplementedError('this function must be overridden by subclasses')
2754
2755 def ui_present_report_details(self, is_update):
2756 '''Show details of the bug report.
2757@@ -1097,17 +1118,17 @@
2758 Return the action: send full report ('full'), send reduced report
2759 ('reduced'), or do not send anything ('cancel').
2760 '''
2761- raise NotImplementedError, 'this function must be overridden by subclasses'
2762+ raise NotImplementedError('this function must be overridden by subclasses')
2763
2764 def ui_info_message(self, title, text):
2765 '''Show an information message box with given title and text.'''
2766
2767- raise NotImplementedError, 'this function must be overridden by subclasses'
2768+ raise NotImplementedError('this function must be overridden by subclasses')
2769
2770 def ui_error_message(self, title, text):
2771 '''Show an error message box with given title and text.'''
2772
2773- raise NotImplementedError, 'this function must be overridden by subclasses'
2774+ raise NotImplementedError('this function must be overridden by subclasses')
2775
2776 def ui_start_info_collection_progress(self):
2777 '''Open a indefinite progress bar for data collection.
2778@@ -1115,26 +1136,26 @@
2779 This tells the user to wait while debug information is being
2780 collected.
2781 '''
2782- raise NotImplementedError, 'this function must be overridden by subclasses'
2783+ raise NotImplementedError('this function must be overridden by subclasses')
2784
2785 def ui_pulse_info_collection_progress(self):
2786 '''Advance the data collection progress bar.
2787
2788 This function is called every 100 ms.
2789 '''
2790- raise NotImplementedError, 'this function must be overridden by subclasses'
2791+ raise NotImplementedError('this function must be overridden by subclasses')
2792
2793 def ui_stop_info_collection_progress(self):
2794 '''Close debug data collection progress window.'''
2795
2796- raise NotImplementedError, 'this function must be overridden by subclasses'
2797+ raise NotImplementedError('this function must be overridden by subclasses')
2798
2799 def ui_start_upload_progress(self):
2800 '''Open progress bar for data upload.
2801
2802 This tells the user to wait while debug information is being uploaded.
2803 '''
2804- raise NotImplementedError, 'this function must be overridden by subclasses'
2805+ raise NotImplementedError('this function must be overridden by subclasses')
2806
2807 def ui_set_upload_progress(self, progress):
2808 '''Update data upload progress bar.
2809@@ -1144,12 +1165,12 @@
2810
2811 This function is called every 100 ms.
2812 '''
2813- raise NotImplementedError, 'this function must be overridden by subclasses'
2814+ raise NotImplementedError('this function must be overridden by subclasses')
2815
2816 def ui_stop_upload_progress(self):
2817 '''Close debug data upload progress window.'''
2818
2819- raise NotImplementedError, 'this function must be overridden by subclasses'
2820+ raise NotImplementedError('this function must be overridden by subclasses')
2821
2822 def ui_shutdown(self):
2823 '''Called right before terminating the program.
2824@@ -1169,7 +1190,7 @@
2825 Return True if the user selected "Yes", False if selected "No" or
2826 "None" on cancel/dialog closing.
2827 '''
2828- raise NotImplementedError, 'this function must be overridden by subclasses'
2829+ raise NotImplementedError('this function must be overridden by subclasses')
2830
2831 def ui_question_choice(self, text, options, multiple):
2832 '''Show an question with predefined choices.
2833@@ -1181,14 +1202,14 @@
2834 Return list of selected option indexes, or None if the user cancelled.
2835 If multiple == False, the list will always have one element.
2836 '''
2837- raise NotImplementedError, 'this function must be overridden by subclasses'
2838+ raise NotImplementedError('this function must be overridden by subclasses')
2839
2840 def ui_question_file(self, text):
2841 '''Show a file selector dialog.
2842
2843 Return path if the user selected a file, or None if cancelled.
2844 '''
2845- raise NotImplementedError, 'this function must be overridden by subclasses'
2846+ raise NotImplementedError('this function must be overridden by subclasses')
2847
2848 def ui_question_userpass(self, message):
2849 '''Request username and password from user.
2850@@ -1198,7 +1219,7 @@
2851
2852 Return a tuple (username, password), or None if cancelled.
2853 '''
2854- raise NotImplementedError, 'this function must be overridden by subclasses'
2855+ raise NotImplementedError('this function must be overridden by subclasses')
2856
2857 class HookUI:
2858 '''Interactive functions which can be used in package hooks.
2859@@ -1303,7 +1324,10 @@
2860
2861 if __name__ == '__main__':
2862 import unittest, shutil, signal, tempfile, resource
2863- from cStringIO import StringIO
2864+ try:
2865+ from cStringIO import StringIO
2866+ except ImportError:
2867+ from io import StringIO
2868 import apport.report
2869 import problem_report
2870 import apport.crashdb_impl.memory
2871@@ -1314,14 +1338,14 @@
2872 def __init__(self):
2873 # use our dummy crashdb
2874 self.crashdb_conf = tempfile.NamedTemporaryFile()
2875- print >> self.crashdb_conf, '''default = 'testsuite'
2876+ self.crashdb_conf.write('''default = 'testsuite'
2877 databases = {
2878 'testsuite': {
2879 'impl': 'memory',
2880 'bug_pattern_base': None
2881 }
2882 }
2883-'''
2884+''')
2885 self.crashdb_conf.flush()
2886
2887 os.environ['APPORT_CRASHDB_CONF'] = self.crashdb_conf.name
2888@@ -1487,14 +1511,14 @@
2889 def test_format_filesize(self):
2890 '''format_filesize().'''
2891
2892- self.assertEqual(self.ui.format_filesize(0), '0.0 KiB')
2893- self.assertEqual(self.ui.format_filesize(2048), '2.0 KiB')
2894- self.assertEqual(self.ui.format_filesize(2560), '2.5 KiB')
2895- self.assertEqual(self.ui.format_filesize(1000000), '976.6 KiB')
2896- self.assertEqual(self.ui.format_filesize(1048576), '1.0 MiB')
2897- self.assertEqual(self.ui.format_filesize(2.7*1048576), '2.7 MiB')
2898- self.assertEqual(self.ui.format_filesize(1024*1048576), '1.0 GiB')
2899- self.assertEqual(self.ui.format_filesize(2560*1048576), '2.5 GiB')
2900+ self.assertEqual(self.ui.format_filesize(0), '0.0 KB')
2901+ self.assertEqual(self.ui.format_filesize(2048), '2.0 KB')
2902+ self.assertEqual(self.ui.format_filesize(2560), '2.6 KB')
2903+ self.assertEqual(self.ui.format_filesize(999999), '1000.0 KB')
2904+ self.assertEqual(self.ui.format_filesize(1000000), '1.0 MB')
2905+ self.assertEqual(self.ui.format_filesize(2.7*1000000), '2.7 MB')
2906+ self.assertEqual(self.ui.format_filesize(1024*1000000), '1.0 GB')
2907+ self.assertEqual(self.ui.format_filesize(2560*1000000), '2.6 GB')
2908
2909 def test_get_size_loaded(self):
2910 '''get_complete_size() and get_reduced_size() for loaded Reports.'''
2911@@ -1503,21 +1527,21 @@
2912
2913 fsize = os.path.getsize(self.report_file.name)
2914 complete_ratio = float(self.ui.get_complete_size()) / fsize
2915- self.assert_(complete_ratio >= 0.99 and complete_ratio <= 1.01)
2916+ self.assertTrue(complete_ratio >= 0.99 and complete_ratio <= 1.01)
2917
2918 rs = self.ui.get_reduced_size()
2919- self.assert_(rs > 1000)
2920- self.assert_(rs < 10000)
2921+ self.assertTrue(rs > 1000)
2922+ self.assertTrue(rs < 10000)
2923
2924 # now add some information (e. g. from package hooks)
2925 self.ui.report['ExtraInfo'] = 'A' * 50000
2926 s = self.ui.get_complete_size()
2927- self.assert_(s >= fsize + 49900)
2928- self.assert_(s < fsize + 60000)
2929+ self.assertTrue(s >= fsize + 49900)
2930+ self.assertTrue(s < fsize + 60000)
2931
2932 rs = self.ui.get_reduced_size()
2933- self.assert_(rs > 51000)
2934- self.assert_(rs < 60000)
2935+ self.assertTrue(rs > 51000)
2936+ self.assertTrue(rs < 60000)
2937
2938 def test_get_size_constructed(self):
2939 '''get_complete_size() and get_reduced_size() for on-the-fly Reports.'''
2940@@ -1526,8 +1550,8 @@
2941 self.ui.report['Hello'] = 'World'
2942
2943 s = self.ui.get_complete_size()
2944- self.assert_(s > 5)
2945- self.assert_(s < 100)
2946+ self.assertTrue(s > 5)
2947+ self.assertTrue(s < 100)
2948
2949 self.assertEqual(s, self.ui.get_reduced_size())
2950
2951@@ -1546,7 +1570,7 @@
2952 self.update_report_file()
2953 self.ui.load_report(self.report_file.name)
2954
2955- self.assert_(self.ui.report == None)
2956+ self.assertTrue(self.ui.report == None)
2957 self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
2958 self.assertEqual(self.ui.msg_severity, 'info')
2959
2960@@ -1563,7 +1587,7 @@
2961 self.report_file.flush()
2962
2963 self.ui.load_report(self.report_file.name)
2964- self.assert_(self.ui.report == None)
2965+ self.assertTrue(self.ui.report == None)
2966 self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
2967 self.assertEqual(self.ui.msg_severity, 'error')
2968
2969@@ -1579,8 +1603,8 @@
2970
2971 self.ui.restart()
2972 time.sleep(1) # FIXME: race condition
2973- self.assert_(os.path.exists(p))
2974- self.assert_(not os.path.exists(r))
2975+ self.assertTrue(os.path.exists(p))
2976+ self.assertTrue(not os.path.exists(r))
2977 os.unlink(p)
2978
2979 # test with RespawnCommand
2980@@ -1590,8 +1614,8 @@
2981
2982 self.ui.restart()
2983 time.sleep(1) # FIXME: race condition
2984- self.assert_(not os.path.exists(p))
2985- self.assert_(os.path.exists(r))
2986+ self.assertTrue(not os.path.exists(p))
2987+ self.assertTrue(os.path.exists(r))
2988 os.unlink(r)
2989
2990 # test that invalid command does not make us fall apart
2991@@ -1606,7 +1630,7 @@
2992 # report without any information (distro bug)
2993 self.ui.report = apport.Report()
2994 self.ui.collect_info()
2995- self.assert_(set(['Date', 'Uname', 'DistroRelease', 'ProblemType']).issubset(
2996+ self.assertTrue(set(['Date', 'Uname', 'DistroRelease', 'ProblemType']).issubset(
2997 set(self.ui.report.keys())))
2998 self.assertEqual(self.ui.ic_progress_pulses, 0,
2999 'no progress dialog for distro bug info collection')
3000@@ -1624,10 +1648,10 @@
3001 self.ui.report['Fstab'] = ('/etc/fstab', True)
3002 self.ui.report['CompressedValue'] = problem_report.CompressedValue('Test')
3003 self.ui.collect_info()
3004- self.assert_(set(['SourcePackage', 'Package', 'ProblemType',
3005+ self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
3006 'Uname', 'Dependencies', 'DistroRelease', 'Date',
3007 'ExecutablePath']).issubset(set(self.ui.report.keys())))
3008- self.assert_(self.ui.ic_progress_pulses > 0,
3009+ self.assertTrue(self.ui.ic_progress_pulses > 0,
3010 'progress dialog for package bug info collection')
3011 self.assertEqual(self.ui.ic_progress_active, False,
3012 'progress dialog for package bug info collection finished')
3013@@ -1639,10 +1663,10 @@
3014 self.ui.report = apport.Report()
3015 self.ui.cur_package = 'bash'
3016 self.ui.collect_info()
3017- self.assert_(set(['SourcePackage', 'Package', 'ProblemType',
3018+ self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
3019 'Uname', 'Dependencies', 'DistroRelease',
3020 'Date']).issubset(set(self.ui.report.keys())))
3021- self.assert_(self.ui.ic_progress_pulses > 0,
3022+ self.assertTrue(self.ui.ic_progress_pulses > 0,
3023 'progress dialog for package bug info collection')
3024 self.assertEqual(self.ui.ic_progress_active, False,
3025 'progress dialog for package bug info collection finished')
3026@@ -1699,13 +1723,13 @@
3027
3028 self.assertEqual(self.ui.msg_severity, None)
3029 self.assertEqual(self.ui.msg_title, None)
3030- self.assert_(self.ui.present_details_shown)
3031+ self.assertTrue(self.ui.present_details_shown)
3032 self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3033
3034- self.assert_(self.ui.ic_progress_pulses > 0)
3035+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3036 self.assertEqual(self.ui.report['SourcePackage'], 'bash')
3037- self.assert_('Dependencies' in self.ui.report.keys())
3038- self.assert_('ProcEnviron' in self.ui.report.keys())
3039+ self.assertTrue('Dependencies' in self.ui.report.keys())
3040+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3041 self.assertEqual(self.ui.report['ProblemType'], 'Bug')
3042
3043 # should not crash on nonexisting package
3044@@ -1715,8 +1739,8 @@
3045
3046 self.assertEqual(self.ui.msg_severity, 'error')
3047
3048- def test_run_report_bug_pid(self):
3049- '''run_report_bug() for a pid.'''
3050+ def test_run_report_bug_pid_tags(self):
3051+ '''run_report_bug() for a pid with extra tags.'''
3052
3053 # fork a test process
3054 pid = os.fork()
3055@@ -1728,7 +1752,7 @@
3056
3057 try:
3058 # report a bug on cat process
3059- sys.argv = ['ui-test', '-f', '-P', str(pid)]
3060+ sys.argv = ['ui-test', '-f', '--tag', 'foo', '-P', str(pid)]
3061 self.ui = _TestSuiteUserInterface()
3062 self.assertEqual(self.ui.run_argv(), True)
3063 finally:
3064@@ -1736,19 +1760,21 @@
3065 os.kill(pid, signal.SIGKILL)
3066 os.waitpid(pid, 0)
3067
3068- self.assert_('SourcePackage' in self.ui.report.keys())
3069- self.assert_('Dependencies' in self.ui.report.keys())
3070- self.assert_('ProcMaps' in self.ui.report.keys())
3071+ self.assertTrue('SourcePackage' in self.ui.report.keys())
3072+ self.assertTrue('Dependencies' in self.ui.report.keys())
3073+ self.assertTrue('ProcMaps' in self.ui.report.keys())
3074 self.assertEqual(self.ui.report['ExecutablePath'], '/bin/sleep')
3075 self.failIf(self.ui.report.has_key('ProcCmdline')) # privacy!
3076- self.assert_('ProcEnviron' in self.ui.report.keys())
3077+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3078 self.assertEqual(self.ui.report['ProblemType'], 'Bug')
3079+ self.assertTrue('Tags' in self.ui.report.keys())
3080+ self.assertTrue('foo' in self.ui.report['Tags'])
3081
3082 self.assertEqual(self.ui.msg_severity, None)
3083 self.assertEqual(self.ui.msg_title, None)
3084 self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3085- self.assert_(self.ui.present_details_shown)
3086- self.assert_(self.ui.ic_progress_pulses > 0)
3087+ self.assertTrue(self.ui.present_details_shown)
3088+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3089
3090 @classmethod
3091 def _find_unused_pid(klass):
3092@@ -1759,7 +1785,7 @@
3093 pid += 1
3094 try:
3095 os.kill(pid, 0)
3096- except OSError, e:
3097+ except OSError as e:
3098 if e.errno == errno.ESRCH:
3099 break
3100 return pid
3101@@ -1792,7 +1818,7 @@
3102 (fd, exename) = tempfile.mkstemp()
3103 os.write(fd, open('/bin/cat').read())
3104 os.close(fd)
3105- os.chmod(exename, 0755)
3106+ os.chmod(exename, 0o755)
3107
3108 # unpackaged test process
3109 pid = os.fork()
3110@@ -1826,14 +1852,14 @@
3111 self.assertEqual(self.ui.opened_url, None)
3112 self.failIf(self.ui.present_details_shown)
3113
3114- self.assert_(self.ui.ic_progress_pulses > 0)
3115+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3116
3117 r = apport.Report()
3118 r.load(open(reportfile))
3119
3120 self.assertEqual(r['SourcePackage'], 'bash')
3121- self.assert_('Dependencies' in r.keys())
3122- self.assert_('ProcEnviron' in r.keys())
3123+ self.assertTrue('Dependencies' in r.keys())
3124+ self.assertTrue('ProcEnviron' in r.keys())
3125 self.assertEqual(r['ProblemType'], 'Bug')
3126
3127 # report it
3128@@ -1845,7 +1871,7 @@
3129
3130 self.assertEqual(self.ui.msg_text, None)
3131 self.assertEqual(self.ui.msg_severity, None)
3132- self.assert_(self.ui.present_details_shown)
3133+ self.assertTrue(self.ui.present_details_shown)
3134
3135 def _gen_test_crash(self):
3136 '''Generate a Report with real crash data.'''
3137@@ -1908,7 +1934,7 @@
3138 self.assertEqual(self.ui.msg_title, None)
3139 self.assertEqual(self.ui.opened_url, None)
3140 self.assertNotEqual(self.ui.ic_progress_pulses, 0)
3141- self.assert_(self.ui.present_details_shown)
3142+ self.assertTrue(self.ui.present_details_shown)
3143
3144 # report in crash notification dialog, send full report
3145 r.write(open(report_file, 'w'))
3146@@ -1920,15 +1946,15 @@
3147 self.assertEqual(self.ui.msg_title, None)
3148 self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3149 self.assertNotEqual(self.ui.ic_progress_pulses, 0)
3150- self.assert_(self.ui.present_details_shown)
3151+ self.assertTrue(self.ui.present_details_shown)
3152
3153- self.assert_('SourcePackage' in self.ui.report.keys())
3154- self.assert_('Dependencies' in self.ui.report.keys())
3155- self.assert_('Stacktrace' in self.ui.report.keys())
3156- self.assert_('ProcEnviron' in self.ui.report.keys())
3157+ self.assertTrue('SourcePackage' in self.ui.report.keys())
3158+ self.assertTrue('Dependencies' in self.ui.report.keys())
3159+ self.assertTrue('Stacktrace' in self.ui.report.keys())
3160+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3161 self.assertEqual(self.ui.report['ProblemType'], 'Crash')
3162- self.assert_(len(self.ui.report['CoreDump']) > 10000)
3163- self.assert_(self.ui.report['Title'].startswith('cat crashed with SIGSEGV'))
3164+ self.assertTrue(len(self.ui.report['CoreDump']) > 10000)
3165+ self.assertTrue(self.ui.report['Title'].startswith('cat crashed with SIGSEGV'))
3166
3167 # report in crash notification dialog, send reduced report
3168 r.write(open(report_file, 'w'))
3169@@ -1940,16 +1966,16 @@
3170 self.assertEqual(self.ui.msg_title, None)
3171 self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3172 self.assertNotEqual(self.ui.ic_progress_pulses, 0)
3173- self.assert_(self.ui.present_details_shown)
3174+ self.assertTrue(self.ui.present_details_shown)
3175
3176- self.assert_('SourcePackage' in self.ui.report.keys())
3177- self.assert_('Dependencies' in self.ui.report.keys())
3178- self.assert_('Stacktrace' in self.ui.report.keys())
3179+ self.assertTrue('SourcePackage' in self.ui.report.keys())
3180+ self.assertTrue('Dependencies' in self.ui.report.keys())
3181+ self.assertTrue('Stacktrace' in self.ui.report.keys())
3182 self.assertEqual(self.ui.report['ProblemType'], 'Crash')
3183- self.assert_(not self.ui.report.has_key('CoreDump'))
3184+ self.assertTrue(not self.ui.report.has_key('CoreDump'))
3185
3186 # so far we did not blacklist, verify that
3187- self.assert_(not self.ui.report.check_ignored())
3188+ self.assertTrue(not self.ui.report.check_ignored())
3189
3190 # cancel crash notification dialog and blacklist
3191 r.write(open(report_file, 'w'))
3192@@ -1961,7 +1987,7 @@
3193 self.assertEqual(self.ui.opened_url, None)
3194 self.assertEqual(self.ui.ic_progress_pulses, 0)
3195
3196- self.assert_(self.ui.report.check_ignored())
3197+ self.assertTrue(self.ui.report.check_ignored())
3198
3199 def test_run_crash_abort(self):
3200 '''run_crash() for an unreportable abort()'''
3201@@ -1974,7 +2000,7 @@
3202 self.ui.present_details_response = 'full'
3203 self.ui.run_crash(self.report_file.name)
3204
3205- self.assert_('assert' in self.ui.msg_text, '%s: %s' %
3206+ self.assertTrue('assert' in self.ui.msg_text, '%s: %s' %
3207 (self.ui.msg_title, self.ui.msg_text))
3208 self.assertEqual(self.ui.msg_severity, 'info')
3209 self.failIf(self.ui.present_details_shown)
3210@@ -1994,7 +2020,7 @@
3211
3212 self.assertEqual(self.ui.msg_text, None)
3213 self.assertEqual(self.ui.msg_severity, None)
3214- self.assert_(self.ui.present_details_shown)
3215+ self.assertTrue(self.ui.present_details_shown)
3216
3217 # unreportable
3218 self.report['Package'] = 'bash'
3219@@ -2005,7 +2031,7 @@
3220 self.ui = _TestSuiteUserInterface()
3221 self.assertEqual(self.ui.run_argv(), True)
3222
3223- self.assert_('It stinks.' in self.ui.msg_text, '%s: %s' %
3224+ self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
3225 (self.ui.msg_title, self.ui.msg_text))
3226 self.assertEqual(self.ui.msg_severity, 'info')
3227 self.failIf(self.ui.present_details_shown)
3228@@ -2029,7 +2055,7 @@
3229
3230 self.ui.run_crash(self.report_file.name)
3231
3232- self.assert_('It stinks.' in self.ui.msg_text, '%s: %s' %
3233+ self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
3234 (self.ui.msg_title, self.ui.msg_text))
3235 self.assertEqual(self.ui.msg_severity, 'info')
3236
3237@@ -2077,7 +2103,7 @@
3238 self.ui = _TestSuiteUserInterface()
3239 self.ui.run_crash(report_file)
3240 self.assertEqual(self.ui.msg_severity, 'error')
3241- self.assert_('memory' in self.ui.msg_text, '%s: %s' %
3242+ self.assertTrue('memory' in self.ui.msg_text, '%s: %s' %
3243 (self.ui.msg_title, self.ui.msg_text))
3244
3245 def test_run_crash_preretraced(self):
3246@@ -2106,7 +2132,7 @@
3247 self.assertEqual(self.ui.msg_title, None)
3248 self.assertEqual(self.ui.opened_url, None)
3249 self.assertEqual(self.ui.ic_progress_pulses, 0)
3250- self.assert_(self.ui.present_details_shown)
3251+ self.assertTrue(self.ui.present_details_shown)
3252
3253 def test_run_crash_errors(self):
3254 '''run_crash() on various error conditions.'''
3255@@ -2196,16 +2222,16 @@
3256 self.assertEqual(self.ui.msg_severity, None)
3257 self.assertEqual(self.ui.msg_title, None)
3258 self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3259- self.assert_(self.ui.present_details_shown)
3260+ self.assertTrue(self.ui.present_details_shown)
3261
3262- self.assert_('SourcePackage' in self.ui.report.keys())
3263- self.assert_('Package' in self.ui.report.keys())
3264+ self.assertTrue('SourcePackage' in self.ui.report.keys())
3265+ self.assertTrue('Package' in self.ui.report.keys())
3266 self.assertEqual(self.ui.report['ProblemType'], 'Package')
3267
3268 # verify that additional information has been collected
3269- self.assert_('Architecture' in self.ui.report.keys())
3270- self.assert_('DistroRelease' in self.ui.report.keys())
3271- self.assert_('Uname' in self.ui.report.keys())
3272+ self.assertTrue('Architecture' in self.ui.report.keys())
3273+ self.assertTrue('DistroRelease' in self.ui.report.keys())
3274+ self.assertTrue('Uname' in self.ui.report.keys())
3275
3276 def test_run_crash_kernel(self):
3277 '''run_crash() for a kernel error.'''
3278@@ -2247,11 +2273,11 @@
3279 ' ' + str(self.ui.msg_text))
3280 self.assertEqual(self.ui.msg_title, None)
3281 self.assertEqual(self.ui.opened_url, 'http://linux.bugs.example.com/%i' % self.ui.crashdb.latest_id())
3282- self.assert_(self.ui.present_details_shown)
3283+ self.assertTrue(self.ui.present_details_shown)
3284
3285- self.assert_('SourcePackage' in self.ui.report.keys())
3286+ self.assertTrue('SourcePackage' in self.ui.report.keys())
3287 # did we run the hooks properly?
3288- self.assert_('KernelDebug' in self.ui.report.keys())
3289+ self.assertTrue('KernelDebug' in self.ui.report.keys())
3290 self.assertEqual(self.ui.report['ProblemType'], 'KernelCrash')
3291
3292 def test_run_crash_anonymity(self):
3293@@ -2285,7 +2311,7 @@
3294 '', {'dummy_data': 1})
3295
3296 self.assertEqual(self.ui.run_argv(), False)
3297- self.assert_('No additional information collected.' in
3298+ self.assertTrue('No additional information collected.' in
3299 self.ui.msg_text)
3300 self.failIf(self.ui.present_details_shown)
3301
3302@@ -2298,7 +2324,7 @@
3303 '', {'dummy_data': 1})
3304
3305 self.assertEqual(self.ui.run_argv(), False)
3306- self.assert_('No additional information collected.' in
3307+ self.assertTrue('No additional information collected.' in
3308 self.ui.msg_text)
3309 self.failIf(self.ui.present_details_shown)
3310
3311@@ -2316,17 +2342,17 @@
3312 self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
3313 self.assertEqual(self.ui.msg_title, None)
3314 self.assertEqual(self.ui.opened_url, None)
3315- self.assert_(self.ui.present_details_shown)
3316-
3317- self.assert_(self.ui.ic_progress_pulses > 0)
3318- self.assert_(self.ui.report['Package'].startswith('bash '))
3319- self.assert_('Dependencies' in self.ui.report.keys())
3320- self.assert_('ProcEnviron' in self.ui.report.keys())
3321-
3322- def test_run_update_report_existing_package_cli(self):
3323- '''run_update_report() on an existing package (CLI argument).'''
3324-
3325- sys.argv = ['ui-test', '-u', '1', '-p', 'bash']
3326+ self.assertTrue(self.ui.present_details_shown)
3327+
3328+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3329+ self.assertTrue(self.ui.report['Package'].startswith('bash '))
3330+ self.assertTrue('Dependencies' in self.ui.report.keys())
3331+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3332+
3333+ def test_run_update_report_existing_package_cli_tags(self):
3334+ '''run_update_report() on an existing package (CLI argument) with extra tag'''
3335+
3336+ sys.argv = ['ui-test', '-u', '1', '-p', 'bash', '--tag', 'foo']
3337 self.ui = _TestSuiteUserInterface()
3338 self.ui.crashdb = apport.crashdb_impl.memory.CrashDatabase(None,
3339 '', {'dummy_data': 1})
3340@@ -2335,12 +2361,13 @@
3341 self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
3342 self.assertEqual(self.ui.msg_title, None)
3343 self.assertEqual(self.ui.opened_url, None)
3344- self.assert_(self.ui.present_details_shown)
3345+ self.assertTrue(self.ui.present_details_shown)
3346
3347- self.assert_(self.ui.ic_progress_pulses > 0)
3348- self.assert_(self.ui.report['Package'].startswith('bash '))
3349- self.assert_('Dependencies' in self.ui.report.keys())
3350- self.assert_('ProcEnviron' in self.ui.report.keys())
3351+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3352+ self.assertTrue(self.ui.report['Package'].startswith('bash '))
3353+ self.assertTrue('Dependencies' in self.ui.report.keys())
3354+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3355+ self.assertTrue('foo' in self.ui.report['Tags'])
3356
3357 def test_run_update_report_existing_package_cli_cmdname(self):
3358 '''run_update_report() on an existing package (-collect program).'''
3359@@ -2354,12 +2381,12 @@
3360 self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
3361 self.assertEqual(self.ui.msg_title, None)
3362 self.assertEqual(self.ui.opened_url, None)
3363- self.assert_(self.ui.present_details_shown)
3364+ self.assertTrue(self.ui.present_details_shown)
3365
3366- self.assert_(self.ui.ic_progress_pulses > 0)
3367- self.assert_(self.ui.report['Package'].startswith('bash '))
3368- self.assert_('Dependencies' in self.ui.report.keys())
3369- self.assert_('ProcEnviron' in self.ui.report.keys())
3370+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3371+ self.assertTrue(self.ui.report['Package'].startswith('bash '))
3372+ self.assertTrue('Dependencies' in self.ui.report.keys())
3373+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3374
3375 def test_run_update_report_noninstalled_but_hook(self):
3376 '''run_update_report() on an uninstalled package with a source hook.'''
3377@@ -2377,12 +2404,12 @@
3378 self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
3379 self.assertEqual(self.ui.msg_title, None)
3380 self.assertEqual(self.ui.opened_url, None)
3381- self.assert_(self.ui.present_details_shown)
3382+ self.assertTrue(self.ui.present_details_shown)
3383
3384- self.assert_(self.ui.ic_progress_pulses > 0)
3385+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3386 self.assertEqual(self.ui.report['Package'], 'foo (not installed)')
3387 self.assertEqual(self.ui.report['MachineType'], 'Laptop')
3388- self.assert_('ProcEnviron' in self.ui.report.keys())
3389+ self.assertTrue('ProcEnviron' in self.ui.report.keys())
3390
3391 def _run_hook(self, code):
3392 f = open(os.path.join(self.hookdir, 'coreutils.py'), 'w')
3393@@ -2478,12 +2505,12 @@
3394 sys.argv = ['ui-test', '-s', 'foobar' ]
3395 self.ui = _TestSuiteUserInterface()
3396 self.assertEqual(self.ui.run_argv(), True)
3397- self.assert_('foobar" is not known' in self.ui.msg_text)
3398+ self.assertTrue('foobar" is not known' in self.ui.msg_text)
3399 self.assertEqual(self.ui.msg_severity, 'error')
3400
3401 # does not determine package
3402 f = open(os.path.join(symptom_script_dir, 'nopkg.py'), 'w')
3403- print >> f, 'def run(report, ui):\n pass'
3404+ f.write('def run(report, ui):\n pass\n')
3405 f.close()
3406 orig_stderr = sys.stderr
3407 sys.argv = ['ui-test', '-s', 'nopkg' ]
3408@@ -2492,11 +2519,11 @@
3409 self.assertRaises(SystemExit, self.ui.run_argv)
3410 err = sys.stderr.getvalue()
3411 sys.stderr = orig_stderr
3412- self.assert_('did not determine the affected package' in err)
3413+ self.assertTrue('did not determine the affected package' in err)
3414
3415 # does not define run()
3416 f = open(os.path.join(symptom_script_dir, 'norun.py'), 'w')
3417- print >> f, 'def something(x, y):\n return 1'
3418+ f.write('def something(x, y):\n return 1\n')
3419 f.close()
3420 sys.argv = ['ui-test', '-s', 'norun' ]
3421 self.ui = _TestSuiteUserInterface()
3422@@ -2504,11 +2531,11 @@
3423 self.assertRaises(SystemExit, self.ui.run_argv)
3424 err = sys.stderr.getvalue()
3425 sys.stderr = orig_stderr
3426- self.assert_('norun.py crashed:' in err)
3427+ self.assertTrue('norun.py crashed:' in err)
3428
3429 # crashing script
3430 f = open(os.path.join(symptom_script_dir, 'crash.py'), 'w')
3431- print >> f, 'def run(report, ui):\n return 1/0'
3432+ f.write('def run(report, ui):\n return 1/0\n')
3433 f.close()
3434 sys.argv = ['ui-test', '-s', 'crash' ]
3435 self.ui = _TestSuiteUserInterface()
3436@@ -2516,45 +2543,56 @@
3437 self.assertRaises(SystemExit, self.ui.run_argv)
3438 err = sys.stderr.getvalue()
3439 sys.stderr = orig_stderr
3440- self.assert_('crash.py crashed:' in err)
3441- self.assert_('ZeroDivisionError:' in err)
3442+ self.assertTrue('crash.py crashed:' in err)
3443+ self.assertTrue('ZeroDivisionError:' in err)
3444
3445 # working noninteractive script
3446 f = open(os.path.join(symptom_script_dir, 'itching.py'), 'w')
3447- print >> f, 'def run(report, ui):\n report["itch"] = "scratch"\n return "bash"'
3448+ f.write('def run(report, ui):\n report["itch"] = "scratch"\n return "bash"\n')
3449 f.close()
3450 sys.argv = ['ui-test', '-s', 'itching' ]
3451 self.ui = _TestSuiteUserInterface()
3452 self.assertEqual(self.ui.run_argv(), True)
3453 self.assertEqual(self.ui.msg_text, None)
3454 self.assertEqual(self.ui.msg_severity, None)
3455- self.assert_(self.ui.present_details_shown)
3456+ self.assertTrue(self.ui.present_details_shown)
3457
3458 self.assertEqual(self.ui.report['itch'], 'scratch')
3459- self.assert_('DistroRelease' in self.ui.report)
3460+ self.assertTrue('DistroRelease' in self.ui.report)
3461 self.assertEqual(self.ui.report['SourcePackage'], 'bash')
3462- self.assert_(self.ui.report['Package'].startswith('bash '))
3463+ self.assertTrue(self.ui.report['Package'].startswith('bash '))
3464 self.assertEqual(self.ui.report['ProblemType'], 'Bug')
3465
3466+ # working noninteractive script with extra tag
3467+ sys.argv = ['ui-test', '--tag', 'foo', '-s', 'itching' ]
3468+ self.ui = _TestSuiteUserInterface()
3469+ self.assertEqual(self.ui.run_argv(), True)
3470+ self.assertEqual(self.ui.msg_text, None)
3471+ self.assertEqual(self.ui.msg_severity, None)
3472+ self.assertTrue(self.ui.present_details_shown)
3473+
3474+ self.assertEqual(self.ui.report['itch'], 'scratch')
3475+ self.assertTrue('foo' in self.ui.report['Tags'])
3476+
3477 # working interactive script
3478 f = open(os.path.join(symptom_script_dir, 'itching.py'), 'w')
3479- print >> f, '''def run(report, ui):
3480+ f.write('''def run(report, ui):
3481 report['itch'] = 'slap'
3482 report['q'] = str(ui.yesno('do you?'))
3483 return 'bash'
3484-'''
3485+''')
3486 f.close()
3487 sys.argv = ['ui-test', '-s', 'itching' ]
3488 self.ui = _TestSuiteUserInterface()
3489 self.ui.question_yesno_response = True
3490 self.assertEqual(self.ui.run_argv(), True)
3491- self.assert_(self.ui.present_details_shown)
3492+ self.assertTrue(self.ui.present_details_shown)
3493 self.assertEqual(self.ui.msg_text, 'do you?')
3494
3495 self.assertEqual(self.ui.report['itch'], 'slap')
3496- self.assert_('DistroRelease' in self.ui.report)
3497+ self.assertTrue('DistroRelease' in self.ui.report)
3498 self.assertEqual(self.ui.report['SourcePackage'], 'bash')
3499- self.assert_(self.ui.report['Package'].startswith('bash '))
3500+ self.assertTrue(self.ui.report['Package'].startswith('bash '))
3501 self.assertEqual(self.ui.report['ProblemType'], 'Bug')
3502 self.assertEqual(self.ui.report['q'], 'True')
3503
3504@@ -2562,13 +2600,13 @@
3505 '''run_report_bug() without specifying arguments and available symptoms.'''
3506
3507 f = open(os.path.join(symptom_script_dir, 'foo.py'), 'w')
3508- print >> f, '''description = 'foo does not work'
3509+ f.write('''description = 'foo does not work'
3510 def run(report, ui):
3511 return 'bash'
3512-'''
3513+''')
3514 f.close()
3515 f = open(os.path.join(symptom_script_dir, 'bar.py'), 'w')
3516- print >> f, 'def run(report, ui):\n return "coreutils"'
3517+ f.write('def run(report, ui):\n return "coreutils"\n')
3518 f.close()
3519
3520 sys.argv = ['ui-test', '-f']
3521@@ -2577,7 +2615,7 @@
3522 self.ui.question_choice_response = None
3523 self.assertEqual(self.ui.run_argv(), True)
3524 self.assertEqual(self.ui.msg_severity, None)
3525- self.assert_('kind of problem' in self.ui.msg_text)
3526+ self.assertTrue('kind of problem' in self.ui.msg_text)
3527 self.assertEqual(set(self.ui.msg_choices),
3528 set(['bar', 'foo does not work', 'Other problem']))
3529
3530@@ -2590,17 +2628,15 @@
3531 self.ui.question_choice_response = [self.ui.msg_choices.index('foo does not work')]
3532 self.assertEqual(self.ui.run_argv(), True)
3533 self.assertEqual(self.ui.msg_severity, None)
3534- self.assert_(self.ui.ic_progress_pulses > 0)
3535- self.assert_(self.ui.present_details_shown)
3536- self.assert_(self.ui.report['Package'].startswith('bash'))
3537+ self.assertTrue(self.ui.ic_progress_pulses > 0)
3538+ self.assertTrue(self.ui.present_details_shown)
3539+ self.assertTrue(self.ui.report['Package'].startswith('bash'))
3540
3541- def test_parse_argv(self):
3542+ def test_parse_argv_single_arg(self):
3543 '''parse_args() option inference for a single argument'''
3544
3545- def _chk(program_name, arg, expected_opts, argv_options=None):
3546+ def _chk(program_name, arg, expected_opts):
3547 sys.argv = [program_name]
3548- if argv_options:
3549- sys.argv += argv_options
3550 if arg:
3551 sys.argv.append(arg)
3552 orig_stderr = sys.stderr
3553@@ -2617,71 +2653,132 @@
3554 # no arguments -> show pending crashes
3555 _chk('apport-gtk', None, {'filebug': False, 'package': None,
3556 'pid': None, 'crash_file': None, 'symptom': None,
3557- 'update_report': None, 'save': None})
3558- # ... except when being called as '*-bug', then default to bug mode
3559- _chk('apport-bug', None, {'filebug': True, 'package': None,
3560- 'pid': None, 'crash_file': None, 'symptom': None,
3561- 'update_report': None, 'save': None})
3562+ 'update_report': None, 'save': None, 'tag': []})
3563 # updating report not allowed without args
3564 self.assertRaises(SystemExit, _chk, 'apport-collect', None, {})
3565
3566 # package
3567 _chk('apport-kde', 'coreutils', {'filebug': True, 'package':
3568 'coreutils', 'pid': None, 'crash_file': None, 'symptom': None,
3569- 'update_report': None, 'save': None})
3570- _chk('apport-bug', 'coreutils', {'filebug': True, 'package':
3571- 'coreutils', 'pid': None, 'crash_file': None, 'symptom': None,
3572- 'update_report': None, 'save': None})
3573- _chk('apport-bug', 'coreutils', {'filebug': True, 'package':
3574- 'coreutils', 'pid': None, 'crash_file': None, 'symptom': None,
3575- 'update_report': None, 'save': 'foo.apport'},
3576- ['--save', 'foo.apport'])
3577+ 'update_report': None, 'save': None, 'tag': []})
3578
3579 # symptom is preferred over package
3580 f = open(os.path.join(symptom_script_dir, 'coreutils.py'), 'w')
3581- print >> f, '''description = 'foo does not work'
3582+ f.write('''description = 'foo does not work'
3583 def run(report, ui):
3584 return 'bash'
3585-'''
3586+''')
3587 f.close()
3588 _chk('apport-cli', 'coreutils', {'filebug': True, 'package': None,
3589 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
3590- 'update_report': None, 'save': None})
3591- _chk('apport-bug', 'coreutils', {'filebug': True, 'package': None,
3592- 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
3593- 'update_report': None, 'save': None})
3594+ 'update_report': None, 'save': None, 'tag': []})
3595
3596 # PID
3597 _chk('apport-cli', '1234', {'filebug': True, 'package': None,
3598 'pid': '1234', 'crash_file': None, 'symptom': None,
3599- 'update_report': None, 'save': None})
3600- _chk('apport-bug', '1234', {'filebug': True, 'package': None,
3601- 'pid': '1234', 'crash_file': None, 'symptom': None,
3602- 'update_report': None, 'save': None})
3603+ 'update_report': None, 'save': None, 'tag': []})
3604
3605 # .crash/.apport files; check correct handling of spaces
3606 for suffix in ('.crash', '.apport'):
3607- for prog in ('apport-cli', 'apport-bug'):
3608- _chk(prog, '/tmp/f oo' + suffix, {'filebug': False,
3609- 'package': None, 'pid': None,
3610- 'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
3611- 'update_report': None, 'save': None})
3612+ _chk('apport-cli', '/tmp/f oo' + suffix, {'filebug': False,
3613+ 'package': None, 'pid': None,
3614+ 'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
3615+ 'update_report': None, 'save': None, 'tag': []})
3616
3617 # executable
3618 _chk('apport-cli', '/usr/bin/tail', {'filebug': True,
3619 'package': 'coreutils',
3620 'pid': None, 'crash_file': None, 'symptom': None,
3621- 'update_report': None, 'save': None})
3622- _chk('apport-bug', '/usr/bin/tail', {'filebug': True,
3623- 'package': 'coreutils',
3624- 'pid': None, 'crash_file': None, 'symptom': None,
3625- 'update_report': None, 'save': None})
3626+ 'update_report': None, 'save': None, 'tag': []})
3627
3628 # update existing report
3629 _chk('apport-collect', '1234', {'filebug': False, 'package': None,
3630- 'crash_file': None, 'symptom': None, 'update_report': 1234})
3631+ 'crash_file': None, 'symptom': None, 'update_report': 1234,
3632+ 'tag': []})
3633 _chk('apport-update-bug', '1234', {'filebug': False, 'package': None,
3634- 'crash_file': None, 'symptom': None, 'update_report': 1234})
3635+ 'crash_file': None, 'symptom': None, 'update_report': 1234,
3636+ 'tag': []})
3637+
3638+ def test_parse_argv_apport_bug(self):
3639+ '''parse_args() option inference when invoked as *-bug'''
3640+
3641+ def _chk(args, expected_opts):
3642+ sys.argv = ['apport-bug'] + args
3643+ orig_stderr = sys.stderr
3644+ sys.stderr = open('/dev/null', 'w')
3645+ try:
3646+ ui = UserInterface()
3647+ finally:
3648+ sys.stderr.close()
3649+ sys.stderr = orig_stderr
3650+ expected_opts['version'] = None
3651+ self.assertEqual(ui.args, [])
3652+ self.assertEqual(ui.options, expected_opts)
3653+
3654+ #
3655+ # no arguments: default to 'ask for symptom' bug mode
3656+ #
3657+ _chk([], {'filebug': True, 'package': None,
3658+ 'pid': None, 'crash_file': None, 'symptom': None,
3659+ 'update_report': None, 'save': None, 'tag': []})
3660+
3661+ #
3662+ # single arguments
3663+ #
3664+
3665+ # package
3666+ _chk(['coreutils'], {'filebug': True, 'package':
3667+ 'coreutils', 'pid': None, 'crash_file': None, 'symptom': None,
3668+ 'update_report': None, 'save': None, 'tag': []})
3669+
3670+ # symptom (preferred over package)
3671+ f = open(os.path.join(symptom_script_dir, 'coreutils.py'), 'w')
3672+ f.write('''description = 'foo does not work'
3673+def run(report, ui):
3674+ return 'bash'
3675+''')
3676+ f.close()
3677+ _chk(['coreutils'], {'filebug': True, 'package': None,
3678+ 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
3679+ 'update_report': None, 'save': None, 'tag': []})
3680+ os.unlink(os.path.join(symptom_script_dir, 'coreutils.py'))
3681+
3682+ # PID
3683+ _chk(['1234'], {'filebug': True, 'package': None,
3684+ 'pid': '1234', 'crash_file': None, 'symptom': None,
3685+ 'update_report': None, 'save': None, 'tag': []})
3686+
3687+ # .crash/.apport files; check correct handling of spaces
3688+ for suffix in ('.crash', '.apport'):
3689+ _chk(['/tmp/f oo' + suffix], {'filebug': False,
3690+ 'package': None, 'pid': None,
3691+ 'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
3692+ 'update_report': None, 'save': None, 'tag': []})
3693+
3694+ # executable name
3695+ _chk(['/usr/bin/tail'], {'filebug': True, 'package': 'coreutils',
3696+ 'pid': None, 'crash_file': None, 'symptom': None,
3697+ 'update_report': None, 'save': None, 'tag': []})
3698+
3699+ #
3700+ # supported options
3701+ #
3702+
3703+ # --save
3704+ _chk(['--save', 'foo.apport', 'coreutils'], {'filebug': True,
3705+ 'package': 'coreutils', 'pid': None, 'crash_file': None,
3706+ 'symptom': None, 'update_report': None, 'save': 'foo.apport',
3707+ 'tag': []})
3708+
3709+ # --tag
3710+ _chk(['--tag', 'foo', 'coreutils'], {'filebug': True,
3711+ 'package': 'coreutils', 'pid': None, 'crash_file': None,
3712+ 'symptom': None, 'update_report': None, 'save': None,
3713+ 'tag': ['foo']})
3714+ _chk(['--tag', 'foo', '--tag', 'bar', 'coreutils'], {
3715+ 'filebug': True, 'package': 'coreutils', 'pid': None,
3716+ 'crash_file': None, 'symptom': None, 'update_report': None,
3717+ 'save': None, 'tag': ['foo', 'bar']})
3718
3719 unittest.main()
3720
3721
3722=== modified file 'apport_python_hook.py'
3723--- apport_python_hook.py 2010-03-02 09:48:29 +0000
3724+++ apport_python_hook.py 2011-02-14 19:45:28 +0000
3725@@ -48,7 +48,11 @@
3726 if not enabled():
3727 return
3728
3729- from cStringIO import StringIO
3730+ try:
3731+ from cStringIO import StringIO
3732+ except ImportError:
3733+ from io import StringIO
3734+
3735 import re, tempfile, traceback
3736 from apport.fileutils import likely_packaged
3737
3738@@ -102,7 +106,7 @@
3739 # don't clobber existing report
3740 return
3741 report_file = os.fdopen(os.open(pr_filename,
3742- os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0600), 'w')
3743+ os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o600), 'w')
3744 try:
3745 pr.write(report_file)
3746 finally:
3747@@ -154,14 +158,14 @@
3748 func(42)
3749 ''' % extracode)
3750 os.close(fd)
3751- os.chmod(script, 0755)
3752+ os.chmod(script, 0o755)
3753
3754 p = subprocess.Popen([script, 'testarg1', 'testarg2'],
3755 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
3756 err = p.communicate()[1]
3757 self.assertEqual(p.returncode, 1,
3758 'crashing test python program exits with failure code')
3759- self.assert_('Exception: This should happen.' in err)
3760+ self.assertTrue('Exception: This should happen.' in err)
3761 self.failIf('OSError' in err, err)
3762 finally:
3763 os.unlink(script)
3764@@ -179,7 +183,7 @@
3765 try:
3766 self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
3767 self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
3768- 0600, 'report has correct permissions')
3769+ 0o600, 'report has correct permissions')
3770
3771 pr = problem_report.ProblemReport()
3772 pr.load(open(reports[0]))
3773@@ -192,13 +196,13 @@
3774 'Traceback', 'ProblemType', 'ProcEnviron', 'ProcStatus',
3775 'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps',
3776 'UserGroups']
3777- self.assert_(set(expected_keys).issubset(set(pr.keys())),
3778+ self.assertTrue(set(expected_keys).issubset(set(pr.keys())),
3779 'report has necessary fields')
3780- self.assert_('bin/python' in pr['InterpreterPath'])
3781+ self.assertTrue('bin/python' in pr['InterpreterPath'])
3782 self.assertEqual(pr['ExecutablePath'], script)
3783 self.assertEqual(pr['PythonArgs'], "['%s', 'testarg1', 'testarg2']" % script)
3784- self.assert_(pr['Traceback'].startswith('Traceback'))
3785- self.assert_("func\n raise Exception, 'This should happen." in pr['Traceback'])
3786+ self.assertTrue(pr['Traceback'].startswith('Traceback'))
3787+ self.assertTrue("func\n raise Exception, 'This should happen." in pr['Traceback'])
3788
3789 def test_existing(self):
3790 '''Python crash hook overwrites seen existing files.'''
3791@@ -212,7 +216,7 @@
3792 to_del.update(reports)
3793 self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
3794 self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
3795- 0600, 'report has correct permissions')
3796+ 0o600, 'report has correct permissions')
3797
3798 # touch report -> "seen" case
3799 apport.fileutils.mark_report_seen(reports[0])
3800@@ -246,7 +250,7 @@
3801 try:
3802 self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
3803 self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
3804- 0600, 'report has correct permissions')
3805+ 0o600, 'report has correct permissions')
3806
3807 pr = problem_report.ProblemReport()
3808 pr.load(open(reports[0]))
3809@@ -259,10 +263,10 @@
3810 'Traceback', 'ProblemType', 'ProcEnviron', 'ProcStatus',
3811 'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps',
3812 'UserGroups']
3813- self.assert_(set(expected_keys).issubset(set(pr.keys())),
3814+ self.assertTrue(set(expected_keys).issubset(set(pr.keys())),
3815 'report has necessary fields')
3816- self.assert_('bin/python' in pr['InterpreterPath'])
3817- self.assert_(pr['Traceback'].startswith('Traceback'))
3818+ self.assertTrue('bin/python' in pr['InterpreterPath'])
3819+ self.assertTrue(pr['Traceback'].startswith('Traceback'))
3820
3821 def _assert_no_reports(self):
3822 '''Assert that there are no crash reports.'''
3823@@ -310,7 +314,7 @@
3824 func(42)
3825 ''')
3826 os.close(fd)
3827- os.chmod(script, 0755)
3828+ os.chmod(script, 0o755)
3829
3830 # move aside current ignore file
3831 if os.path.exists(ifpath):
3832@@ -328,7 +332,7 @@
3833 err = p.communicate()[1]
3834 self.assertEqual(p.returncode, 1,
3835 'crashing test python program exits with failure code')
3836- self.assert_('Exception: This should happen.' in err)
3837+ self.assertTrue('Exception: This should happen.' in err)
3838
3839 finally:
3840 os.unlink(script)
3841
3842=== modified file 'backends/packaging-apt-dpkg.py'
3843--- backends/packaging-apt-dpkg.py 2010-09-02 22:17:44 +0000
3844+++ backends/packaging-apt-dpkg.py 2011-02-14 19:45:28 +0000
3845@@ -18,6 +18,7 @@
3846 warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)
3847 import apt
3848
3849+import apport
3850 from apport.packaging import PackageInfo
3851
3852 class __AptDpkgPackageInfo(PackageInfo):
3853@@ -28,8 +29,6 @@
3854 self._apt_cache = None
3855 self._contents_dir = None
3856 self._mirror = None
3857- # Checking whether we should use the API from python-apt 0.7.9
3858- self.apt_pre_079 = not hasattr(apt.Package, 'installed')
3859
3860 self.configuration = '/etc/default/apport'
3861
3862@@ -63,29 +62,21 @@
3863 try:
3864 return self._cache()[package]
3865 except KeyError:
3866- raise ValueError, 'package does not exist'
3867+ raise ValueError('package does not exist')
3868
3869 def get_version(self, package):
3870 '''Return the installed version of a package.'''
3871
3872 pkg = self._apt_pkg(package)
3873- if self.apt_pre_079:
3874- if not pkg.isInstalled:
3875- raise ValueError, 'package does not exist'
3876- return pkg.installedVersion
3877- else:
3878- inst = pkg.installed
3879- if not inst:
3880- raise ValueError, 'package does not exist'
3881- return inst.version
3882+ inst = pkg.installed
3883+ if not inst:
3884+ raise ValueError('package does not exist')
3885+ return inst.version
3886
3887 def get_available_version(self, package):
3888 '''Return the latest available version of a package.'''
3889
3890- if self.apt_pre_079:
3891- return self._apt_pkg(package).candidateVersion
3892- else:
3893- return self._apt_pkg(package).candidate.version
3894+ return self._apt_pkg(package).candidate.version
3895
3896 def get_dependencies(self, package):
3897 '''Return a list of packages a package depends on.'''
3898@@ -100,15 +91,12 @@
3899 def get_source(self, package):
3900 '''Return the source package name for a package.'''
3901
3902- if self.apt_pre_079:
3903- return self._apt_pkg(package).sourcePackageName
3904+ if self._apt_pkg(package).installed:
3905+ return self._apt_pkg(package).installed.source_name
3906+ elif self._apt_pkg(package).candidate:
3907+ return self._apt_pkg(package).candidate.source_name
3908 else:
3909- if self._apt_pkg(package).installed:
3910- return self._apt_pkg(package).installed.source_name
3911- elif self._apt_pkg(package).candidate:
3912- return self._apt_pkg(package).candidate.source_name
3913- else:
3914- raise ValueError, 'package %s does not exist' % package
3915+ raise ValueError('package %s does not exist' % package)
3916
3917 def is_distro_package(self, package):
3918 '''Check if a package is a genuine distro package (True) or comes from
3919@@ -121,12 +109,8 @@
3920
3921 pkg = self._apt_pkg(package)
3922 # some PPA packages have installed version None, see LP#252734
3923- if self.apt_pre_079:
3924- if pkg.isInstalled and pkg.installedVersion is None:
3925- return False
3926- else:
3927- if pkg.installed and pkg.installed.version is None:
3928- return False
3929+ if pkg.installed and pkg.installed.version is None:
3930+ return False
3931
3932 native_origins = [this_os]
3933 try:
3934@@ -139,10 +123,7 @@
3935 pass
3936
3937 origins = None
3938- if self.apt_pre_079:
3939- origins = pkg.candidateOrigin
3940- else:
3941- origins = pkg.candidate.origins
3942+ origins = pkg.candidate.origins
3943 if origins: # might be None
3944 for o in origins:
3945 if o.origin in native_origins:
3946@@ -155,15 +136,12 @@
3947 This might differ on multiarch architectures (e. g. an i386 Firefox
3948 package on a x86_64 system)'''
3949
3950- if self.apt_pre_079:
3951- return self._apt_pkg(package).architecture or 'unknown'
3952+ if self._apt_pkg(package).installed:
3953+ return self._apt_pkg(package).installed.architecture or 'unknown'
3954+ elif self._apt_pkg(package).candidate:
3955+ return self._apt_pkg(package).candidate.architecture or 'unknown'
3956 else:
3957- if self._apt_pkg(package).installed:
3958- return self._apt_pkg(package).installed.architecture or 'unknown'
3959- elif self._apt_pkg(package).candidate:
3960- return self._apt_pkg(package).candidate.architecture or 'unknown'
3961- else:
3962- raise ValueError, 'package %s does not exist' % package
3963+ raise ValueError('package %s does not exist' % package)
3964
3965 def get_files(self, package):
3966 '''Return list of files shipped by a package.'''
3967@@ -197,11 +175,11 @@
3968 try:
3969 # ignore lines with NUL bytes (happens, LP#96050)
3970 if '\0' in line:
3971- print >> sys.stderr, 'WARNING:', sumfile, 'contains NUL character, ignoring line'
3972+ apport.warning('%s contains NUL character, ignoring line', sumfile)
3973 continue
3974 words = line.split()
3975 if not words:
3976- print >> sys.stderr, 'WARNING:', sumfile, 'contains empty line, ignoring line'
3977+ apport.warning('%s contains empty line, ignoring line', sumfile)
3978 continue
3979 s = os.stat('/' + words[-1])
3980 if max(s.st_mtime, s.st_ctime) <= max_time:
3981@@ -364,7 +342,7 @@
3982 debug_pkgname = 'linux-image-debug-%s' % kver
3983 c = self._cache()
3984 if c.has_key(debug_pkgname) and c[debug_pkgname].isInstalled:
3985- #print 'kernel ddeb already installed'
3986+ #print('kernel ddeb already installed')
3987 return (installed, outdated)
3988 target_dir = apt_pkg.Config.FindDir('Dir::Cache::archives')+'/partial'
3989 deb = '%s_%s_%s.ddeb' % (debug_pkgname, ver, arch)
3990@@ -414,16 +392,16 @@
3991 else:
3992 c.update()
3993 c.open(apt.progress.OpProgress())
3994- except SystemError, e:
3995+ except SystemError as e:
3996 if 'Hash Sum mismatch' in str(e):
3997 # temporary archive inconsistency
3998- print >> sys.stderr, str(e), 'aborting'
3999+ apport.error('%s, aborting' % str(e))
4000 sys.exit(99) # signal crash digger about transient error
4001 else:
4002 raise
4003 except apt.cache.LockFailedException:
4004 if os.geteuid() != 0:
4005- print >> sys.stderr, 'WARNING: Could not update apt, you need to be root'
4006+ apport.error('Could not update apt, you need to be root')
4007 else:
4008 raise
4009
4010@@ -439,42 +417,35 @@
4011 try:
4012 (pkg, version) = l.split()[:2]
4013 except ValueError:
4014- print >> sys.stderr, 'WARNING: invalid Package/Dependencies line: ', l
4015+ apport.warning('invalid Package/Dependencies line: %s', l)
4016 # invalid line, ignore
4017 continue
4018 dependency_versions[pkg] = version
4019 try:
4020 if self.get_architecture(pkg) != 'all':
4021+ dependency_versions[pkg+'-dbg'] = dependency_versions[pkg]
4022 dependency_versions[pkg+'-dbgsym'] = dependency_versions[pkg]
4023 except ValueError:
4024- print >> sys.stderr, 'WARNING: package %s not known to package cache' % pkg
4025+ apport.warning('package %s not known to package cache', pkg)
4026
4027 for pkg, ver in dependency_versions.iteritems():
4028 if not c.has_key(pkg):
4029- print >> sys.stderr, 'WARNING: package %s not available' % pkg
4030+ apport.warning('package %s not available', pkg)
4031 continue
4032
4033 # ignore packages which are already installed in the right version
4034- if self.apt_pre_079:
4035- if (ver and c[pkg].isInstalled and c[pkg].installedVersion ==\
4036- ver) or (not ver and c[pkg].isInstalled):
4037- continue
4038- else:
4039- if (ver and c[pkg].installed and c[pkg].installed.version ==\
4040- ver) or (not ver and c[pkg].installed):
4041- continue
4042+ if (ver and c[pkg].installed and c[pkg].installed.version ==\
4043+ ver) or (not ver and c[pkg].installed):
4044+ continue
4045
4046 candidate_version = None
4047- if self.apt_pre_079:
4048- candidate_version = c[pkg].candidateVersion
4049- else:
4050- candidate_version = c[pkg].candidate.version
4051+ candidate_version = c[pkg].candidate.version
4052 if ver and candidate_version != ver:
4053 if not pkg.endswith('-dbgsym'):
4054 outdated += '%s: installed version %s, latest version: %s\n' % (
4055 pkg, ver, candidate_version)
4056- print >> sys.stderr, 'WARNING: %s version %s required, but %s is available' % (
4057- pkg, ver, candidate_version)
4058+ apport.warning('%s version %s required, but %s is available',
4059+ pkg, ver, candidate_version)
4060 if not unpack_only:
4061 uninstallable.append (c[pkg].name)
4062 continue
4063@@ -501,15 +472,14 @@
4064 try:
4065 c.commit(fetchProgress, installProgress)
4066 except SystemError:
4067- print >> sys.stderr, 'Error: Could not install all archives. If you use this tool on a production system, it is recommended to use the -u option. See --help for details.'
4068- sys.exit(1)
4069+ apport.fatal('Could not install all archives. If you use this tool on a production system, it is recommended to use the -u option. See --help for details.')
4070
4071 # after commit(), the Cache object does not empty the pending
4072 # changes, so we need to reinitialize it to avoid applying the same
4073 # changes again below
4074 installed = [p.name for p in c.getChanges()]
4075 c = apt.Cache()
4076- except IOError, e:
4077+ except IOError as e:
4078 pass # we will complain to the user later
4079
4080 # package hooks might reassign Package:, check that we have the originally crashing binary
4081@@ -517,12 +487,12 @@
4082 if path in report and not os.path.exists(report[path]):
4083 pkg = self.get_file_package(report[path], True)
4084 if pkg:
4085- print 'Installing extra package', pkg, 'to get', path
4086+ print('Installing extra package %s to get %s' % (pkg, path))
4087 c[pkg].markInstall(False)
4088 else:
4089 err = 'current version of package %s does not contain the program %s any more' % (report['Package'], path)
4090 outdated += err + '\n'
4091- print >> sys.stderr, 'WARNING:', err
4092+ apport.warning(err)
4093
4094 # check list of libraries that the crashed process referenced at
4095 # runtime and warn about those which are not available
4096@@ -532,7 +502,7 @@
4097 if not l.strip():
4098 continue
4099 cols = l.split()
4100- if 'x' in cols[1] and len(cols) == 6 and '.so' in cols[5]:
4101+ if len(cols) == 6 and 'x' in cols[1] and '.so' in cols[5]:
4102 lib = os.path.realpath(cols[5])
4103 libs.add(lib)
4104
4105@@ -545,19 +515,19 @@
4106 if pkg:
4107 if not os.path.exists(l):
4108 if pkg in uninstallable:
4109- print >> sys.stderr, 'WARNING: %s cannot be installed (incompatible version)' % pkg
4110+ apport.warning('%s cannot be installed (incompatible version)', pkg)
4111 continue
4112 if c.has_key(pkg):
4113 c[pkg].markInstall(False)
4114 else:
4115- print >> sys.stderr, 'WARNING: %s was loaded at runtime, but its package %s is not available' % (l, pkg)
4116+ apport.warning('%s was loaded at runtime, but its package %s is not available', l, pkg)
4117
4118 if c.has_key(pkg+'-dbgsym') and pkg+'-dbgsym' not in uninstallable :
4119 c[pkg+'-dbgsym'].markInstall(False)
4120 else:
4121- print >> sys.stderr, 'WARNING: %s-dbgsym is not available or is incompatible' % pkg
4122+ apport.warning('%s-dbgsym is not available or is incompatible', pkg)
4123 else:
4124- print >> sys.stderr, 'WARNING: %s is needed, but cannot be mapped to a package' % l
4125+ apport.warning('%s is needed, but cannot be mapped to a package', l)
4126
4127 try:
4128 if c.getChanges():
4129@@ -567,17 +537,12 @@
4130 else:
4131 c.commit(fetchProgress, installProgress)
4132 installed += [p.name for p in c.getChanges()]
4133- except (SystemError, IOError), e:
4134- print >> sys.stderr, 'WARNING: could not install missing packages:', e
4135+ except (SystemError, IOError) as e:
4136+ apport.warning('could not install missing packages: %s', str(e))
4137 if os.geteuid() != 0:
4138- print >> sys.stderr, 'You either need to call this program as root or install these packages manually:'
4139+ apport.error('You either need to call this program as root or install these packages manually:')
4140 for p in c.getChanges():
4141- if self.apt_pre_079:
4142- print >> sys.stderr, ' %s %s' % (p.name,
4143- p.candidateVersion)
4144- else:
4145- print >> sys.stderr, ' %s %s' % (p.name,
4146- p.candidate.version)
4147+ apport.error(' %s %s', p.name, p.candidate.version)
4148
4149 return (installed, outdated)
4150
4151@@ -612,7 +577,7 @@
4152 if dpkg.returncode == 0:
4153 return out
4154 else:
4155- raise ValueError, 'package does not exist'
4156+ raise ValueError('package does not exist')
4157
4158 def _check_files_md5(self, sumfile):
4159 '''Internal function for calling md5sum.
4160@@ -655,7 +620,7 @@
4161 self._mirror = fields[1]
4162 break
4163 else:
4164- raise SystemError, 'cannot determine default mirror: /etc/apt/sources.list does not contain a valid deb line'
4165+ raise SystemError('cannot determine default mirror: /etc/apt/sources.list does not contain a valid deb line')
4166
4167 return self._mirror
4168
4169@@ -794,8 +759,8 @@
4170 pm = apt.apt_pkg.GetPackageManager(cache._depcache)
4171 try:
4172 res = cache._fetchArchives(fetcher, pm)
4173- except IOError, e:
4174- print >> sys.stderr, 'ERROR: could not fetch all archives:', e
4175+ except IOError as e:
4176+ apport.error('could not fetch all archives: %s', str(e))
4177
4178 # extract
4179 if verbosity:
4180@@ -805,15 +770,15 @@
4181 if no_dpkg:
4182 for i in fetcher.Items:
4183 if verbosity:
4184- print 'Extracting', i.DestFile
4185+ print('Extracting ' + i.DestFile)
4186 if subprocess.call(['dpkg', '-x', i.DestFile, '/'], stdout=so,
4187 stderr=subprocess.STDOUT) != 0:
4188- print >> sys.stderr, 'WARNING: %s failed to extract' % i.DestFile
4189+ apport.warning('%s failed to extract', i.DestFile)
4190 else:
4191 res = subprocess.call(['dpkg', '--force-depends', '--force-overwrite', '--unpack'] +
4192 [klass.deb_without_preinst(i.DestFile) for i in fetcher.Items], stdout=so)
4193 if res != 0:
4194- raise IOError, 'dpkg failed to unpack archives'
4195+ raise IOError('dpkg failed to unpack archives')
4196
4197 # remove other maintainer scripts
4198 for c in cache.getChanges():
4199@@ -874,14 +839,14 @@
4200 def test_get_version(self):
4201 '''get_version().'''
4202
4203- self.assert_(impl.get_version('libc6').startswith('2'))
4204+ self.assertTrue(impl.get_version('libc6').startswith('2'))
4205 self.assertRaises(ValueError, impl.get_version, 'nonexisting')
4206 self.assertRaises(ValueError, impl.get_version, 'wukrainian')
4207
4208 def test_get_available_version(self):
4209 '''get_available_version().'''
4210
4211- self.assert_(impl.get_available_version('libc6').startswith('2'))
4212+ self.assertTrue(impl.get_available_version('libc6').startswith('2'))
4213 self.assertRaises(ValueError, impl.get_available_version, 'nonexisting')
4214
4215 def test_get_dependencies(self):
4216@@ -889,36 +854,36 @@
4217
4218 # package with both Depends: and Pre-Depends:
4219 d = impl.get_dependencies('bash')
4220- self.assert_(len(d) > 2)
4221- self.assert_('libc6' in d)
4222+ self.assertTrue(len(d) > 2)
4223+ self.assertTrue('libc6' in d)
4224 for dep in d:
4225- self.assert_(impl.get_version(dep))
4226+ self.assertTrue(impl.get_version(dep))
4227
4228 # Pre-Depends: only
4229 d = impl.get_dependencies('coreutils')
4230- self.assert_(len(d) >= 1)
4231- self.assert_('libc6' in d)
4232+ self.assertTrue(len(d) >= 1)
4233+ self.assertTrue('libc6' in d)
4234 for dep in d:
4235- self.assert_(impl.get_version(dep))
4236+ self.assertTrue(impl.get_version(dep))
4237
4238 # Depends: only
4239 d = impl.get_dependencies('libc6')
4240- self.assert_(len(d) >= 1)
4241+ self.assertTrue(len(d) >= 1)
4242 for dep in d:
4243- self.assert_(impl.get_version(dep))
4244+ self.assertTrue(impl.get_version(dep))
4245
4246 def test_get_source(self):
4247 '''get_source().'''
4248
4249 self.assertRaises(ValueError, impl.get_source, 'nonexisting')
4250 self.assertEqual(impl.get_source('bash'), 'bash')
4251- self.assert_('glibc' in impl.get_source('libc6'))
4252+ self.assertTrue('glibc' in impl.get_source('libc6'))
4253
4254 def test_is_distro_package(self):
4255 '''is_distro_package().'''
4256
4257 self.assertRaises(ValueError, impl.is_distro_package, 'nonexisting')
4258- self.assert_(impl.is_distro_package('bash'))
4259+ self.assertTrue(impl.is_distro_package('bash'))
4260 # no False test here, hard to come up with a generic one
4261
4262 def test_get_architecture(self):
4263@@ -936,7 +901,7 @@
4264 '''get_files().'''
4265
4266 self.assertRaises(ValueError, impl.get_files, 'nonexisting')
4267- self.assert_('/bin/bash' in impl.get_files('bash'))
4268+ self.assertTrue('/bin/bash' in impl.get_files('bash'))
4269
4270 def test_get_file_package(self):
4271 '''get_file_package() on installed files.'''
4272@@ -959,14 +924,16 @@
4273 try:
4274 mapdir = os.path.join(basedir, 'dists', release_name)
4275 os.makedirs(mapdir)
4276- print >> gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
4277- impl.get_system_architecture()), 'w'), '''
4278+ f = gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
4279+ impl.get_system_architecture()), 'w')
4280+ f.write('''
4281 foo header
4282 FILE LOCATION
4283 usr/bin/frobnicate foo/frob
4284 usr/bin/frob foo/frob-utils
4285 bo/gu/s na/mypackage
4286-'''
4287+''')
4288+ f.close()
4289
4290 self.assertEqual(impl.get_file_package('usr/bin/frob', False, mapdir), None)
4291 # must not match frob (same file name prefix)
4292@@ -987,7 +954,7 @@
4293 os.mkdir(cache_dir)
4294 self.assertEqual(impl.get_file_package('usr/bin/frob', True, cache_dir), 'frob-utils')
4295 self.assertEqual(len(os.listdir(cache_dir)), 1)
4296- self.assert_(os.listdir(cache_dir)[0].startswith('Contents-'))
4297+ self.assertTrue(os.listdir(cache_dir)[0].startswith('Contents-'))
4298 self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')
4299 finally:
4300 shutil.rmtree(basedir)
4301@@ -1013,7 +980,7 @@
4302 arch = impl.get_system_architecture()
4303 # must be nonempty without line breaks
4304 self.assertNotEqual(arch, '')
4305- self.assert_('\n' not in arch)
4306+ self.assertTrue('\n' not in arch)
4307
4308 def test_compare_versions(self):
4309 '''compare_versions.'''
4310@@ -1055,13 +1022,13 @@
4311 def test_get_kernel_pacakge(self):
4312 '''get_kernel_package().'''
4313
4314- self.assert_('linux' in impl.get_kernel_package())
4315+ self.assertTrue('linux' in impl.get_kernel_package())
4316
4317 def test_package_name_glob(self):
4318 '''package_name_glob().'''
4319
4320- self.assert_(len(impl.package_name_glob('a*')) > 5)
4321- self.assert_('bash' in impl.package_name_glob('ba*h'))
4322+ self.assertTrue(len(impl.package_name_glob('a*')) > 5)
4323+ self.assertTrue('bash' in impl.package_name_glob('ba*h'))
4324 self.assertEqual(impl.package_name_glob('bash'), ['bash'])
4325 self.assertEqual(impl.package_name_glob('xzywef*'), [])
4326
4327
4328=== modified file 'bin/apport-cli'
4329--- bin/apport-cli 2010-03-18 20:55:30 +0000
4330+++ bin/apport-cli 2011-02-14 19:45:28 +0000
4331@@ -35,7 +35,8 @@
4332 def raw_input_char(self, prompt):
4333 '''raw_input, but read only one character'''
4334
4335- print >> sys.stdout, prompt,
4336+ sys.stdout.write(prompt)
4337+ sys.stdout.write(' ')
4338
4339 file = sys.stdin.fileno()
4340 saved_attributes = termios.tcgetattr(file)
4341@@ -50,20 +51,20 @@
4342 finally:
4343 termios.tcsetattr(file, termios.TCSANOW, saved_attributes)
4344
4345- print
4346+ sys.stdout.write('\n')
4347 return ch
4348
4349 def show(self):
4350 self.visible = True
4351- print self.heading
4352+ print(self.heading)
4353 if self.text:
4354- print self.text
4355+ print(self.text)
4356
4357 def run(self, prompt=None):
4358 if not self.visible:
4359 self.show()
4360
4361- print
4362+ sys.stdout.write('\n')
4363 try:
4364 # Only one button
4365 if len (self.keys) <= 1:
4366@@ -72,11 +73,11 @@
4367 # Multiple choices
4368 while True:
4369 if prompt is not None:
4370- print prompt
4371+ print(prompt)
4372 else:
4373- print _('What would you like to do? Your options are:')
4374+ print(_('What would you like to do? Your options are:'))
4375 for index, button in enumerate(self.buttons):
4376- print ' %s: %s' % (self.keys[index], button)
4377+ print(' %s: %s' % (self.keys[index], button))
4378
4379 response = self.raw_input_char(_('Please choose (%s):') % ('/'.join(self.keys)))
4380 try:
4381@@ -84,7 +85,7 @@
4382 except ValueError:
4383 pass
4384 except KeyboardInterrupt:
4385- print
4386+ sys.stdout.write('\n')
4387 sys.exit(1)
4388
4389 def addbutton(self, button, hotkey=None):
4390@@ -130,7 +131,7 @@
4391 # adapt dialog heading and label appropriately
4392 if desktop_entry:
4393 name = desktop_entry.getName()
4394- elif self.report.has_key('ExecutablePath'):
4395+ elif 'ExecutablePath' in self.report:
4396 name = os.path.basename(self.report['ExecutablePath'])
4397 else:
4398 name = self.cur_package
4399@@ -174,7 +175,7 @@
4400 def ui_present_kernel_error(self):
4401 message = _('Your system encountered a serious kernel problem.')
4402 annotation = ''
4403- if self.report.has_key('Annotation'):
4404+ if 'Annotation' in self.report:
4405 annotation += self.report['Annotation'] + '\n\n'
4406 annotation += _('You can help the developers to fix the problem by reporting it.')
4407
4408@@ -226,7 +227,7 @@
4409 'automatically opened web browser.'))
4410
4411 # complete/reduced reports
4412- if self.report.has_key('CoreDump') and self.report.has_useful_stacktrace():
4413+ if 'CoreDump' in self.report and self.report.has_useful_stacktrace():
4414 complete = dialog.addbutton(_('&Send complete report (recommended; %s)') %
4415 self.format_filesize(self.get_complete_size()))
4416 reduced = dialog.addbutton(_('Send &reduced report (slow Internet connection; %s)') %
4417@@ -253,7 +254,7 @@
4418 subprocess.Popen(["/usr/bin/sensible-pager"],
4419 stdin=subprocess.PIPE,
4420 close_fds=True).communicate(self._get_details())
4421- except IOError, e:
4422+ except IOError as e:
4423 # ignore broken pipe (premature quit)
4424 if e.errno == errno.EPIPE:
4425 pass
4426@@ -264,12 +265,12 @@
4427 # we do not already have a report file if we report a bug
4428 if not self.report_file:
4429 prefix = 'apport.'
4430- if self.report.has_key('Package'):
4431+ if 'Package' in self.report:
4432 prefix += self.report['Package'].split()[0] + '.'
4433 (fd, self.report_file) = tempfile.mkstemp(prefix=prefix, suffix='.apport')
4434 self.report.write(os.fdopen(fd, 'w'))
4435
4436- print _('Problem report file:'), self.report_file
4437+ print(_('Problem report file:') + ' ' + self.report_file)
4438 return 'cancel'
4439
4440 # Fallback
4441@@ -296,7 +297,7 @@
4442 self.progress.set()
4443
4444 def ui_stop_info_collection_progress(self):
4445- print
4446+ sys.stdout.write('\n')
4447
4448 def ui_start_upload_progress(self):
4449 self.progress = CLIProgressDialog (
4450@@ -309,7 +310,7 @@
4451 self.progress.set(progress)
4452
4453 def ui_stop_upload_progress(self):
4454- print
4455+ sys.stdout.write('\n')
4456
4457 def ui_question_yesno(self, text):
4458 '''Show a yes/no question.
4459@@ -386,16 +387,17 @@
4460
4461 Return path if the user selected a file, or None if cancelled.
4462 '''
4463- print '\n*** ', text
4464+ print('\n*** ' + text)
4465 while True:
4466- print _('Path to file (Enter to cancel):'), ' ',
4467+ sys.stdout.write(_('Path to file (Enter to cancel):'))
4468+ sys.stdout.write(' ')
4469 f = sys.stdin.readline().strip()
4470 if not f:
4471 return None
4472 if not os.path.exists(f):
4473- print _('File does not exist.')
4474+ print(_('File does not exist.'))
4475 elif os.path.isdir(f):
4476- print _('This is a directory.')
4477+ print(_('This is a directory.'))
4478 else:
4479 return f
4480
4481@@ -412,4 +414,4 @@
4482 if __name__ == '__main__':
4483 app = CLIUserInterface()
4484 if not app.run_argv():
4485- print >> sys.stderr, _('No pending crash reports. Try --help for more information.')
4486+ print(_('No pending crash reports. Try --help for more information.'))
4487
4488=== modified file 'bin/apport-retrace'
4489--- bin/apport-retrace 2010-06-01 07:48:01 +0000
4490+++ bin/apport-retrace 2011-02-14 19:45:28 +0000
4491@@ -201,18 +201,17 @@
4492 try:
4493 report = apport.Report()
4494 report.load(open(reportfile))
4495- except (MemoryError, TypeError, ValueError, IOError, zlib.error), e:
4496- print >> sys.stderr, 'Cannot open report file:', e
4497- sys.exit(1)
4498+ except (MemoryError, TypeError, ValueError, IOError, zlib.error) as e:
4499+ apport.fatal('Cannot open report file: %s', str(e))
4500 elif reportfile.isdigit():
4501 # crash ID
4502 try:
4503 report = crashdb.download(int(reportfile))
4504- except (MemoryError, TypeError, ValueError, IOError, zlib.error), e:
4505+ except (MemoryError, TypeError, ValueError, IOError, zlib.error) as e:
4506 # if we process the report automatically, and it is invalid, close it with
4507 # an informative message and exit cleanly to not break crash-digger
4508 if options.auth_file and not options.output and not options.stdout:
4509- print >> sys.stderr, 'Broken report:', str(e), 'closing as invalid'
4510+ apport.error('Broken report: %s, closing as invalid', str(e))
4511 crashdb.mark_retrace_failed(reportfile, '''Thank you for your report!
4512
4513 However, processing it in order to get sufficient information for the
4514@@ -232,9 +231,8 @@
4515 crashid = reportfile
4516 reportfile = None
4517 else:
4518- print >> sys.stderr, 'ERROR: "%s" is neither an existing report file nor a \
4519-crash ID' % reportfile
4520- sys.exit(1)
4521+ apport.fatal('"%s" is neither an existing report file nor a crash ID',
4522+ reportfile)
4523
4524 if options.core_file:
4525 report['CoreDump'] = file(options.core_file).read()
4526@@ -249,11 +247,11 @@
4527 required_fields = set(['CoreDump', 'ExecutablePath', 'Package'])
4528 if report['ProblemType'] == 'KernelCrash':
4529 if not set(['Package','VmCore']).issubset(set(report.keys())):
4530- print >> sys.stderr, 'report file does not contain the required fields'
4531+ apport.error('report file does not contain the required fields')
4532 sys.exit(0)
4533 elif not required_fields.issubset(set(report.keys())):
4534- print >> sys.stderr, 'report file does not contain one of the required fields: ' + \
4535- ' '.join(required_fields)
4536+ apport.error('report file does not contain one of the required fields: ' + \
4537+ ' '.join(required_fields))
4538 sys.exit(0)
4539
4540 (installed, outdated_msg) = apport.packaging.install_retracing_packages(report,
4541@@ -298,8 +296,7 @@
4542 if modified:
4543 if not reportfile and not options.output:
4544 if not options.auth_file:
4545- print >> sys.stderr, 'You need to specify --auth for uploading retraced results back to the crash database.'
4546- sys.exit(1)
4547+ apport.fatal('You need to specify --auth for uploading retraced results back to the crash database.')
4548 if not options.confirm or confirm_traces(report):
4549 # check for duplicates
4550 update_bug = True
4551
4552=== modified file 'bin/apport-unpack'
4553--- bin/apport-unpack 2009-09-08 12:03:44 +0000
4554+++ bin/apport-unpack 2011-02-14 19:45:28 +0000
4555@@ -30,13 +30,11 @@
4556 try:
4557 if os.path.isdir(dir):
4558 if os.listdir(dir):
4559- print >> sys.stderr, _('Destination directory exists and is not empty.')
4560- sys.exit(1)
4561+ apport.fatal(_('Destination directory exists and is not empty.'))
4562 else:
4563 os.mkdir(dir)
4564 except OSError, e:
4565- print >> sys.stderr, e
4566- sys.exit(1)
4567+ apport.fatal(str(e))
4568
4569 pr = problem_report.ProblemReport()
4570 if report == '-':
4571@@ -45,7 +43,6 @@
4572 try:
4573 pr.load(open(report))
4574 except IOError, e:
4575- print >> sys.stderr, e
4576- sys.exit(1)
4577+ apport.fatal(str(e))
4578 for k in pr:
4579 open(os.path.join(dir, k), 'w').write(pr[k])
4580
4581=== modified file 'bin/crash-digger'
4582--- bin/crash-digger 2009-12-23 10:55:52 +0000
4583+++ bin/crash-digger 2011-02-14 19:45:28 +0000
4584@@ -11,6 +11,7 @@
4585
4586 import os, time, optparse, subprocess, sys, signal, zlib, errno, stat, shutil
4587
4588+import apport
4589 from apport.crashdb import get_crashdb
4590
4591 # TODO: permanently save a set for crashes which aren't for our chroot map
4592@@ -34,16 +35,14 @@
4593
4594 self.log('Initializing crash digger, using chroot map %s' % self.chroot_map)
4595
4596- # read chroot map, verify it, and get available releases
4597+ # read chroot map, verify it, and get available releases
4598 if self.chroot_map:
4599 now = time.time()
4600 outdated_chroots = False
4601 m = eval(open(self.chroot_map).read(), {}, {})
4602 for r, chroot in m.iteritems():
4603 if not os.path.exists(chroot):
4604- print >> sys.stderr, 'Error: chroot %s for %s does not exist' % \
4605- (chroot, r)
4606- sys.exit(1)
4607+ apport.fatal('Error: chroot %s for %s does not exist', chroot, r)
4608 st = os.stat(chroot)
4609 if now - st.st_mtime > 86400 and not stat.S_ISDIR(st.st_mode):
4610 outdated_chroots = True
4611@@ -56,7 +55,7 @@
4612 'upgrade', 'all'], stdout=sys.stdout,
4613 stderr=subprocess.STDOUT) == 0
4614
4615- self.crashdb = get_crashdb(auth_file)
4616+ self.crashdb = get_crashdb(auth_file)
4617
4618 if self.dup_db:
4619 self.crashdb.init_duplicate_db(self.dup_db)
4620@@ -68,7 +67,7 @@
4621 the current date and time.'''
4622
4623 if self.verbose:
4624- print >> sys.stdout, '%s: %s' % (time.strftime('%x %X'), str)
4625+ sys.stdout.write('%s: %s\n' % (time.strftime('%x %X'), str))
4626 sys.stdout.flush()
4627
4628 def fill_pool(self):
4629@@ -98,13 +97,13 @@
4630 try:
4631 rel = self.crashdb.get_distro_release(id)
4632 except ValueError:
4633- self.log('could not determine release -- no DistroRelease field?')
4634+ self.log('could not determine release -- no DistroRelease field?')
4635 self.crashdb.mark_retraced(id)
4636- return
4637- if rel not in self.releases:
4638- self.log('crash is release %s which does not have a chroot available, skipping' % rel)
4639+ return
4640+ if rel not in self.releases:
4641+ self.log('crash is release %s which does not have a chroot available, skipping' % rel)
4642 # TODO: self.no_chroot_pool.add(id)
4643- return
4644+ return
4645
4646 argv = ['apport-chroot', '-m', self.chroot_map, '--auth',
4647 self.auth_file]
4648@@ -120,7 +119,7 @@
4649 self.retrace_pool = set()
4650 self.log('transient error reported; halting')
4651 return
4652- raise SystemError, 'retracing #%i failed' % id
4653+ raise SystemError('retracing #%i failed' % id)
4654
4655 self.crashdb.mark_retraced(id)
4656
4657@@ -132,9 +131,9 @@
4658
4659 try:
4660 report = self.crashdb.download(id)
4661- except (MemoryError, TypeError, ValueError, IOError, zlib.error), e:
4662+ except (MemoryError, TypeError, ValueError, IOError, zlib.error) as e:
4663 self.log('Cannot download report: ' + str(e))
4664- print >> sys.stderr, 'Cannot download report %i:' % id, str(e)
4665+ apport.error('Cannot download report %i: %s', id, str(e))
4666 return
4667
4668 res = self.crashdb.check_duplicate(id, report)
4669@@ -189,11 +188,9 @@
4670 (opts, args) = optparser.parse_args()
4671
4672 if not opts.chroot_map and not opts.dupcheck_mode:
4673- print >> sys.stderr, 'Error: --chroot-map or --dupcheck needs to be given'
4674- sys.exit(1)
4675+ apport.fatal('Error: --chroot-map or --dupcheck needs to be given')
4676 if not opts.auth_file:
4677- print >> sys.stderr, 'Error: -a/--auth needs to be given'
4678- sys.exit(1)
4679+ apport.fatal('Error: -a/--auth needs to be given')
4680
4681 return (opts, args)
4682
4683@@ -205,9 +202,9 @@
4684
4685 if opts.lockfile:
4686 try:
4687- f = os.open(opts.lockfile, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0666)
4688+ f = os.open(opts.lockfile, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o666)
4689 os.close(f)
4690- except OSError, e:
4691+ except OSError as e:
4692 if e.errno == errno.EEXIST:
4693 sys.exit(0)
4694 else:
4695
4696=== modified file 'bin/dupdb-admin'
4697--- bin/dupdb-admin 2009-12-23 10:55:52 +0000
4698+++ bin/dupdb-admin 2011-02-14 19:45:28 +0000
4699@@ -14,6 +14,7 @@
4700 import optparse, sys, os.path
4701
4702 from apport.crashdb import get_crashdb
4703+import apport
4704
4705 def command_status(crashdb, opts, args):
4706 '''Give general status.'''
4707@@ -39,8 +40,7 @@
4708 '''Change the master ID of a crash.'''
4709
4710 if len(args) != 2:
4711- print >> sys.stderr, 'changeid needs exactly two arguments (use --help for a short help)'
4712- sys.exit(1)
4713+ apport.fatal('changeid needs exactly two arguments (use --help for a short help)')
4714 (oldid, newid) = args
4715
4716 crashdb.duplicate_db_change_master_id(oldid, newid)
4717@@ -64,8 +64,7 @@
4718 options, args = optparser.parse_args()
4719
4720 if not os.path.exists(options.db_file):
4721- print >> sys.stderr, 'file does not exist:', options.db_file
4722- sys.exit(1)
4723+ apport.fatal('file does not exist: %s', options.db_file)
4724
4725 crashdb = get_crashdb(None, None, {})
4726 crashdb.init_duplicate_db(options.db_file)
4727@@ -73,7 +72,7 @@
4728 try:
4729 command = globals()['command_' + args.pop(0)]
4730 except KeyError:
4731- print >> sys.stderr, 'unknown command (use --help for a short help)'
4732- sys.exit(1)
4733+ apport.fatal('unknown command (use --help for a short help)')
4734+
4735 command(crashdb, options, args)
4736
4737
4738=== modified file 'data/apport'
4739--- data/apport 2010-03-31 10:22:53 +0000
4740+++ data/apport 2011-02-14 19:45:28 +0000
4741@@ -34,7 +34,7 @@
4742 lockfile = os.path.join(apport.fileutils.report_dir, '.lock')
4743 try:
4744 fd = os.open(lockfile, os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW)
4745- except OSError, e:
4746+ except OSError as e:
4747 error_log('cannot create lock file (uid %i): %s' % (os.getuid(), str(e)))
4748 sys.exit(1)
4749
4750@@ -50,8 +50,8 @@
4751 stat = None
4752 try:
4753 stat = os.stat('/proc/' + pid)
4754- except OSError, e:
4755- raise ValueError, 'Invalid process ID: ' + str(e)
4756+ except OSError as e:
4757+ raise ValueError('Invalid process ID: ' + str(e))
4758
4759 if partial:
4760 effective_gid = os.getegid()
4761@@ -73,11 +73,11 @@
4762 if not os.isatty(sys.stderr.fileno()):
4763 log = os.environ.get('APPORT_LOG_FILE', '/var/log/apport.log')
4764 try:
4765- f = os.open(log, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0600)
4766+ f = os.open(log, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0o600)
4767 try:
4768 admgid = grp.getgrnam('adm')[2]
4769 os.chown(log, -1, admgid)
4770- os.chmod(log, 0640)
4771+ os.chmod(log, 0o640)
4772 except KeyError:
4773 pass # if group adm doesn't exist, just leave it as root
4774 except OSError: # on a permission error, don't touch stderr
4775@@ -89,8 +89,7 @@
4776 def error_log(msg):
4777 '''Output something to the error log.'''
4778
4779- print >> sys.stderr, 'apport (pid %s) %s:' % (os.getpid(),
4780- time.asctime()), msg
4781+ apport.error('apport (pid %s) %s: %s', os.getpid(), time.asctime(), msg)
4782
4783 def _log_signal_handler(sgn, frame):
4784 '''Internal apport signal handler. Just log the signal handler and exit.'''
4785@@ -142,7 +141,7 @@
4786 try:
4787 if open('/proc/sys/kernel/core_uses_pid').read().strip() != '0':
4788 core_path += '.' + str(pid)
4789- core_file = os.open(core_path, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0600)
4790+ core_file = os.open(core_path, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o600)
4791 except (OSError, IOError):
4792 return
4793
4794@@ -196,8 +195,8 @@
4795
4796 if len(sys.argv) != 4:
4797 try:
4798- print >> sys.stderr, 'Usage:', sys.argv[0], '<pid> <signal number> <core file ulimit>'
4799- print >> sys.stderr, 'The core dump is read from stdin.'
4800+ print('Usage: %s <pid> <signal number> <core file ulimit>' % sys.argv[0])
4801+ print('The core dump is read from stdin.')
4802 except IOError:
4803 # sys.stderr might not actually exist, expecially not when being called
4804 # from the kernel
4805@@ -307,7 +306,7 @@
4806 os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0), 'w')
4807 assert reportfile.fileno() > sys.stderr.fileno()
4808 os.chown(report, pidstat.st_uid, pidstat.st_gid)
4809- except (OSError, IOError), e:
4810+ except (OSError, IOError) as e:
4811 error_log('Could not create report file: %s' % str(e))
4812 sys.exit(1)
4813
4814@@ -334,14 +333,14 @@
4815 os.unlink(report)
4816 raise
4817 if report:
4818- os.chmod(report, 0600)
4819+ os.chmod(report, 0o600)
4820 if reportfile != sys.stderr:
4821 error_log('wrote report %s' % report)
4822 except (SystemExit, KeyboardInterrupt):
4823 raise
4824-except Exception, e:
4825+except Exception as e:
4826 error_log('Unhandled exception:')
4827 traceback.print_exc()
4828- print >> sys.stderr, 'pid: %i, uid: %i, gid: %i, euid: %i, egid: %i' % (
4829- os.getpid(), os.getuid(), os.getgid(), os.geteuid(), os.getegid())
4830- print >> sys.stderr, 'environment:', os.environ
4831+ error_log('pid: %i, uid: %i, gid: %i, euid: %i, egid: %i' % (
4832+ os.getpid(), os.getuid(), os.getgid(), os.geteuid(), os.getegid()))
4833+ error_log('environment: %s' % str(os.environ))
4834
4835=== modified file 'data/gcc_ice_hook'
4836--- data/gcc_ice_hook 2009-09-08 10:30:40 +0000
4837+++ data/gcc_ice_hook 2011-02-14 19:45:28 +0000
4838@@ -16,8 +16,8 @@
4839
4840 # parse command line arguments
4841 if len(sys.argv) != 3:
4842- print >> sys.stderr, 'Usage:', sys.argv[0], '<executable name> <gcc -E output file>'
4843- print >> sys.stderr, 'If "-" is specified as second argument, the preprocessed source is read from stdin.'
4844+ print('Usage: %s <executable name> <gcc -E output file>' % sys.argv[0])
4845+ print('If "-" is specified as second argument, the preprocessed source is read from stdin.')
4846 sys.exit(1)
4847
4848 (exename, sourcefile) = sys.argv[1:]
4849
4850=== modified file 'data/general-hooks/parse_segv.py'
4851--- data/general-hooks/parse_segv.py 2010-05-04 20:41:48 +0000
4852+++ data/general-hooks/parse_segv.py 2011-02-14 19:45:28 +0000
4853@@ -12,11 +12,12 @@
4854 # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
4855 # the full text of the license.
4856
4857-import sys, re
4858+import sys, re, logging
4859
4860 class ParseSegv(object):
4861 def __init__(self, registers, disassembly, maps, debug=False):
4862- self.debug = debug
4863+ if debug:
4864+ logging.basicConfig(level=logging.DEBUG)
4865
4866 self.regs = self.parse_regs(registers)
4867 self.sp = None
4868@@ -43,7 +44,7 @@
4869 try:
4870 span, perms, bits, dev = items[0:4]
4871 except:
4872- raise ValueError, 'Cannot parse maps line: %s' % (line.strip())
4873+ raise ValueError('Cannot parse maps line: %s' % (line.strip()))
4874 if len(items)==5:
4875 name = None
4876 else:
4877@@ -52,8 +53,7 @@
4878 if name == '[stack]':
4879 self.stack_vma = len(maps)
4880 maps.append({'start': start, 'end': end, 'perms': perms, 'name': name})
4881- if self.debug:
4882- print >>sys.stderr, start, end, perms, name
4883+ logging.debug(start, end, perms, name)
4884 return maps
4885
4886 def parse_regs(self, reg_str):
4887@@ -61,33 +61,30 @@
4888 for line in reg_str.splitlines():
4889 reg, hexvalue = line.split()[0:2]
4890 regs[reg] = int(hexvalue,16)
4891- if self.debug:
4892- print >>sys.stderr, '%s:0x%08x' % (reg, regs[reg])
4893+ logging.debug('%s:0x%08x', reg, regs[reg])
4894 return regs
4895
4896 def parse_disassembly(self, disassembly):
4897 if not self.regs:
4898- raise ValueError, 'Registers not loaded yet!?'
4899+ raise ValueError('Registers not loaded yet!?')
4900 lines = disassembly.splitlines()
4901 # Throw away possible 'Dump' gdb report line
4902 if len(lines)>0 and lines[0].startswith('Dump'):
4903 lines.pop(0)
4904 if len(lines)<1:
4905- raise ValueError, 'Failed to load empty disassembly'
4906+ raise ValueError('Failed to load empty disassembly')
4907 line = lines[0].strip()
4908 # Drop GDB 7.1's leading $pc mark
4909 if line.startswith('=>'):
4910 line = line[2:].strip()
4911- if self.debug:
4912- print >>sys.stderr, line
4913+ logging.debug(line)
4914 pc_str = line.split()[0]
4915 if pc_str.startswith('0x'):
4916 pc = int(pc_str.split(':')[0],16)
4917 else:
4918 # Could not identify this instruction line
4919- raise ValueError, 'Could not parse PC "%s" from disassembly line: %s' % (pc_str, line)
4920- if self.debug:
4921- print >>sys.stderr, 'pc: 0x%08x' % (pc)
4922+ raise ValueError('Could not parse PC "%s" from disassembly line: %s' % (pc_str, line))
4923+ logging.debug('pc: 0x%08x', pc)
4924
4925 full_insn_str = line.split(':',1)[1].strip()
4926 # Handle invalid memory
4927@@ -108,8 +105,7 @@
4928 args_str = insn_parts.pop(-1)
4929 # Assume remainder is the insn itself
4930 insn = ' '.join(insn_parts)
4931- if self.debug:
4932- print >>sys.stderr, 'insn: %s' % (insn)
4933+ logging.debug('insn: %s', insn)
4934
4935 args = []
4936 src = None
4937@@ -118,20 +114,17 @@
4938 # Could not find insn args
4939 args = None
4940 else:
4941- if self.debug:
4942- print >>sys.stderr, 'args: "%s"' % (args_str)
4943+ logging.debug('args: "%s"', args_str)
4944
4945 for m in re.finditer('([^,\(]*(\(:?[^\)]+\))*)',args_str):
4946 if len(m.group(0)):
4947 args.append(m.group(0))
4948 if len(args)>0:
4949 src = args[0]
4950- if self.debug:
4951- print >>sys.stderr, 'src: %s' % (src)
4952+ logging.debug('src: %s', src)
4953 if len(args)>1:
4954 dest = args[1]
4955- if self.debug:
4956- print >>sys.stderr, 'dest: %s' % (dest)
4957+ logging.debug('dest: %s', dest)
4958
4959 # Set up possible implicit memory destinations (stack actions)
4960 if insn in ['push','pop','pushl','popl','call','callq','ret','retq']:
4961@@ -198,7 +191,7 @@
4962 if reg in self.regs:
4963 #print 'got %s (%d & %d == %d)' % (reg, self.regs[reg], mask, self.regs[reg] & ~mask)
4964 return self.regs[reg] & ~mask
4965- raise ValueError, "Could not resolve register '%s'" % (reg_orig)
4966+ raise ValueError("Could not resolve register '%s'" % (reg_orig))
4967
4968 def calculate_arg(self, arg):
4969 # Check for and pre-remove segment offset
4970@@ -225,7 +218,7 @@
4971 add = self.regs[offset[1:]]
4972 else:
4973 if not offset.startswith('0x'):
4974- raise ValueError, 'Unknown offset literal: %s' % (parts[0])
4975+ raise ValueError('Unknown offset literal: %s' % (parts[0]))
4976 add = int(offset[2:],16) * sign
4977 else:
4978 add = 0
4979@@ -356,24 +349,22 @@
4980 if understood:
4981 report['SegvReason'] = reason
4982 report['SegvAnalysis'] = details
4983- except BaseException, e:
4984+ except BaseException as e:
4985 report['SegvAnalysis'] = 'Failure: %s' % (str(e))
4986
4987
4988 if __name__ == '__main__':
4989 if len(sys.argv)==2 and sys.argv[1] in ['-h','--help']:
4990- print 'To run self-test, run without any arguments (or with -v)'
4991- print 'To do stand-alone crash parsing:'
4992- print ' Usage: %s Registers.txt Disassembly.txt ProcMaps.txt' % (sys.argv[0])
4993+ print('To run self-test, run without any arguments (or with -v)')
4994+ print('To do stand-alone crash parsing:')
4995+ print(' Usage: %s Registers.txt Disassembly.txt ProcMaps.txt' % (sys.argv[0]))
4996 sys.exit(0)
4997 elif len(sys.argv)==4:
4998 segv = ParseSegv(file(sys.argv[1]).read(), \
4999 file(sys.argv[2]).read(), \
5000 file(sys.argv[3]).read())
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches