Merge lp:~qcxhome/gtg/bugzilla-plugin-refactor into lp:~gtg/gtg/old-trunk
- bugzilla-plugin-refactor
- Merge into old-trunk
Status: | Merged |
---|---|
Merged at revision: | 1300 |
Proposed branch: | lp:~qcxhome/gtg/bugzilla-plugin-refactor |
Merge into: | lp:~gtg/gtg/old-trunk |
Diff against target: |
516 lines (+296/-164) 5 files modified
GTG/plugins/bugzilla/bug.py (+60/-35) GTG/plugins/bugzilla/bugzilla.py (+60/-59) GTG/plugins/bugzilla/notification.py (+59/-0) GTG/plugins/bugzilla/server.py (+0/-70) GTG/plugins/bugzilla/services.py (+117/-0) |
To merge this branch: | bzr merge lp:~qcxhome/gtg/bugzilla-plugin-refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Izidor Matušov | Approve | ||
Review via email: mp+165611@code.launchpad.net |
Commit message
Description of the change
- 1300. By Chenxiong Qi
-
FIX: code style errors
Chenxiong Qi (qcxhome) wrote : | # |
Hi izidor,
I have pushed a commit to fix the code style errors. Do I need make another
merge proposal for this change in that branch page? Thanks!
Regards,
Chenxiong Qi
在 2013-6-2 下午4:06,"Izidor Matušov" <email address hidden>写道:
> Review: Needs Fixing
>
> Hi,
>
> thanks for your patch. Please fix the following (style) errors:
>
> $ make pep8
> GTG/plugins/
> found 1
> GTG/plugins/
> GTG/plugins/
> GTG/plugins/
>
> $ make pyflakes
> GTG/plugins/
> to but never used
> --
>
> https:/
> You are the owner of lp:~qcxhome/gtg/bugzilla-plugin-refactor.
>
Izidor Matušov (izidor) wrote : | # |
Thank you for your patch :)
Preview Diff
1 | === modified file 'GTG/plugins/bugzilla/bug.py' |
2 | --- GTG/plugins/bugzilla/bug.py 2013-02-25 08:12:02 +0000 |
3 | +++ GTG/plugins/bugzilla/bug.py 2013-06-04 14:16:49 +0000 |
4 | @@ -14,44 +14,69 @@ |
5 | # You should have received a copy of the GNU General Public License along with |
6 | # this program. If not, see <http://www.gnu.org/licenses/>. |
7 | |
8 | -# this handles old versions of pybugz as well as new ones |
9 | -try: |
10 | - from bugz import bugzilla |
11 | - assert bugzilla |
12 | -except: |
13 | - import bugz as bugzilla |
14 | - |
15 | -# changed the default action to skip auth |
16 | - |
17 | - |
18 | -class Bug: |
19 | - |
20 | - def __init__(self, base, nb): |
21 | - # this also handles old versions of pybugz |
22 | - try: |
23 | - bugs = bugzilla.BugzillaProxy( |
24 | - base, skip_auth=True).Bug.get({'ids': [nb, ], }) |
25 | - except: |
26 | - bugs = bugzilla.BugzillaProxy(base).Bug.get({'ids': [nb, ], }) |
27 | - self.bug = bugs['bugs'][0] |
28 | - |
29 | - def get_title(self): |
30 | +__all__ = ('BugFactory',) |
31 | + |
32 | + |
33 | +class Bug(object): |
34 | + |
35 | + def __init__(self, bug): |
36 | + ''' Initialize Bug object using bug object retrieved via Bugzilla |
37 | + service XMLRPC |
38 | + ''' |
39 | + self.bug = bug |
40 | + |
41 | + @property |
42 | + def summary(self): |
43 | return self.bug['summary'] |
44 | |
45 | - def get_product(self): |
46 | + @property |
47 | + def product(self): |
48 | return self.bug['product'] |
49 | |
50 | - def get_component(self): |
51 | + @property |
52 | + def description(self): |
53 | + return self.bug['summary'] |
54 | + |
55 | + @property |
56 | + def component(self): |
57 | return self.bug['component'] |
58 | |
59 | - def get_description(self): |
60 | - return self.bug['summary'] |
61 | - |
62 | -if __name__ == '__main__': |
63 | - for bug in [Bug('https://bugzilla.gnome.org', '598354'), |
64 | - Bug('https://bugs.freedesktop.org', '24120')]: |
65 | - print "title:", bug.get_title() |
66 | - print "product:", bug.get_product() |
67 | - print "component:", bug.get_component() |
68 | - print "description:", bug.get_description() |
69 | - print "" |
70 | + |
71 | +class GnomeBug(Bug): |
72 | + pass |
73 | + |
74 | + |
75 | +class FreedesktopBug(Bug): |
76 | + pass |
77 | + |
78 | + |
79 | +class GentooBug(Bug): |
80 | + pass |
81 | + |
82 | + |
83 | +class MozillaBug(Bug): |
84 | + pass |
85 | + |
86 | + |
87 | +class SambaBug(Bug): |
88 | + pass |
89 | + |
90 | + |
91 | +class RedHatBug(Bug): |
92 | + pass |
93 | + |
94 | + |
95 | +bugs = { |
96 | + 'bugzilla.gnome.org': GnomeBug, |
97 | + 'bugs.freedesktop.org': FreedesktopBug, |
98 | + 'bugzilla.mozilla.org': MozillaBug, |
99 | + 'bugzilla.samba.org': SambaBug, |
100 | + 'bugs.gentoo.org': GentooBug, |
101 | + 'bugzilla.redhat.com': RedHatBug, |
102 | +} |
103 | + |
104 | + |
105 | +class BugFactory(object): |
106 | + @staticmethod |
107 | + def create(serviceDomain, bug): |
108 | + return bugs[serviceDomain](bug) |
109 | |
110 | === modified file 'GTG/plugins/bugzilla/bugzilla.py' |
111 | --- GTG/plugins/bugzilla/bugzilla.py 2013-02-25 07:35:07 +0000 |
112 | +++ GTG/plugins/bugzilla/bugzilla.py 2013-06-04 14:16:49 +0000 |
113 | @@ -15,19 +15,71 @@ |
114 | # this program. If not, see <http://www.gnu.org/licenses/>. |
115 | |
116 | import gobject |
117 | +import re |
118 | import threading |
119 | import xmlrpclib |
120 | from urlparse import urlparse |
121 | |
122 | -from GTG.plugins.bugzilla.server import ServersStore |
123 | -from GTG.plugins.bugzilla.bug import Bug |
124 | +from services import BugzillaServiceFactory |
125 | +from notification import send_notification |
126 | + |
127 | +__all__ = ('pluginBugzilla', ) |
128 | + |
129 | +bugIdPattern = re.compile('^\d+$') |
130 | + |
131 | + |
132 | +class GetBugInformationTask(threading.Thread): |
133 | + |
134 | + def __init__(self, task, **kwargs): |
135 | + ''' Initialize task data, where task is the GTG task object. ''' |
136 | + self.task = task |
137 | + super(GetBugInformationTask, self).__init__(**kwargs) |
138 | + |
139 | + def parseBugUrl(self, url): |
140 | + r = urlparse(url) |
141 | + queries = dict([item.split('=') for item in r.query.split('&')]) |
142 | + return r.scheme, r.hostname, queries |
143 | + |
144 | + def run(self): |
145 | + bug_url = self.task.get_title() |
146 | + scheme, hostname, queries = self.parseBugUrl(bug_url) |
147 | + |
148 | + bug_id = queries.get('id', None) |
149 | + if bugIdPattern.match(bug_id) is None: |
150 | + # FIXME: make some sensable action instead of returning silently. |
151 | + return |
152 | + |
153 | + try: |
154 | + bugzillaService = BugzillaServiceFactory.create(scheme, hostname) |
155 | + bug = bugzillaService.getBug(bug_id) |
156 | + except xmlrpclib.Fault, err: |
157 | + code = err.faultCode |
158 | + if code == 100: # invalid bug ID |
159 | + title = 'Invalid bug ID #%s' % bug_id |
160 | + elif code == 101: # bug ID not exist |
161 | + title = 'Bug #%s does not exist.' % bug_id |
162 | + elif code == 102: # Access denied |
163 | + title = 'Access denied to bug %s' % bug_url |
164 | + else: # unrecoganized error code currently |
165 | + title = err.faultString |
166 | + |
167 | + send_notification(bugzillaService.name, title) |
168 | + except Exception, err: |
169 | + send_notification(bugzillaService.name, err.message) |
170 | + else: |
171 | + title = '#%s: %s' % (bug_id, bug.summary) |
172 | + gobject.idle_add(self.task.set_title, title) |
173 | + text = "%s\n\n%s" % (bug_url, bug.description) |
174 | + gobject.idle_add(self.task.set_text, text) |
175 | + |
176 | + tags = bugzillaService.getTags(bug) |
177 | + if tags is not None and tags: |
178 | + for tag in tags: |
179 | + gobject.idle_add(self.task.add_tag, '@%s' % tag) |
180 | |
181 | |
182 | class pluginBugzilla: |
183 | |
184 | - def __init__(self): |
185 | - self.servers = ServersStore() |
186 | - |
187 | def activate(self, plugin_api): |
188 | self.plugin_api = plugin_api |
189 | self.connect_id = plugin_api.get_ui().connect( |
190 | @@ -37,62 +89,11 @@ |
191 | # this is a gobject callback that will block the Browser. |
192 | # decoupling with a thread. All interaction with task and tags objects |
193 | #(anything in a Tree) must be done with gobject.idle_add (invernizzi) |
194 | - thread = threading.Thread(target=self.__analyze_task, |
195 | - args=(task_id, )) |
196 | - thread.setDaemon(True) |
197 | - thread.start() |
198 | |
199 | - def __analyze_task(self, task_id): |
200 | task = self.plugin_api.get_requester().get_task(task_id) |
201 | - url = task.get_title() |
202 | - r = urlparse(url) |
203 | - if r.hostname is None: |
204 | - return |
205 | - |
206 | - server = self.servers.get(r.hostname) |
207 | - if server is None: |
208 | - return |
209 | - |
210 | - base = '%s://%s/xmlrpc.cgi' % (r.scheme, server.name) |
211 | - |
212 | - # get the number of the bug |
213 | - try: |
214 | - nb = r.query.split('id=')[1] |
215 | - except IndexError: |
216 | - return |
217 | - |
218 | - try: |
219 | - bug = Bug(base, nb) |
220 | - except xmlrpclib.Fault, err: |
221 | - code = err.faultCode |
222 | - if code == 100: # invalid bug ID |
223 | - title = 'Invalid bug ID #%s' % nb |
224 | - elif code == 101: # bug ID not exist |
225 | - title = 'Bug #%s does not exist.' % nb |
226 | - elif code == 102: # Access denied |
227 | - title = 'Access denied to bug #%s' % nb |
228 | - else: # unrecoganized error code currently |
229 | - title = err.faultString |
230 | - old_title = task.get_title() |
231 | - gobject.idle_add(task.set_title, title) |
232 | - gobject.idle_add(task.set_text, old_title) |
233 | - return |
234 | - except: |
235 | - return |
236 | - |
237 | - title = bug.get_title() |
238 | - if title is None: |
239 | - # can't find the title of the bug |
240 | - return |
241 | - |
242 | - gobject.idle_add(task.set_title, '#%s: %s' % (nb, title)) |
243 | - |
244 | - text = "%s\n\n%s" % (url, bug.get_description()) |
245 | - gobject.idle_add(task.set_text, text) |
246 | - |
247 | - tag = server.get_tag(bug) |
248 | - if tag is not None: |
249 | - gobject.idle_add(task.add_tag, '@%s' % tag) |
250 | + bugTask = GetBugInformationTask(task) |
251 | + bugTask.setDaemon(True) |
252 | + bugTask.start() |
253 | |
254 | def deactivate(self, plugin_api): |
255 | plugin_api.get_ui().disconnect(self.connect_id) |
256 | |
257 | === added file 'GTG/plugins/bugzilla/notification.py' |
258 | --- GTG/plugins/bugzilla/notification.py 1970-01-01 00:00:00 +0000 |
259 | +++ GTG/plugins/bugzilla/notification.py 2013-06-04 14:16:49 +0000 |
260 | @@ -0,0 +1,59 @@ |
261 | +# -*- coding: utf-8 -*- |
262 | + |
263 | +''' |
264 | +Notification is used to show messages to GTG users. |
265 | +''' |
266 | + |
267 | +import atexit |
268 | +import subprocess |
269 | + |
270 | +__all__ = ("send_notification", ) |
271 | + |
272 | +APP_NAME = "GTG" |
273 | +# How many millisecond the notification area lasts |
274 | +TIMEOUT = 3000 |
275 | + |
276 | + |
277 | +def _notify_via_pynotify(title, message): |
278 | + pynotify.init(APP_NAME) |
279 | + nt = pynotify.Notification(title, message) |
280 | + nt.set_timeout(TIMEOUT) |
281 | + nt.show() |
282 | + |
283 | + |
284 | +def _notify_via_notify_send(title, message): |
285 | + cmd = "notify-send --app-name=%s --expire-time=%d \"%s\" \"%s\"" % ( |
286 | + APP_NAME, TIMEOUT, title, message) |
287 | + subprocess.Popen(cmd, shell=True) |
288 | + |
289 | + |
290 | +# A reference to the concrete handler that sends notification. |
291 | +# By default, this reference is set to None in case all candidates are not |
292 | +# available to keep silient when unexpected things happen. |
293 | +_notify_handler = None |
294 | +try: |
295 | + # Primarily, pynotify is used to send notification. However, it might not |
296 | + # appear in user's machine. So, we'll try another alternative. |
297 | + import pynotify |
298 | + _notify_handler = _notify_via_pynotify |
299 | +except ImportError: |
300 | + # The alternative is notify-send, which is a command line utility provided |
301 | + # by libnotify package. |
302 | + proc = subprocess.Popen("which notify-send", shell=True) |
303 | + if proc.wait() == 0: |
304 | + _notify_handler = _notify_via_notify_send |
305 | + |
306 | + |
307 | +def send_notification(title, message): |
308 | + ''' A proxy to send notification |
309 | + |
310 | + When no notification utility is available, just keep silent. |
311 | + ''' |
312 | + |
313 | + if _notify_handler is not None: |
314 | + _notify_handler(title, message) |
315 | + |
316 | + |
317 | +@atexit.register |
318 | +def uinit_pynotify(): |
319 | + pynotify.uninit() |
320 | |
321 | === removed file 'GTG/plugins/bugzilla/server.py' |
322 | --- GTG/plugins/bugzilla/server.py 2012-07-13 17:24:28 +0000 |
323 | +++ GTG/plugins/bugzilla/server.py 1970-01-01 00:00:00 +0000 |
324 | @@ -1,70 +0,0 @@ |
325 | -# -*- coding: utf-8 -*- |
326 | -# Copyright (c) 2009 - Guillaume Desmottes <gdesmott@gnome.org> |
327 | -# |
328 | -# This program is free software: you can redistribute it and/or modify it under |
329 | -# the terms of the GNU General Public License as published by the Free Software |
330 | -# Foundation, either version 3 of the License, or (at your option) any later |
331 | -# version. |
332 | -# |
333 | -# This program is distributed in the hope that it will be useful, but WITHOUT |
334 | -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
335 | -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
336 | -# details. |
337 | -# |
338 | -# You should have received a copy of the GNU General Public License along with |
339 | -# this program. If not, see <http://www.gnu.org/licenses/>. |
340 | - |
341 | -SERVER_TAG_PRODUCT = 1 |
342 | -SERVER_TAG_COMPONENT = 2 |
343 | - |
344 | - |
345 | -class ServersStore: |
346 | - |
347 | - def __init__(self): |
348 | - self.servers = {} |
349 | - |
350 | - # GNOME |
351 | - server = Server('bugzilla.gnome.org') |
352 | - server.tag = SERVER_TAG_PRODUCT |
353 | - self.add(server) |
354 | - |
355 | - # freedesktop.org |
356 | - server = Server('bugs.freedesktop.org') |
357 | - server.tag = SERVER_TAG_COMPONENT |
358 | - self.add(server) |
359 | - |
360 | - # Mozilla |
361 | - server = Server('bugzilla.mozilla.org') |
362 | - server.tag = SERVER_TAG_COMPONENT |
363 | - self.add(server) |
364 | - |
365 | - # Samba |
366 | - server = Server('bugzilla.samba.org') |
367 | - server.tag = SERVER_TAG_COMPONENT |
368 | - self.add(server) |
369 | - |
370 | - # GENTOO |
371 | - server = Server('bugs.gentoo.org') |
372 | - server.tag = SERVER_TAG_COMPONENT |
373 | - self.add(server) |
374 | - |
375 | - def add(self, server): |
376 | - self.servers[server.name] = server |
377 | - |
378 | - def get(self, name): |
379 | - return self.servers.get(name) |
380 | - |
381 | - |
382 | -class Server: |
383 | - |
384 | - def __init__(self, name): |
385 | - self.name = name |
386 | - self.tag = None |
387 | - |
388 | - def get_tag(self, bug): |
389 | - if self.tag is None: |
390 | - return None |
391 | - elif self.tag == SERVER_TAG_PRODUCT: |
392 | - return bug.get_product() |
393 | - elif self.tag == SERVER_TAG_COMPONENT: |
394 | - return bug.get_component() |
395 | |
396 | === added file 'GTG/plugins/bugzilla/services.py' |
397 | --- GTG/plugins/bugzilla/services.py 1970-01-01 00:00:00 +0000 |
398 | +++ GTG/plugins/bugzilla/services.py 2013-06-04 14:16:49 +0000 |
399 | @@ -0,0 +1,117 @@ |
400 | +# -*- coding: utf-8 -*- |
401 | + |
402 | +# Remove dependence of bugz due to that plugin just needs get action and |
403 | +# it is done by Python xmlrpclib simply enough. |
404 | +from xmlrpclib import ServerProxy |
405 | + |
406 | +from bug import BugFactory |
407 | + |
408 | +__all__ = ('BugzillaServiceFactory',) |
409 | + |
410 | + |
411 | +class BugzillaService(object): |
412 | + name = 'Bugzilla Service' |
413 | + enabled = True |
414 | + tag_from = 'component' |
415 | + |
416 | + def __init__(self, scheme, domain): |
417 | + self.scheme = scheme |
418 | + self.domain = domain |
419 | + |
420 | + def buildXmlRpcServerUrl(self): |
421 | + return '%(scheme)s://%(domain)s/xmlrpc.cgi' % { |
422 | + 'scheme': self.scheme, 'domain': self.domain, |
423 | + } |
424 | + |
425 | + def getProxy(self, server_url): |
426 | + return ServerProxy(server_url) |
427 | + |
428 | + def getBug(self, bug_id): |
429 | + server_url = self.buildXmlRpcServerUrl() |
430 | + proxy = self.getProxy(server_url) |
431 | + bugs = proxy.Bug.get({'ids': [bug_id, ]}) |
432 | + return BugFactory.create(self.domain, bugs['bugs'][0]) |
433 | + |
434 | + def getTags(self, bug): |
435 | + ''' Get a list of tags due to some bug attribute contains list rather |
436 | + than a string in some bugzilla service. |
437 | + ''' |
438 | + tag_names = getattr(bug, self.tag_from, None) |
439 | + if tag_names is None: |
440 | + return [] |
441 | + if not isinstance(tag_names, list): |
442 | + return [tag_names] |
443 | + return tag_names |
444 | + |
445 | + |
446 | +class GnomeBugzilla(BugzillaService): |
447 | + name = 'GNOME Bugzilla Service' |
448 | + tag_from = 'product' |
449 | + |
450 | + |
451 | +class FreedesktopBugzilla(BugzillaService): |
452 | + ''' Bugzilla service of Freedesktop projects ''' |
453 | + |
454 | + name = 'Freedesktop Bugzilla Service' |
455 | + |
456 | + |
457 | +class GentooBugzilla(BugzillaService): |
458 | + ''' Bugzilla service of Gentoo project ''' |
459 | + |
460 | + name = 'Gentoo Bugzilla Service' |
461 | + |
462 | + |
463 | +class MozillaBugzilla(BugzillaService): |
464 | + ''' Bugzilla service of Mozilla products ''' |
465 | + |
466 | + name = 'Mozilla Bugzilla Service' |
467 | + |
468 | + |
469 | +class SambaBugzilla(BugzillaService): |
470 | + ''' Bugzilla service of Samba project ''' |
471 | + |
472 | + enabled = False |
473 | + name = 'Samba Bugzilla Service' |
474 | + |
475 | + |
476 | +class RedHatBugzilla(BugzillaService): |
477 | + ''' Bugzilla service provided by Red Hat ''' |
478 | + |
479 | + name = 'Red Hat Bugzilla Service' |
480 | + |
481 | +# Register bugzilla services manually, however store them in someplace and load |
482 | +# them at once is better. |
483 | +services = { |
484 | + 'bugzilla.gnome.org': GnomeBugzilla, |
485 | + 'bugs.freedesktop.org': FreedesktopBugzilla, |
486 | + 'bugzilla.mozilla.org': MozillaBugzilla, |
487 | + 'bugzilla.samba.org': SambaBugzilla, |
488 | + 'bugs.gentoo.org': GentooBugzilla, |
489 | + 'bugzilla.redhat.com': RedHatBugzilla, |
490 | +} |
491 | + |
492 | + |
493 | +class BugzillaServiceNotExist(Exception): |
494 | + pass |
495 | + |
496 | + |
497 | +class BugzillaServiceDisabled(Exception): |
498 | + ''' Bugzilla service is disabled by user. ''' |
499 | + |
500 | + def __init__(self, domain, *args, **kwargs): |
501 | + self.message = '%s is disabled.' % domain |
502 | + super(BugzillaServiceDisabled, self).__init__(*args, **kwargs) |
503 | + |
504 | + |
505 | +class BugzillaServiceFactory(object): |
506 | + ''' Create a Bugzilla service using scheme and domain ''' |
507 | + |
508 | + @staticmethod |
509 | + def create(scheme, domain): |
510 | + if domain in services: |
511 | + service = services[domain] |
512 | + if not service.enabled: |
513 | + raise BugzillaServiceDisabled(domain) |
514 | + return services[domain](scheme, domain) |
515 | + else: |
516 | + raise BugzillaServiceNotExist(domain) |
Hi,
thanks for your patch. Please fix the following (style) errors:
$ make pep8 bugzilla/ notification. py:46:1: E302 expected 2 blank lines, found 1 bugzilla/ services. py:57:1: E302 expected 2 blank lines, found 1 bugzilla/ services. py:62:1: E302 expected 2 blank lines, found 1 bugzilla/ services. py:67:1: E302 expected 2 blank lines, found 1
GTG/plugins/
GTG/plugins/
GTG/plugins/
GTG/plugins/
$ make pyflakes bugzilla/ notification. py:27: local variable 'proc' is assigned to but never used
GTG/plugins/