diff options
-rw-r--r-- | bugzilla.py | 124 |
1 files changed, 112 insertions, 12 deletions
diff --git a/bugzilla.py b/bugzilla.py index 05215a3..6326649 100644 --- a/bugzilla.py +++ b/bugzilla.py @@ -102,9 +102,9 @@ class Bugzilla(object): # Set querydata and querydefaults as properties so they auto-create # themselves when touched by a user. This bit was lifted from YumBase, # because skvidal is much smarter than I am. - querydata = property(fget=lambda self: self.__get_queryinfo()[0], + querydata = property(fget=lambda self: self._get_queryinfo()[0], fdel=lambda self: setattr(self,"_querydata",None)) - querydefaults = property(fget=lambda self: self.__get_queryinfo()[1], + querydefaults = property(fget=lambda self: self._get_queryinfo()[1], fdel=lambda self: setattr(self,"_querydefaults",None)) def getproducts(self,force_refresh=False): @@ -127,12 +127,20 @@ class Bugzilla(object): return self._components[product] # TODO - add a .components property that acts like a dict? - #---- Methods for reading bugs + #---- Methods for reading bugs and bug info - def getbug(self,id): + # Return raw dicts + def _getbug(self,id): '''Return a dict of full bug info for the given bug id''' return self._proxy.bugzilla.getBug(id, self.user, self.password) + def _getbugsimple(self,id): + '''Return a short dict of simple bug info for the given bug id''' + return self._proxy.bugzilla.getBugSimple(id, self.user, self.password) + # TODO return a Bug object instead + def getbug(self,id): + '''Return a dict of full bug info for the given bug id''' + return self._proxy.bugzilla.getBug(id, self.user, self.password) def getbugsimple(self,id): '''Return a short dict of simple bug info for the given bug id''' return self._proxy.bugzilla.getBugSimple(id, self.user, self.password) @@ -154,10 +162,18 @@ class Bugzilla(object): ''' return self._proxy.bugzilla.runQuery(query,self.user,self.password) - def getwhiteboard(self,id,which): - '''Get the current value of the whiteboard specified by 'which' on bug - with the the given id.''' - raise NotImplementedError + def query_comments(self,product,version,component,string,matchtype='allwordssubstr'): + '''Convenience method - query for bugs filed against the given + product, version, and component whose comments match the given string. + matchtype specifies the type of match to be done. matchtype may be + any of the types listed in querydefaults['long_desc_type_list'], e.g.: + ['allwordssubstr','anywordssubstr','substring','casesubstring', + 'allwords','anywords','regexp','notregexp'] + Return value is the same as with query(). + ''' + q = {'product':product,'version':version,'component':component, + 'long_desc':string,'long_desc_type':matchtype} + return self.query(q) #---- Methods for modifying existing bugs @@ -178,9 +194,6 @@ class Bugzilla(object): def closebug(self,id): raise NotImplementedError - - def setwhiteboard(self,id,type,data): - raise NotImplmementedError def setassignee(self,id,assignee): raise NotImplementedError @@ -191,6 +204,13 @@ class Bugzilla(object): def updatecc(self,id,cclist): raise NotImplementedError + 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.''' + data = {'type':which,'text':text,'action':action} + self._proxy.bugzilla.updateWhiteboard(id,data,self.user,self.password) + #---- Methods for working with attachments def __attachment_encode(self,fh): @@ -265,7 +285,6 @@ class Bugzilla(object): '''Create a bug with the given info. Returns the bug ID.''' raise NotImplementedError - # TODO: allow simple 'tagging' by adding/removing text to whiteboard(s) # TODO: flag handling? class CookieTransport(xmlrpclib.Transport): @@ -340,3 +359,84 @@ class SafeCookieTransport(xmlrpclib.SafeTransport,CookieTransport): '''SafeTransport subclass that supports cookies.''' scheme = 'https' request = CookieTransport.request + +class Bug(object): + '''A container object for a bug report. Requires a Bugzilla instance - + every Bug is on a Bugzilla, obviously. + Optional keyword args: + dict=DICT - populate attributes with the result of a getBug() call + bug_id=ID - if dict does not contain bug_id, this is required before + you can read any attributes or make modifications to this + bug. + ''' + def __init__(self,bugzilla,**kwargs): + self.bugzilla = bugzilla + if 'dict' in kwargs: + self.__dict__.update(kwargs['dict']) + if 'bug_id' in kwargs: + setattr(self,'bug_id',kwargs['bug_id']) + + def __getattr__(self,name): + if name not in ('__members__','__methods__','trait_names', + '_getAttributeNames') and not name.endswith(')'): + # FIXME: that .endswith hack is an extremely stupid way to figure + # out if we're checking on a method call. Find a smarter one! + r = self.bugzilla._getbug(self.bug_id) + self.__dict__.update(r) + if name in self.__dict__: + return self.__dict__[name] + raise AttributeError + + def refreshattr(self,name): + delattr(self,name) + r = self.bugzilla.query({'bug_id':self.bug_id,'column_list':[name]}) + self.__dict__.update(r['bugs'][0]) + return self.__dict__[name] + + def getwhiteboard(self,which='status'): + '''Get the current value of the whiteboard specified by 'which'. + Known whiteboard names: 'status','internal','devel','qa'. + Defaults to the 'status' whiteboard. + ''' + return getattr(self,"%s_whiteboard" % which) + def _dowhiteboard(self,text,which,action): + '''Actually does the updateWhiteboard call to perform the given action + (append,prepend,overwrite) with the given text on the given whiteboard + for the given bug.''' + self.bugzilla._updatewhiteboard(self.bug_id,text,which,action) + # NOTE: right now we don't get to the refreshattr here, because the + # server is throwing an XMLRPC fault on _updatewhiteboard. + # It still *works* - you can manually refresh and see the change - but + # for some reason it's complaining. Might be a bugzilla bug. + self.refreshattr("%s_whiteboard" % which) + def appendwhiteboard(self,text,which='status'): + '''Append the given text (with a space before it) to the given + whiteboard. Defaults to using status_whiteboard.''' + self._dowhiteboard(text,which,'append') + def prependwhiteboard(self,text,which='status'): + '''Prepend the given text (with a space following it) to the given + whiteboard. Defaults to using status_whiteboard.''' + self._dowhiteboard(text,which,'prepend') + def setwhiteboard(self,text,which='status'): + '''Overwrites the contents of the given whiteboard with the given text. + Defaults to using status_whiteboard.''' + self._dowhiteboard(text,which,'overwrite') + def addtag(self,tag,which='status'): + '''Adds the given tag to the given bug.''' + whiteboard = self.getwhiteboard(which) + if whiteboard: + self.appendwhiteboard(tag,which) + else: + self.setwhiteboard(tag,which) + def tags(self,which='status'): + '''Get a list of tags (basically just whitespace-split the given + whiteboard)''' + return self.getwhiteboard(which).split() + def deltag(self,tag,which='status'): + '''Removes the given tag from the given bug.''' + tags = self.gettags(which) + tags.remove(tag) + self.setwhiteboard(' '.join(tags),which) + +# TODO: add a sync() method that writes the changed data in the Bug object +# back to Bugzilla. Someday. |