summaryrefslogtreecommitdiffstats
path: root/bugzilla
diff options
context:
space:
mode:
Diffstat (limited to 'bugzilla')
-rw-r--r--bugzilla/__init__.py2
-rw-r--r--bugzilla/bugzilla3.py148
-rw-r--r--bugzilla/rhbugzilla.py150
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)