From: W. Trevor King Date: Sat, 28 Nov 2009 12:41:13 +0000 (-0500) Subject: Added Bug.merge() and Comment.merge(). X-Git-Tag: 1.0.0~59^2~76^2~12 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=3e050db00d2ffa2c011efc4d9b47d8edeac5c43c;p=be.git Added Bug.merge() and Comment.merge(). Added *.explicit_attrs list creation to Bug and Comment.from_xml(). Added match_alt_id keyword argumennt to .comment_from_uuid(). Removed extra enline following '' tag in Bug and Comment.xml(). --- diff --git a/libbe/bug.py b/libbe/bug.py index fecb9b7..23d5488 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -20,6 +20,7 @@ Define the Bug class for representing bugs. """ +import copy import os import os.path import errno @@ -302,7 +303,7 @@ class Bug(settings_object.SavedSettingsObject): if v is not None: lines.append(' <%s>%s' % (k,xml.sax.saxutils.escape(v),k)) for estr in self.extra_strings: - lines.append(' %s\n' % estr) + lines.append(' %s' % estr) if show_comments == True: comout = self.comment_root.xml_thread(indent=indent+2, auto_name_map=True, @@ -332,6 +333,8 @@ class Bug(settings_object.SavedSettingsObject): >>> bugB.uuid = bugB.alt_id >>> bugB.xml(shortname="bug-1") == xml True + >>> bugB.explicit_attrs # doctest: +NORMALIZE_WHITESPACE + ['uuid', 'severity', 'status', 'creator', 'created', 'summary'] """ if type(xml_string) == types.UnicodeType: xml_string = xml_string.strip().encode('unicode_escape') @@ -340,8 +343,9 @@ class Bug(settings_object.SavedSettingsObject): raise utility.InvalidXML( \ 'bug', bug, 'root element must be ') tags=['uuid','short-name','severity','status','assigned','target', - 'reporter', 'creator', 'created', 'summary', 'extra-string', + 'reporter', 'creator','created','summary','extra-string', 'comment'] + self.explicit_attrs = [] uuid = None estrs = [] for child in bug.getchildren(): @@ -353,23 +357,120 @@ class Bug(settings_object.SavedSettingsObject): else: text = xml.sax.saxutils.unescape(child.text) text = text.decode('unicode_escape').strip() - if child.tag == "uuid": + if child.tag == 'uuid': uuid = text continue # don't set the bug's uuid tag. - if child.tag == 'extra-string': + elif child.tag == 'extra-string': estrs.append(text) continue # don't set the bug's extra_string yet. - else: - attr_name = child.tag.replace('-','_') + attr_name = child.tag.replace('-','_') + self.explicit_attrs.append(attr_name) setattr(self, attr_name, text) elif verbose == True: print >> sys.stderr, "Ignoring unknown tag %s in %s" \ % (child.tag, comment.tag) - if uuid not in [None, self.uuid]: + if uuid != self.uuid: if not hasattr(self, 'alt_id') or self.alt_id == None: self.alt_id = uuid self.extra_strings = estrs + def merge(self, other, allow_changes=True, allow_new_comments=True): + """ + Merge info from other into this bug. Overrides any attributes + in self that are listed in other.explicit_attrs. + >>> bugA = Bug(uuid='0123', summary='Need to test Bug.merge()') + >>> bugA.date = 'Thu, 01 Jan 1970 00:00:00 +0000' + >>> bugA.creator = 'Frank' + >>> bugA.extra_strings += ['TAG: very helpful'] + >>> bugA.extra_strings += ['TAG: favorite'] + >>> commA = bugA.comment_root.new_reply(body='comment A') + >>> commA.uuid = 'uuid-commA' + >>> bugB = Bug(uuid='3210', summary='More tests for Bug.merge()') + >>> bugB.date = 'Fri, 02 Jan 1970 00:00:00 +0000' + >>> bugB.creator = 'John' + >>> bugB.explicit_attrs = ['creator', 'summary'] + >>> bugB.extra_strings += ['TAG: very helpful'] + >>> bugB.extra_strings += ['TAG: useful'] + >>> commB = bugB.comment_root.new_reply(body='comment B') + >>> commB.uuid = 'uuid-commB' + >>> bugA.merge(bugB, allow_changes=False) + Traceback (most recent call last): + ... + ValueError: Merge would change creator "Frank"->"John" for bug 0123 + >>> bugA.merge(bugB, allow_new_comments=False) + Traceback (most recent call last): + ... + ValueError: Merge would add comment uuid-commB (alt: None) to bug 0123 + >>> bugA.merge(bugB) + >>> print bugA.xml(show_comments=True) # doctest: +ELLIPSIS + + 0123 + 0123 + minor + open + John + ... + More tests for Bug.merge() + TAG: favorite + TAG: useful + TAG: very helpful + + uuid-commA + 0123:1 + + ... + text/plain + comment A + + + uuid-commB + 0123:2 + + ... + text/plain + comment B + + + """ + for attr in other.explicit_attrs: + old = getattr(self, attr) + new = getattr(other, attr) + if old != new: + if allow_changes == True: + setattr(self, attr, new) + else: + raise ValueError, \ + 'Merge would change %s "%s"->"%s" for bug %s' \ + % (attr, old, new, self.uuid) + if allow_changes == False and len(other.extra_strings) > 0: + raise ValueError, \ + 'Merge would change extra_strings for bug %s' % self.uuid + for estr in other.extra_strings: + if not estr in self.extra_strings: + self.extra_strings.append(estr) + import sys + for o_comm in other.comments(): + s_comm = None + try: + s_comm = self.comment_root.comment_from_uuid(o_comm.uuid) + except KeyError, e: + try: + s_comm = self.comment_root.comment_from_uuid(o_comm.alt_id) + except KeyError, e: + pass + if s_comm == None: + if allow_new_comments == False: + raise ValueError, \ + 'Merge would add comment %s (alt: %s) to bug %s' \ + % (o_comm.uuid, o_comm.alt_id, self.uuid) + o_comm_copy = copy.copy(o_comm) + o_comm_copy.bug = self + print >> sys.stderr, "add comment %s" % o_comm.uuid + self.comment_root.add_reply(o_comm_copy) + else: + print >> sys.stderr, "merge comment %s into %s" % (o_comm.uuid, s_comm.uuid) + s_comm.merge(o_comm, allow_changes=allow_changes) + def string(self, shortlist=False, show_comments=False): if self.bugdir == None: shortname = self.uuid @@ -493,8 +594,8 @@ class Bug(settings_object.SavedSettingsObject): return self.comment_root.comment_from_shortname(shortname, *args, **kwargs) - def comment_from_uuid(self, uuid): - return self.comment_root.comment_from_uuid(uuid) + def comment_from_uuid(self, uuid, *args, **kwargs): + return self.comment_root.comment_from_uuid(uuid, *args, **kwargs) def comment_shortnames(self, shortname=None): """ diff --git a/libbe/comment.py b/libbe/comment.py index 5cc43c4..1adb6f4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -344,7 +344,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): if v != None: lines.append(' <%s>%s' % (k,xml.sax.saxutils.escape(v),k)) for estr in self.extra_strings: - lines.append(' %s\n' % estr) + lines.append(' %s' % estr) lines.append('') istring = ' '*indent sep = '\n' + istring @@ -362,6 +362,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> xml = commA.xml(shortname="com-1") >>> commB = Comment() >>> commB.from_xml(xml, verbose=True) + >>> commB.explicit_attrs + ['author', 'date', 'content_type', 'body', 'alt_id'] >>> commB.xml(shortname="com-1") == xml False >>> commB.uuid = commB.alt_id @@ -377,6 +379,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): 'comment', comment, 'root element must be ') tags=['uuid','alt-id','in-reply-to','author','date','content-type', 'body','extra-string'] + self.explicit_attrs = [] uuid = None body = None estrs = [] @@ -392,19 +395,21 @@ class Comment(Tree, settings_object.SavedSettingsObject): if child.tag == 'uuid': uuid = text continue # don't set the comment's uuid tag. - if child.tag == 'body': + elif child.tag == 'body': body = text + self.explicit_attrs.append(child.tag) continue # don't set the comment's body yet. - if child.tag == 'extra-string': + elif child.tag == 'extra-string': estrs.append(text) continue # don't set the comment's extra_string yet. - else: - attr_name = child.tag.replace('-','_') + attr_name = child.tag.replace('-','_') + self.explicit_attrs.append(attr_name) setattr(self, attr_name, text) elif verbose == True: print >> sys.stderr, 'Ignoring unknown tag %s in %s' \ % (child.tag, comment.tag) - if self.alt_id == None and uuid not in [None, self.uuid]: + if self.alt_id == None: + self.explicit_attrs.append('alt_id') self.alt_id = uuid if body != None: if self.content_type.startswith('text/'): @@ -413,6 +418,58 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.body = base64.decodestring(body) self.extra_strings = estrs + def merge(self, other, allow_changes=True): + """ + Merge info from other into this comment. Overrides any + attributes in self that are listed in other.explicit_attrs. + >>> commA = Comment(bug=None, body='Some insightful remarks') + >>> commA.uuid = '0123' + >>> commA.date = 'Thu, 01 Jan 1970 00:00:00 +0000' + >>> commA.author = 'Frank' + >>> commA.extra_strings += ['TAG: very helpful'] + >>> commA.extra_strings += ['TAG: favorite'] + >>> commB = Comment(bug=None, body='More insightful remarks') + >>> commB.uuid = '3210' + >>> commB.date = 'Fri, 02 Jan 1970 00:00:00 +0000' + >>> commB.author = 'John' + >>> commB.explicit_attrs = ['author', 'body'] + >>> commB.extra_strings += ['TAG: very helpful'] + >>> commB.extra_strings += ['TAG: useful'] + >>> commA.merge(commB, allow_changes=False) + Traceback (most recent call last): + ... + ValueError: Merge would change author "Frank"->"John" for comment 0123 + >>> commA.merge(commB) + >>> print commA.xml() + + 0123 + 0123 + John + Thu, 01 Jan 1970 00:00:00 +0000 + text/plain + More insightful remarks + TAG: favorite + TAG: useful + TAG: very helpful + + """ + for attr in other.explicit_attrs: + old = getattr(self, attr) + new = getattr(other, attr) + if old != new: + if allow_changes == True: + setattr(self, attr, new) + else: + raise ValueError, \ + 'Merge would change %s "%s"->"%s" for comment %s' \ + % (attr, old, new, self.uuid) + if allow_changes == False and len(other.extra_strings) > 0: + raise ValueError, \ + 'Merge would change extra_strings for comment %s' % self.uuid + for estr in other.extra_strings: + if not estr in self.extra_strings: + self.extra_strings.append(estr) + def string(self, indent=0, shortname=None): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") @@ -674,7 +731,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): raise InvalidShortname(comment_shortname, list(self.comment_shortnames(*args, **kwargs))) - def comment_from_uuid(self, uuid): + def comment_from_uuid(self, uuid, match_alt_id=True): """ Use a comment shortname to look up a comment. >>> a = Comment(bug=None, uuid="a") @@ -684,13 +741,24 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> c.uuid = "c" >>> d = a.new_reply() >>> d.uuid = "d" + >>> d.alt_id = "d-alt" >>> comm = a.comment_from_uuid("d") >>> id(comm) == id(d) True + >>> comm = a.comment_from_uuid("d-alt") + >>> id(comm) == id(d) + True + >>> comm = a.comment_from_uuid(None, match_alt_id=False) + Traceback (most recent call last): + ... + KeyError: None """ for comment in self.traverse(): if comment.uuid == uuid: return comment + if match_alt_id == True and uuid != None \ + and comment.alt_id == uuid: + return comment raise KeyError(uuid) def cmp_attr(comment_1, comment_2, attr, invert=False):