~ubuntu-branches/ubuntu/edgy/apport/edgy

« back to all changes in this revision

Viewing changes to problem_report.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2006-08-11 15:40:05 UTC
  • Revision ID: package-import@ubuntu.com-20060811154005-rk27ie8x0lzfxx28
Tags: 0.10
* apport-gtk: Show report file size in bug report window.
* apport: Correctly handle relative paths to core dumps (use crashed
  process' cwd).
* Fix the GPL URLs in source file's copyright comments.
* debian/apport.cron.daily: Add -mindepth 1 to find commands to avoid
  attempting to remove the /var/crash/ directory. Closes: LP#55107
* problem_report.py:
  - Fix precise whitespace handling in continuation lines, add selftest.
  - Add selftest for reading a report, modifying fields, and writing it
    back.
  - Fix writing back binary data, adapt test suite to check it.
  - Fixed ProblemReport.load() to clean up old data, added selftest.
  - Restructure class to inherit from IterableUserDict and throw away all
    the now obsolete dictionary wrapper methods.
* debian/apport.init: Add colon to description to make output less
  confusing.
* Add apport-retrace and install it into apport: This tool takes a crash
  report and refreshes the stack traces in it. This is particularly useful
  if debug symbols are installed now, but haven't been at the time the crash
  occured.

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
the full text of the license.
11
11
'''
12
12
 
13
 
import bz2, base64, time
 
13
import bz2, base64, time, UserDict
14
14
 
15
 
class ProblemReport:
 
15
class ProblemReport(UserDict.IterableUserDict):
16
16
    def __init__(self, type = 'Crash', date = None):
17
17
        '''Initialize a fresh problem report.
18
18
        
21
21
 
22
22
        if date == None:
23
23
            date = time.asctime()
24
 
        self.info = {'ProblemType': type, 'Date': date}
 
24
        self.data = {'ProblemType': type, 'Date': date}
25
25
 
26
26
    def load(self, file, binary=True):
27
27
        '''Initialize problem report from a file-like object, using Debian
30
30
        if binary is False, binary data is not loaded; the dictionary key is
31
31
        created, but its value will be an empty string.'''
32
32
 
 
33
        self.data.clear()
33
34
        key = None
34
35
        value = None
35
36
        b64_block = False
42
43
                if b64_block:
43
44
                    value += bd.decompress(base64.b64decode(line))
44
45
                else:
45
 
                    value += line[1:]
 
46
                    if len(value) > 0:
 
47
                        value += '\n'
 
48
                    value += line[1:-1]
46
49
            else:
47
50
                if b64_block:
48
51
                    b64_block = False
49
52
                    bd = None
50
53
                if key:
51
54
                    assert value != None
52
 
                    self.info[key] = value
 
55
                    self.data[key] = value
53
56
                (key, value) = line.split(':', 1)
54
57
                value = value.strip()
55
58
                if value == 'base64':
59
62
                        bd = bz2.BZ2Decompressor()
60
63
 
61
64
        if key != None:
62
 
            self.info[key] = value
 
65
            self.data[key] = value
 
66
 
 
67
    def _is_binary(self, string):
 
68
        '''Check if the given strings contains binary data.'''
 
69
 
 
70
        for c in string:
 
71
            if c < ' ' and not c.isspace():
 
72
                return True
 
73
        return False
63
74
 
64
75
    def write(self, file):
65
76
        '''Write information into the given file-like object, using Debian
70
81
        which will be read, bzip2'ed, and base64-encoded.
71
82
        '''
72
83
 
73
 
        keys = self.info.keys()
 
84
        keys = self.data.keys()
74
85
        keys.remove('ProblemType')
75
86
        keys.sort()
76
87
        keys.insert(0, 'ProblemType')
77
88
        for k in keys:
78
 
            v = self.info[k]
 
89
            v = self.data[k]
79
90
            # if it's a string, copy it
80
91
            if hasattr(v, 'find'):
81
 
                if v.find('\n') >= 0:
 
92
                if self._is_binary(v):
 
93
                    file.write (k + ': base64\n ')
 
94
                    bc = bz2.BZ2Compressor(9)
 
95
                    outblock = bc.compress(v)
 
96
                    if outblock:
 
97
                        file.write(base64.b64encode(outblock))
 
98
                        file.write('\n ')
 
99
                    file.write(base64.b64encode(bc.flush()))
 
100
                    file.write('\n')
 
101
                elif v.find('\n') >= 0:
82
102
                    assert v.find('\n\n') < 0
83
103
                    print >> file, k + ':'
84
104
                    print >> file, '', v.replace('\n', '\n ')
102
122
                        file.write('\n')
103
123
                        break
104
124
 
105
 
    def __getitem__(self, k):
106
 
        return self.info.__getitem__(k)
107
 
 
108
125
    def __setitem__(self, k, v):
109
126
        assert hasattr(k, 'isalnum')
110
127
        assert k.isalnum()
113
130
            (hasattr(v, '__getitem__') and len(v) == 1 
114
131
            and hasattr(v[0], 'isalnum')))
