1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
1 |
import os |
2 |
import re |
|
3 |
from datetime import datetime |
|
4 |
import email |
|
5 |
import cStringIO |
|
6 |
||
7 |
class Bug: |
|
8 |
def __init__(self, db, id, package=None, date=None, status=None, |
|
9 |
originator=None, severity=None, tags=None, report=None): |
|
10 |
self.db = db |
|
11 |
self.id = id |
|
12 |
self._emails = [] |
|
13 |
||
14 |
if package: |
|
15 |
self.package = package |
|
16 |
if date: |
|
17 |
self.date = date |
|
18 |
if status: |
|
19 |
self.status = status |
|
20 |
if originator: |
|
21 |
self.originator = originator |
|
22 |
if severity: |
|
23 |
self.severity = severity |
|
24 |
if tags: |
|
25 |
self.tags = tags |
|
26 |
if report: |
|
27 |
self.report = report |
|
28 |
||
29 |
def is_open(self): |
|
30 |
#return not self.done and 'fixed' not in self.tags
|
|
31 |
return self.status != 'done' and 'fixed' not in self.tags |
|
32 |
||
33 |
def affects_unstable(self): |
|
34 |
return 'sid' in self.tags or ('woody' not in self.tags and |
|
35 |
'sarge' not in self.tags and |
|
36 |
'experimental' not in self.tags) |
|
37 |
||
38 |
def affects_package(self, packageset): |
|
39 |
for package in self.packagelist(): |
|
40 |
if package in packageset: |
|
41 |
return True |
|
42 |
return False |
|
43 |
||
44 |
def is_release_critical(self): |
|
45 |
return self.severity in ('critical', 'grave', 'serious') |
|
46 |
||
47 |
def __str__(self): |
|
48 |
return 'Bug#%d' % self.id |
|
49 |
||
50 |
def __getattr__(self, name): |
|
51 |
# Lazy loading of non-indexed attributes
|
|
52 |
||
53 |
if not self.db.load(self, name): |
|
54 |
raise AttributeError, name |
|
55 |
||
56 |
if not hasattr(self, name): |
|
57 |
raise InternalError, "Database.load did not provide attribute '%s'" % name |
|
58 |
||
59 |
return getattr(self, name) |
|
60 |
||
61 |
def packagelist(self): |
|
62 |
if self.package is None: |
|
63 |
return [] |
|
64 |
if ',' in self.package: |
|
65 |
return self.package.split(',') |
|
66 |
return [self.package] |
|
67 |
||
68 |
def emails(self): |
|
69 |
if self._emails: |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
70 |
return self._emails |
71 |
for comment in self.comments: |
|
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
72 |
message = email.message_from_string(comment) |
73 |
self._emails.append(message) |
|
74 |
return self._emails |
|
75 |
||
76 |
class IndexParseError(Exception): pass |
|
77 |
class StatusParseError(Exception): pass |
|
78 |
class StatusMissing(Exception): pass |
|
79 |
class SummaryMissing(Exception): pass |
|
80 |
class SummaryParseError(Exception): pass |
|
81 |
class SummaryVersionError(Exception): pass |
|
82 |
class ReportMissing(Exception): pass |
|
83 |
class ReportParseError(Exception): pass |
|
84 |
class LogMissing(Exception): pass |
|
85 |
class LogParseFailed(Exception): pass |
|
86 |
class InternalError(Exception): pass |
|
87 |
||
88 |
class Database: |
|
89 |
def __init__(self, root): |
|
90 |
self.root = root |
|
91 |
||
92 |
class bug_iterator: |
|
93 |
index_record = re.compile(r'^(?P<package>\S+) (?P<bugid>\d+) (?P<date>\d+) (?P<status>\w+) \[(?P<originator>.*)\] (?P<severity>\w+)(?: (?P<tags>.*))?$') |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
94 |
|
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
95 |
def __init__(self, db, filter=None): |
96 |
self.db = db |
|
97 |
self.index = open(os.path.join(self.db.root, 'index/index.db')) |
|
1194
by Canonical.com Patch Queue Manager
rosetta stats legend, fixes for hoary |
98 |
self.filter = filter |
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
99 |
|
100 |
def next(self): |
|
101 |
line = self.index.readline() |
|
102 |
if not line: |
|
103 |
raise StopIteration |
|
104 |
||
105 |
match = self.index_record.match(line) |
|
106 |
if not match: |
|
107 |
raise IndexParseError(line) |
|
108 |
||
109 |
return Bug(self.db, |
|
110 |
int(match.group('bugid')), |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
111 |
match.group('package'), |
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
112 |
datetime.fromtimestamp(int(match.group('date'))), |
113 |
match.group('status'), |
|
114 |
match.group('originator'), |
|
115 |
match.group('severity'), |
|
116 |
match.group('tags').split(' ')) |
|
117 |
||
118 |
def load(self, bug, name): |
|
119 |
if name in ('originator', 'date', 'subject', 'msgid', 'package', |
|
120 |
'tags', 'done', 'forwarded', 'mergedwith', 'severity'): |
|
121 |
self.load_summary(bug) |
|
122 |
elif name == 'report': |
|
123 |
self.load_report(bug) |
|
124 |
elif name in ('comments',): |
|
125 |
self.load_log(bug) |
|
126 |
elif name == 'status': |
|
2048
by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh |
127 |
if bug.done is not None: |
128 |
bug.status = 'done' |
|
129 |
if bug.forwarded is not None: |
|
130 |
bug.status = 'forwarded' |
|
131 |
bug.status = 'open' |
|
132 |
else: |
|
1149
by Canonical.com Patch Queue Manager
malone debbugs integration |
133 |
return False |
134 |
||
135 |
return True |
|
136 |
||
137 |
def load_summary(self, bug): |
|
138 |
summary = os.path.join(self.root, 'db-h', self._hash(bug), '%d.summary' % bug.id) |
|
139 |
||
140 |
try: |
|
141 |
fd = open(summary) |
|
142 |
except IOError, e: |
|
143 |
if e.errno == 2: |
|
144 |
raise SummaryMissing, summary |
|
145 |
raise
|
|
146 |
||
147 |
try: |
|
148 |
message = email.message_from_file(fd) |
|
149 |
except Exception, e: |
|
150 |
raise SummaryParseError, '%s: %s' % (summary, str(e)) |
|
151 |
||
152 |
version = message['format-version'] |
|
153 |
if version is None: |
|
154 |
raise SummaryParseError, "%s: Missing Format-Version" % summary |
|
155 |
||
156 |
if version != '2': |
|
157 |
raise SummaryVersionError, "%s: I don't understand version %s" % (summary, version) |
|
158 |
||
159 |
bug.originator = message['submitter'] |
|
160 |
bug.date = datetime.fromtimestamp(int(message['date'])) |
|
161 |
bug.subject = message['subject'] |
|
162 |
bug.msgid = message['message-id'] |
|
163 |
bug.package = message['package'] |
|
164 |
bug.done = message['done'] |
|
165 |
bug.forwarded = message['forwarded-to'] |
|
166 |
bug.severity = message['severity'] |
|
167 |
||
168 |
if 'merged-with' in message: |
|
169 |
bug.mergedwith = map(int,message['merged-with'].split(' ')) |
|
170 |
else: |
|
171 |
bug.mergedwith = [] |
|
172 |
||
173 |
if 'tags' in message: |
|
174 |
bug.tags = message['tags'].split(' ') |
|
175 |
else: |
|
176 |
bug.tags = [] |
|
177 |
||
178 |
def load_report(self, bug): |
|
179 |
report = os.path.join(self.root, 'db-h', self._hash(bug), '%d.report' % bug.id) |
|
180 |
||
181 |
try: |
|
182 |
fd = open(report) |
|
183 |
except IOError, e: |
|
184 |
if e.errno == 2: |
|
185 |
raise ReportMissing, report |
|
186 |
raise
|
|
187 |
||
188 |
bug.report = fd.read() |
|
189 |
fd.close() |
|
190 |
||
191 |
def load_log(self, bug): |
|
192 |
log = os.path.join(self.root, 'db-h', self._hash(bug), '%d.log' % bug.id) |
|
193 |
comments = [] |
|
194 |
||
195 |
try: |
|
196 |
logreader = os.popen('./debbugs-log.pl %s' % log, 'r') |
|
197 |
comment = cStringIO.StringIO() |
|
198 |
for line in logreader: |
|
199 |
if line == '.\n': |
|
200 |
comments.append(comment.getvalue()) |
|
201 |
comment = cStringIO.StringIO() |
|
202 |
elif line.startswith('.'): |
|
203 |
comment.write(line[1:]) |
|
204 |
else: |
|
205 |
comment.write(line) |
|
206 |
if comment.tell() != 0: |
|
207 |
raise LogParseFailed('Unterminated comment from debbugs-log.pl') |
|
208 |
exitcode = logreader.close() |
|
209 |
if exitcode is not None: |
|
210 |
raise LogParseFailed('debbugs-log.pl exited with code %d' % exitcode) |
|
211 |
except IOError, e: |
|
212 |
if e.errno == 2: |
|
213 |
raise LogMissing, log |
|
214 |
raise
|
|
215 |
||
216 |
bug.comments = comments |
|
217 |
||
218 |
def _hash(self, bug): |
|
219 |
return '%02d' % (bug.id % 100) |
|
220 |
||
221 |
def __iter__(self): |
|
222 |
return self.bug_iterator(self, None) |
|
223 |
||
224 |
def __getitem__(self, bug): |
|
225 |
return Bug(self, bug) |
|
226 |
||
227 |
if __name__ == '__main__': |
|
228 |
import sys |
|
229 |
||
230 |
for bug in Database('/srv/debzilla.no-name-yet.com/debbugs'): |
|
231 |
try: |
|
232 |
print bug, bug.subject |
|
233 |
except Exception, e: |
|
234 |
print >>sys.stderr, '%s: %s' % (e.__class__.__name__, str(e)) |
|
235 |
||
236 |
||
237 |