From 2cbd34ec163f503648245f2076f5601ed9498de6 Mon Sep 17 00:00:00 2001 From: Will Woods Date: Tue, 19 Aug 2008 14:06:18 -0400 Subject: Move bugzilla3.RHBugzilla32 to rhbugzilla.RHBugzilla3, and import that in toplevel bugzilla module --- bugzilla/__init__.py | 2 +- bugzilla/bugzilla3.py | 148 ------------------------------------------------ bugzilla/rhbugzilla.py | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 149 deletions(-) diff --git a/bugzilla/__init__.py b/bugzilla/__init__.py index 2e5ee75..8dc06db 100644 --- a/bugzilla/__init__.py +++ b/bugzilla/__init__.py @@ -10,7 +10,7 @@ # the full text of the license. from bugzilla3 import Bugzilla3, Bugzilla32 -from rhbugzilla import RHBugzilla +from rhbugzilla import RHBugzilla, RHBugzilla3 import xmlrpclib import logging log = logging.getLogger("bugzilla") diff --git a/bugzilla/bugzilla3.py b/bugzilla/bugzilla3.py index ddd310d..ea017e5 100644 --- a/bugzilla/bugzilla3.py +++ b/bugzilla/bugzilla3.py @@ -139,151 +139,3 @@ class Bugzilla32(Bugzilla3): 'private':private, 'work_time':worktime}) -class RHBugzilla32(Bugzilla32): - '''Concrete implementation of the Bugzilla protocol. This one uses the - methods provided by Red Hat's Bugzilla 3.1.4+ instance, which is a superset - of the Bugzilla 3.2 methods. The additional methods (Bug.search, Bug.update) - should make their way into a later upstream Bugzilla release (probably 4.0). - - Note that RHBZ3 *also* supports most of the old RHBZ methods, under the - 'bugzilla' namespace. - - This class was written using bugzilla.redhat.com's API docs: - https://bugzilla.redhat.com/docs/en/html/api/ - ''' - - version = '0.1' - user_agent = bugzilla.base.user_agent + ' RHBugzilla32/%s' % version - - def _query(self,query): - '''Query bugzilla and return a list of matching bugs. - query must be a dict with fields like those in in querydata['fields']. - You can also pass in keys called 'quicksearch' or 'savedsearch' - - 'quicksearch' will do a quick keyword search like the simple search - on the Bugzilla home page. - 'savedsearch' should be the name of a previously-saved search to - execute. You need to be logged in for this to work. - Returns a dict like this: {'bugs':buglist, - 'sql':querystring} - buglist is a list of dicts describing bugs, and 'sql' contains the SQL - generated by executing the search. - ''' - return self._proxy.Bug.search(query) - - #---- Methods for updating bugs. - - def _update_bugs(self,ids,updates): - '''Update the given fields with the given data in one or more bugs. - ids should be a list of integers or strings, representing bug ids or - aliases. - updates is a dict containing pairs like so: {'fieldname':'newvalue'} - ''' - # TODO document changeable fields & return values - # TODO I think we need to catch XMLRPC exceptions to get a useful - # return value - return self._proxy.Bug.update({'ids':ids,'updates':updates}) - - def _update_bug(self,id,updates): - '''Update a single bug, specified by integer ID or (string) bug alias. - Really just a convenience method for _update_bugs(ids=[id],updates)''' - return self._update_bugs(ids=[id],updates=updates) - - # Eventually - when RHBugzilla is well and truly obsolete - we'll delete - # all of these methods and refactor the Base Bugzilla object so all the bug - # modification calls go through _update_bug. - # Until then, all of these methods are basically just wrappers around it. - - # TODO: allow multiple bug IDs - - def _setstatus(self,id,status,comment='',private=False,private_in_it=False,nomail=False): - '''Set the status of the bug with the given ID.''' - update={'bug_status':status} - if comment: - update['comment'] = comment - return self._update_bug(id,update) - - def _closebug(self,id,resolution,dupeid,fixedin,comment,isprivate,private_in_it,nomail): - '''Close the given bug. This is the raw call, and no data checking is - done here. That's up to the closebug method. - Note that the private_in_it and nomail args are ignored.''' - update={'bug_status':'CLOSED','resolution':resolution} - if dupeid: - update['resolution'] = 'DUPLICATE' - update['dupe_id'] = dupeid - if fixedin: - update['fixed_in'] = fixedin - if comment: - update['comment'] = comment - if isprivate: - update['commentprivacy'] = True - return self._update_bug(id,update) - - def _setassignee(self,id,**data): - '''Raw xmlrpc call to set one of the assignee fields on a bug. - changeAssignment($id, $data, $username, $password) - data: 'assigned_to','reporter','qa_contact','comment' - returns: [$id, $mailresults]''' - # drop empty items - update = dict([(k,v) for k,v in data.iteritems() if v != '']) - return self._update_bug(id,update) - - def _updatedeps(self,id,blocked,dependson,action): - '''Update the deps (blocked/dependson) for the given bug. - blocked, dependson: list of bug ids/aliases - action: 'add' or 'delete' - ''' - if action not in ('add','delete'): - raise ValueError, "action must be 'add' or 'delete'" - update={'%s_blocked' % action: blocked, - '%s_dependson' % action: dependson} - self._update_bug(id,update) - - def _updatecc(self,id,cclist,action,comment='',nomail=False): - '''Updates the CC list using the action and account list specified. - cclist must be a list (not a tuple!) of addresses. - action may be 'add', 'delete', or 'overwrite'. - comment specifies an optional comment to add to the bug. - if mail is True, email will be generated for this change. - ''' - update = {} - if comment: - update['comment'] = comment - - if action in ('add','delete'): - update['%s_cc' % action] = cclist - self._update_bug(id,update) - elif action == 'overwrite': - r = self._getbug(id) - if 'cc' not in r: - raise AttributeError, "Can't find cc list in bug %s" % str(id) - self._updatecc(id,r['cc'],'delete') - self._updatecc(id,cclist,'add') - # FIXME we don't check inputs on other backend methods, maybe this - # is more appropriate in the public method(s) - else: - raise ValueError, "action must be 'add','delete', or 'overwrite'" - - def _updatewhiteboard(self,id,text,which,action): - '''Update the whiteboard given by 'which' for the given bug. - performs the given action (which may be 'append',' prepend', or - 'overwrite') using the given text. - - RHBZ3 Bug.update() only supports overwriting, so append/prepend - may cause two server roundtrips - one to fetch, and one to update. - ''' - if not which.endswith('_whiteboard'): - which = which + '_whiteboard' - update = {} - if action == 'overwrite': - update[which] = text - else: - r = self._getbug(id) - if which not in r: - raise ValueError, "No such whiteboard %s in bug %s" % \ - (which,str(id)) - wb = r[which] - if action == 'prepend': - update[which] = text+' '+wb - elif action == 'append': - update[which] = wb+' '+text - self._update_bug(id,update) diff --git a/bugzilla/rhbugzilla.py b/bugzilla/rhbugzilla.py index 6ea8902..647d8bc 100644 --- a/bugzilla/rhbugzilla.py +++ b/bugzilla/rhbugzilla.py @@ -10,6 +10,7 @@ # the full text of the license. import bugzilla.base +from bugzilla3 import Bugzilla32 version = '0.2' user_agent = bugzilla.base.user_agent + ' RHBugzilla/%s' % version @@ -259,3 +260,152 @@ class RHBugzilla(bugzilla.base.BugzillaBase): Returns bug_id''' r = self._proxy.bugzilla.createBug(data) return r[0] + +class RHBugzilla3(Bugzilla32, RHBugzilla): + '''Concrete implementation of the Bugzilla protocol. This one uses the + methods provided by Red Hat's Bugzilla 3.1.4+ instance, which is a superset + of the Bugzilla 3.2 methods. The additional methods (Bug.search, Bug.update) + should make their way into a later upstream Bugzilla release (probably 4.0). + + Note that RHBZ3 *also* supports most of the old RHBZ methods, under the + 'bugzilla' namespace. + + This class was written using bugzilla.redhat.com's API docs: + https://bugzilla.redhat.com/docs/en/html/api/ + ''' + + version = '0.1' + user_agent = bugzilla.base.user_agent + ' RHBugzilla3/%s' % version + + def _query(self,query): + '''Query bugzilla and return a list of matching bugs. + query must be a dict with fields like those in in querydata['fields']. + You can also pass in keys called 'quicksearch' or 'savedsearch' - + 'quicksearch' will do a quick keyword search like the simple search + on the Bugzilla home page. + 'savedsearch' should be the name of a previously-saved search to + execute. You need to be logged in for this to work. + Returns a dict like this: {'bugs':buglist, + 'sql':querystring} + buglist is a list of dicts describing bugs, and 'sql' contains the SQL + generated by executing the search. + ''' + return self._proxy.Bug.search(query) + + #---- Methods for updating bugs. + + def _update_bugs(self,ids,updates): + '''Update the given fields with the given data in one or more bugs. + ids should be a list of integers or strings, representing bug ids or + aliases. + updates is a dict containing pairs like so: {'fieldname':'newvalue'} + ''' + # TODO document changeable fields & return values + # TODO I think we need to catch XMLRPC exceptions to get a useful + # return value + return self._proxy.Bug.update({'ids':ids,'updates':updates}) + + def _update_bug(self,id,updates): + '''Update a single bug, specified by integer ID or (string) bug alias. + Really just a convenience method for _update_bugs(ids=[id],updates)''' + return self._update_bugs(ids=[id],updates=updates) + + # Eventually - when RHBugzilla is well and truly obsolete - we'll delete + # all of these methods and refactor the Base Bugzilla object so all the bug + # modification calls go through _update_bug. + # Until then, all of these methods are basically just wrappers around it. + + # TODO: allow multiple bug IDs + + def _setstatus(self,id,status,comment='',private=False,private_in_it=False,nomail=False): + '''Set the status of the bug with the given ID.''' + update={'bug_status':status} + if comment: + update['comment'] = comment + return self._update_bug(id,update) + + def _closebug(self,id,resolution,dupeid,fixedin,comment,isprivate,private_in_it,nomail): + '''Close the given bug. This is the raw call, and no data checking is + done here. That's up to the closebug method. + Note that the private_in_it and nomail args are ignored.''' + update={'bug_status':'CLOSED','resolution':resolution} + if dupeid: + update['resolution'] = 'DUPLICATE' + update['dupe_id'] = dupeid + if fixedin: + update['fixed_in'] = fixedin + if comment: + update['comment'] = comment + if isprivate: + update['commentprivacy'] = True + return self._update_bug(id,update) + + def _setassignee(self,id,**data): + '''Raw xmlrpc call to set one of the assignee fields on a bug. + changeAssignment($id, $data, $username, $password) + data: 'assigned_to','reporter','qa_contact','comment' + returns: [$id, $mailresults]''' + # drop empty items + update = dict([(k,v) for k,v in data.iteritems() if v != '']) + return self._update_bug(id,update) + + def _updatedeps(self,id,blocked,dependson,action): + '''Update the deps (blocked/dependson) for the given bug. + blocked, dependson: list of bug ids/aliases + action: 'add' or 'delete' + ''' + if action not in ('add','delete'): + raise ValueError, "action must be 'add' or 'delete'" + update={'%s_blocked' % action: blocked, + '%s_dependson' % action: dependson} + self._update_bug(id,update) + + def _updatecc(self,id,cclist,action,comment='',nomail=False): + '''Updates the CC list using the action and account list specified. + cclist must be a list (not a tuple!) of addresses. + action may be 'add', 'delete', or 'overwrite'. + comment specifies an optional comment to add to the bug. + if mail is True, email will be generated for this change. + ''' + update = {} + if comment: + update['comment'] = comment + + if action in ('add','delete'): + update['%s_cc' % action] = cclist + self._update_bug(id,update) + elif action == 'overwrite': + r = self._getbug(id) + if 'cc' not in r: + raise AttributeError, "Can't find cc list in bug %s" % str(id) + self._updatecc(id,r['cc'],'delete') + self._updatecc(id,cclist,'add') + # FIXME we don't check inputs on other backend methods, maybe this + # is more appropriate in the public method(s) + else: + raise ValueError, "action must be 'add','delete', or 'overwrite'" + + def _updatewhiteboard(self,id,text,which,action): + '''Update the whiteboard given by 'which' for the given bug. + performs the given action (which may be 'append',' prepend', or + 'overwrite') using the given text. + + RHBZ3 Bug.update() only supports overwriting, so append/prepend + may cause two server roundtrips - one to fetch, and one to update. + ''' + if not which.endswith('_whiteboard'): + which = which + '_whiteboard' + update = {} + if action == 'overwrite': + update[which] = text + else: + r = self._getbug(id) + if which not in r: + raise ValueError, "No such whiteboard %s in bug %s" % \ + (which,str(id)) + wb = r[which] + if action == 'prepend': + update[which] = text+' '+wb + elif action == 'append': + update[which] = wb+' '+text + self._update_bug(id,update) -- cgit