115
132
 
116
 
        return self.info.__setitem__(k, v)
117
 
 
118
 
    def __delitem__(self, k):
119
 
        return self.info.__delitem__(k)
120
 
 
121
 
    def __iter__(self):
122
 
        return self.info.__iter__()
123
 
 
124
 
    def has_key(self,k):
125
 
        return self.info.has_key(k)
 
133
        return self.data.__setitem__(k, v)
 
134
 
126
135
 
127
136
#
128
137
# Unit test
194
203
        self.assertEqual(pr['ProblemType'], 'Crash')
195
204
        self.assertEqual(pr['Date'], 'now!')
196
205
        self.assertEqual(pr['Simple'], 'bar')
 
206
        self.assertEqual(pr['WhiteSpace'], ' foo   bar\nbaz\n  blip  ')
 
207
 
 
208
        # test last field a bit more
 
209
        pr.load(StringIO.StringIO(
 
210
'''ProblemType: Crash
 
211
Date: now!
 
212
Simple: bar
 
213
WhiteSpace:
 
214
  foo   bar
 
215
 baz
 
216
   blip  
 
217
 
 
218
'''))
 
219
        self.assertEqual(pr['ProblemType'], 'Crash')
 
220
        self.assertEqual(pr['Date'], 'now!')
 
221
        self.assertEqual(pr['Simple'], 'bar')
197
222
        self.assertEqual(pr['WhiteSpace'], ' foo   bar\nbaz\n  blip  \n')
 
223
        pr = ProblemReport()
 
224
        pr.load(StringIO.StringIO(
 
225
'''ProblemType: Crash
 
226
WhiteSpace:
 
227
  foo   bar
 
228
 baz
 
229
   blip  
 
230
Last: foo
 
231
'''))
 
232
        self.assertEqual(pr['WhiteSpace'], ' foo   bar\nbaz\n  blip  ')
 
233
        self.assertEqual(pr['Last'], 'foo')
 
234
 
 
235
        pr.load(StringIO.StringIO(
 
236
'''ProblemType: Crash
 
237
WhiteSpace:
 
238
  foo   bar
 
239
 baz
 
240
   blip  
 
241
Last: foo
 
242
 
 
243
'''))
 
244
        self.assertEqual(pr['WhiteSpace'], ' foo   bar\nbaz\n  blip  ')
 
245
        self.assertEqual(pr['Last'], 'foo\n')
 
246
 
 
247
        # test that load() cleans up properly
 
248
        pr.load(StringIO.StringIO('ProblemType: Crash'))
 
249
        self.assertEqual(pr.keys(), ['ProblemType'])
198
250
 
199
251
    def test_write_file(self):
200
252
        '''Test writing a report with binary file data.'''
262
314
        self.assertEqual(pr['Before'], 'xtestx')
263
315
        self.assertEqual(pr['ZAfter'], 'ytesty')
264
316
 
 
317
        # write it again
 
318
        io2 = StringIO.StringIO()
 
319
        pr.write(io2)
 
320
        self.assertEqual(io.getvalue(), io2.getvalue())
 
321
 
265
322
    def test_iter(self):
266
323
        '''Test ProblemReport iteration.'''
267
324
 
276
333
 
277
334
        self.assertEqual(len([k for k in pr if k != 'foo']), 2)
278
335
 
 
336
    def test_modify(self):
 
337
        '''Test reading, modifying fields, and writing back.'''
 
338
 
 
339
        report = '''ProblemType: Crash
 
340
Date: now!
 
341
File: base64
 
342
 QlpoOTFBWSZTWc5ays4AAAdGAEEAMAAAECAAMM0AkR6fQsBSDhdyRThQkM5ays4=
 
343
Long:
 
344
 xxx
 
345
 .
 
346
 yyy
 
347
Short: Bar
 
348
'''
 
349
 
 
350
        pr = ProblemReport()
 
351
        pr.load(StringIO.StringIO(report))
 
352
 
 
353
        self.assertEqual(pr['Long'], 'xxx\n.\nyyy')
 
354
 
 
355
        # write back unmodified
 
356
        io = StringIO.StringIO()
 
357
        pr.write(io)
 
358
        self.assertEqual(io.getvalue(), report)
 
359
 
 
360
        pr['Short'] = 'aaa\nbbb'
 
361
        pr['Long'] = '123'
 
362
        io = StringIO.StringIO()
 
363
        pr.write(io)
 
364
        self.assertEqual(io.getvalue(), 
 
365
'''ProblemType: Crash
 
366
Date: now!
 
367
File: base64
 
368
 QlpoOTFBWSZTWc5ays4AAAdGAEEAMAAAECAAMM0AkR6fQsBSDhdyRThQkM5ays4=
 
369
Long: 123
 
370
Short:
 
371
 aaa
 
372
 bbb
 
373
''')
 
374
 
279
375
if __name__ == '__main__':
280
376
    unittest.main()