aboutsummaryrefslogtreecommitdiffstats
path: root/libbe
diff options
context:
space:
mode:
Diffstat (limited to 'libbe')
-rw-r--r--libbe/bug.py262
-rw-r--r--libbe/bugdir.py12
-rw-r--r--libbe/comment.py158
-rw-r--r--libbe/diff.py4
-rw-r--r--libbe/subproc.py2
5 files changed, 358 insertions, 80 deletions
diff --git a/libbe/bug.py b/libbe/bug.py
index fecb9b7..897d841 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
@@ -227,7 +228,7 @@ class Bug(settings_object.SavedSettingsObject):
@Property
@cached_property(generator=_get_comment_root)
@local_property("comment_root")
- @doc_property(doc="The trunk of the comment tree")
+ @doc_property(doc="The trunk of the comment tree. We use a dummy root comment by default, because there can be several comment threads rooted on the same parent bug. To simplify comment interaction, we condense these threads into a single thread with a Comment dummy root.")
def comment_root(): return {}
def _get_vcs(self):
@@ -302,7 +303,7 @@ class Bug(settings_object.SavedSettingsObject):
if v is not None:
lines.append(' <%s>%s</%s>' % (k,xml.sax.saxutils.escape(v),k))
for estr in self.extra_strings:
- lines.append(' <extra-string>%s</extra-string>\n' % estr)
+ lines.append(' <extra-string>%s</extra-string>' % estr)
if show_comments == True:
comout = self.comment_root.xml_thread(indent=indent+2,
auto_name_map=True,
@@ -324,51 +325,282 @@ class Bug(settings_object.SavedSettingsObject):
>>> commA = bugA.comment_root.new_reply(body='comment A')
>>> commB = bugA.comment_root.new_reply(body='comment B')
>>> commC = commA.new_reply(body='comment C')
- >>> xml = bugA.xml(shortname="bug-1")
+ >>> xml = bugA.xml(shortname="bug-1", show_comments=True)
>>> bugB = Bug()
>>> bugB.from_xml(xml, verbose=True)
- >>> bugB.xml(shortname="bug-1") == xml
+ >>> bugB.xml(shortname="bug-1", show_comments=True) == xml
False
>>> bugB.uuid = bugB.alt_id
- >>> bugB.xml(shortname="bug-1") == xml
+ >>> for comm in bugB.comments():
+ ... comm.uuid = comm.alt_id
+ ... comm.alt_id = None
+ >>> bugB.xml(shortname="bug-1", show_comments=True) == xml
True
+ >>> bugB.explicit_attrs # doctest: +NORMALIZE_WHITESPACE
+ ['severity', 'status', 'creator', 'created', 'summary']
+ >>> len(list(bugB.comments()))
+ 3
"""
if type(xml_string) == types.UnicodeType:
xml_string = xml_string.strip().encode('unicode_escape')
- bug = ElementTree.XML(xml_string)
+ if hasattr(xml_string, 'getchildren'): # already an ElementTree Element
+ bug = xml_string
+ else:
+ bug = ElementTree.XML(xml_string)
if bug.tag != 'bug':
raise utility.InvalidXML( \
'bug', bug, 'root element must be <comment>')
tags=['uuid','short-name','severity','status','assigned','target',
- 'reporter', 'creator', 'created', 'summary', 'extra-string',
- 'comment']
+ 'reporter', 'creator','created','summary','extra-string']
+ self.explicit_attrs = []
uuid = None
estrs = []
+ comments = []
for child in bug.getchildren():
if child.tag == 'short-name':
pass
+ elif child.tag == 'comment':
+ comm = comment.Comment(bug=self)
+ comm.from_xml(child)
+ comments.append(comm)
+ continue
elif child.tag in tags:
if child.text == None or len(child.text) == 0:
text = settings_object.EMPTY
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
+ self.add_comments(comments)
+
+ def add_comment(self, comment, *args, **kwargs):
+ """
+ Add a comment too the current bug, under the parent specified
+ by comment.in_reply_to.
+ Note: If a bug uuid is given, set .alt_id to it's value.
+ >>> bugA = Bug(uuid='0123', summary='Need to test Bug.add_comment()')
+ >>> bugA.creator = 'Jack'
+ >>> commA = bugA.comment_root.new_reply(body='comment A')
+ >>> commA.uuid = 'commA'
+ >>> commB = comment.Comment(body='comment B')
+ >>> commB.uuid = 'commB'
+ >>> bugA.add_comment(commB)
+ >>> commC = comment.Comment(body='comment C')
+ >>> commC.uuid = 'commC'
+ >>> commC.in_reply_to = commA.uuid
+ >>> bugA.add_comment(commC)
+ >>> print bugA.xml(shortname="bug-1", show_comments=True) # doctest: +ELLIPSIS
+ <bug>
+ <uuid>0123</uuid>
+ <short-name>bug-1</short-name>
+ <severity>minor</severity>
+ <status>open</status>
+ <creator>Jack</creator>
+ <created>...</created>
+ <summary>Need to test Bug.add_comment()</summary>
+ <comment>
+ <uuid>commA</uuid>
+ <short-name>bug-1:1</short-name>
+ <author></author>
+ <date>...</date>
+ <content-type>text/plain</content-type>
+ <body>comment A</body>
+ </comment>
+ <comment>
+ <uuid>commC</uuid>
+ <short-name>bug-1:2</short-name>
+ <in-reply-to>commA</in-reply-to>
+ <author></author>
+ <date>...</date>
+ <content-type>text/plain</content-type>
+ <body>comment C</body>
+ </comment>
+ <comment>
+ <uuid>commB</uuid>
+ <short-name>bug-1:3</short-name>
+ <author></author>
+ <date>...</date>
+ <content-type>text/plain</content-type>
+ <body>comment B</body>
+ </comment>
+ </bug>
+ """
+ self.add_comments([comment], **kwargs)
+
+ def add_comments(self, comments, default_parent=None,
+ ignore_missing_references=False):
+ """
+ Convert a raw list of comments to single root comment. If a
+ comment does not specify a parent with .in_reply_to, the
+ parent defaults to .comment_root, but you can specify another
+ default parent via default_parent.
+ """
+ uuid_map = {}
+ if default_parent == None:
+ default_parent = self.comment_root
+ for c in list(self.comments()) + comments:
+ assert c.uuid != None
+ assert c.uuid not in uuid_map
+ uuid_map[c.uuid] = c
+ if c.alt_id != None:
+ uuid_map[c.alt_id] = c
+ uuid_map[None] = self.comment_root
+ if default_parent != self.comment_root:
+ assert default_parent.uuid in uuid_map, default_parent
+ for c in comments:
+ if c.in_reply_to == None \
+ and default_parent.uuid != comment.INVALID_UUID:
+ c.in_reply_to = default_parent.uuid
+ elif c.in_reply_to == comment.INVALID_UUID:
+ c.in_reply_to = None
+ try:
+ parent = uuid_map[c.in_reply_to]
+ except KeyError:
+ if ignore_missing_references == True:
+ print >> sys.stderr, \
+ "Ignoring missing reference to %s" % c.in_reply_to
+ parent = default_parent
+ if parent.uuid != comment.INVALID_UUID:
+ c.in_reply_to = parent.uuid
+ else:
+ raise comment.MissingReference(c)
+ c.bug = self
+ parent.append(c)
+
+ def merge(self, other, accept_changes=True,
+ accept_extra_strings=True, accept_comments=True,
+ change_exception=False):
+ """
+ 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, accept_changes=False, accept_extra_strings=False,
+ ... accept_comments=False, change_exception=False)
+ >>> print bugA.creator
+ Frank
+ >>> bugA.merge(bugB, accept_changes=False, accept_extra_strings=False,
+ ... accept_comments=False, change_exception=True)
+ Traceback (most recent call last):
+ ...
+ ValueError: Merge would change creator "Frank"->"John" for bug 0123
+ >>> print bugA.creator
+ Frank
+ >>> bugA.merge(bugB, accept_changes=True, accept_extra_strings=False,
+ ... accept_comments=False, change_exception=True)
+ Traceback (most recent call last):
+ ...
+ ValueError: Merge would add extra string "TAG: useful" for bug 0123
+ >>> print bugA.creator
+ John
+ >>> print bugA.extra_strings
+ ['TAG: favorite', 'TAG: very helpful']
+ >>> bugA.merge(bugB, accept_changes=True, accept_extra_strings=True,
+ ... accept_comments=False, change_exception=True)
+ Traceback (most recent call last):
+ ...
+ ValueError: Merge would add comment uuid-commB (alt: None) to bug 0123
+ >>> print bugA.extra_strings
+ ['TAG: favorite', 'TAG: useful', 'TAG: very helpful']
+ >>> bugA.merge(bugB, accept_changes=True, accept_extra_strings=True,
+ ... accept_comments=True, change_exception=True)
+ >>> print bugA.xml(show_comments=True) # doctest: +ELLIPSIS
+ <bug>
+ <uuid>0123</uuid>
+ <short-name>0123</short-name>
+ <severity>minor</severity>
+ <status>open</status>
+ <creator>John</creator>
+ <created>...</created>
+ <summary>More tests for Bug.merge()</summary>
+ <extra-string>TAG: favorite</extra-string>
+ <extra-string>TAG: useful</extra-string>
+ <extra-string>TAG: very helpful</extra-string>
+ <comment>
+ <uuid>uuid-commA</uuid>
+ <short-name>0123:1</short-name>
+ <author></author>
+ <date>...</date>
+ <content-type>text/plain</content-type>
+ <body>comment A</body>
+ </comment>
+ <comment>
+ <uuid>uuid-commB</uuid>
+ <short-name>0123:2</short-name>
+ <author></author>
+ <date>...</date>
+ <content-type>text/plain</content-type>
+ <body>comment B</body>
+ </comment>
+ </bug>
+ """
+ for attr in other.explicit_attrs:
+ old = getattr(self, attr)
+ new = getattr(other, attr)
+ if old != new:
+ if accept_changes == True:
+ setattr(self, attr, new)
+ elif change_exception == True:
+ raise ValueError, \
+ 'Merge would change %s "%s"->"%s" for bug %s' \
+ % (attr, old, new, self.uuid)
+ for estr in other.extra_strings:
+ if not estr in self.extra_strings:
+ if accept_extra_strings == True:
+ self.extra_strings.append(estr)
+ elif change_exception == True:
+ raise ValueError, \
+ 'Merge would add extra string "%s" for bug %s' \
+ % (estr, self.uuid)
+ for o_comm in other.comments():
+ 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:
+ s_comm = None
+ if s_comm == None:
+ if accept_comments == True:
+ o_comm_copy = copy.copy(o_comm)
+ o_comm_copy.bug = self
+ self.comment_root.add_reply(o_comm_copy)
+ elif change_exception == True:
+ raise ValueError, \
+ 'Merge would add comment %s (alt: %s) to bug %s' \
+ % (o_comm.uuid, o_comm.alt_id, self.uuid)
+ else:
+ s_comm.merge(o_comm, accept_changes=accept_changes,
+ accept_extra_strings=accept_extra_strings,
+ change_exception=change_exception)
def string(self, shortlist=False, show_comments=False):
if self.bugdir == None:
@@ -493,8 +725,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/bugdir.py b/libbe/bugdir.py
index 675b744..301ceb6 100644
--- a/libbe/bugdir.py
+++ b/libbe/bugdir.py
@@ -115,7 +115,7 @@ class BugDir (list, settings_object.SavedSettingsObject):
all bugs/comments/etc. that have been loaded into memory. If
you've been living in memory and want to move to
.sync_with_disk==True, but you're not sure if anything has been
- changed in memory, a call to save() immediately before the
+ changed in memory, a call to .save() immediately before the
.set_sync_with_disk(True) call is a safe move.
Regardless of .sync_with_disk, a call to .save() will write out
@@ -239,7 +239,7 @@ settings easy. Don't set this attribute. Set .vcs instead, and
map = {}
for bug in self:
map[bug.uuid] = bug
- for uuid in self.list_uuids():
+ for uuid in self.uuids():
if uuid not in map:
map[uuid] = None
self._bug_map_value = map # ._bug_map_value used by @local_property
@@ -483,7 +483,7 @@ settings easy. Don't set this attribute. Set .vcs instead, and
if self.sync_with_disk == False:
raise DiskAccessRequired("load all bugs")
self._clear_bugs()
- for uuid in self.list_uuids():
+ for uuid in self.uuids():
self._load_bug(uuid)
def save(self):
@@ -550,7 +550,7 @@ settings easy. Don't set this attribute. Set .vcs instead, and
# methods for managing bugs
- def list_uuids(self):
+ def uuids(self):
uuids = []
if self.sync_with_disk == True and os.path.exists(self.get_path()):
# list the uuids on disk
@@ -651,7 +651,7 @@ class SimpleBugDir (BugDir):
"""
For testing. Set sync_with_disk==False for a memory-only bugdir.
>>> bugdir = SimpleBugDir()
- >>> uuids = list(bugdir.list_uuids())
+ >>> uuids = list(bugdir.uuids())
>>> uuids.sort()
>>> print uuids
['a', 'b']
@@ -741,7 +741,7 @@ class BugDirTestCase(unittest.TestCase):
self.bugdir.new_bug(uuid="c", summary="Praying mantis")
length = len(self.bugdir)
self.failUnless(length == 3, "%d != 3 bugs" % length)
- uuids = list(self.bugdir.list_uuids())
+ uuids = list(self.bugdir.uuids())
self.failUnless(len(uuids) == 3, "%d != 3 uuids" % len(uuids))
self.failUnless(uuids == ["a","b","c"], str(uuids))
bugA = self.bugdir.bug_from_uuid("a")
diff --git a/libbe/comment.py b/libbe/comment.py
index 5cc43c4..c5f1cc9 100644
--- a/libbe/comment.py
+++ b/libbe/comment.py
@@ -65,53 +65,6 @@ class DiskAccessRequired (Exception):
INVALID_UUID = "!!~~\n INVALID-UUID \n~~!!"
-def list_to_root(comments, bug, root=None,
- ignore_missing_references=False):
- """
- Convert a raw list of comments to single root comment. We use a
- dummy root comment by default, because there can be several
- comment threads rooted on the same parent bug. To simplify
- comment interaction, we condense these threads into a single
- thread with a Comment dummy root. Can also be used to append
- a list of subcomments to a non-dummy root comment, so long as
- all the new comments are descendants of the root comment.
-
- No Comment method should use the dummy comment.
- """
- root_comments = []
- uuid_map = {}
- for comment in comments:
- assert comment.uuid != None
- uuid_map[comment.uuid] = comment
- for comment in comments:
- if comment.alt_id != None and comment.alt_id not in uuid_map:
- uuid_map[comment.alt_id] = comment
- if root == None:
- root = Comment(bug, uuid=INVALID_UUID)
- else:
- uuid_map[root.uuid] = root
- for comm in comments:
- if comm.in_reply_to == INVALID_UUID:
- comm.in_reply_to = None
- rep = comm.in_reply_to
- if rep == None or rep == bug.uuid:
- root_comments.append(comm)
- else:
- parentUUID = comm.in_reply_to
- try:
- parent = uuid_map[parentUUID]
- parent.add_reply(comm)
- except KeyError, e:
- if ignore_missing_references == True:
- print >> sys.stderr, \
- "Ignoring missing reference to %s" % parentUUID
- comm.in_reply_to = None
- root_comments.append(comm)
- else:
- raise MissingReference(comm)
- root.extend(root_comments)
- return root
-
def loadComments(bug, load_full=False):
"""
Set load_full=True when you want to load the comment completely
@@ -132,7 +85,9 @@ def loadComments(bug, load_full=False):
comm.load_settings()
dummy = comm.body # force the body to load
comments.append(comm)
- return list_to_root(comments, bug)
+ bug.comment_root = Comment(bug, uuid=INVALID_UUID)
+ bug.add_comments(comments)
+ return bug.comment_root
def saveComments(bug):
if bug.sync_with_disk == False:
@@ -344,7 +299,7 @@ class Comment(Tree, settings_object.SavedSettingsObject):
if v != None:
lines.append(' <%s>%s</%s>' % (k,xml.sax.saxutils.escape(v),k))
for estr in self.extra_strings:
- lines.append(' <extra-string>%s</extra-string>\n' % estr)
+ lines.append(' <extra-string>%s</extra-string>' % estr)
lines.append('</comment>')
istring = ' '*indent
sep = '\n' + istring
@@ -362,6 +317,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
@@ -371,12 +328,16 @@ class Comment(Tree, settings_object.SavedSettingsObject):
"""
if type(xml_string) == types.UnicodeType:
xml_string = xml_string.strip().encode('unicode_escape')
- comment = ElementTree.XML(xml_string)
+ if hasattr(xml_string, 'getchildren'): # already an ElementTree Element
+ comment = xml_string
+ else:
+ comment = ElementTree.XML(xml_string)
if comment.tag != 'comment':
raise utility.InvalidXML( \
'comment', comment, 'root element must be <comment>')
tags=['uuid','alt-id','in-reply-to','author','date','content-type',
'body','extra-string']
+ self.explicit_attrs = []
uuid = None
body = None
estrs = []
@@ -392,19 +353,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 uuid != self.uuid and 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 +376,78 @@ class Comment(Tree, settings_object.SavedSettingsObject):
self.body = base64.decodestring(body)
self.extra_strings = estrs
+ def merge(self, other, accept_changes=True,
+ accept_extra_strings=True, change_exception=False):
+ """
+ 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, accept_changes=False,
+ ... accept_extra_strings=False, change_exception=False)
+ >>> commA.merge(commB, accept_changes=False,
+ ... accept_extra_strings=False, change_exception=True)
+ Traceback (most recent call last):
+ ...
+ ValueError: Merge would change author "Frank"->"John" for comment 0123
+ >>> commA.merge(commB, accept_changes=True,
+ ... accept_extra_strings=False, change_exception=True)
+ Traceback (most recent call last):
+ ...
+ ValueError: Merge would add extra string "TAG: useful" to comment 0123
+ >>> print commA.author
+ John
+ >>> print commA.extra_strings
+ ['TAG: favorite', 'TAG: very helpful']
+ >>> commA.merge(commB, accept_changes=True,
+ ... accept_extra_strings=True, change_exception=True)
+ >>> print commA.extra_strings
+ ['TAG: favorite', 'TAG: useful', 'TAG: very helpful']
+ >>> print commA.xml()
+ <comment>
+ <uuid>0123</uuid>
+ <short-name>0123</short-name>
+ <author>John</author>
+ <date>Thu, 01 Jan 1970 00:00:00 +0000</date>
+ <content-type>text/plain</content-type>
+ <body>More insightful remarks</body>
+ <extra-string>TAG: favorite</extra-string>
+ <extra-string>TAG: useful</extra-string>
+ <extra-string>TAG: very helpful</extra-string>
+ </comment>
+ """
+ for attr in other.explicit_attrs:
+ old = getattr(self, attr)
+ new = getattr(other, attr)
+ if old != new:
+ if accept_changes == True:
+ setattr(self, attr, new)
+ elif change_exception == True:
+ raise ValueError, \
+ 'Merge would change %s "%s"->"%s" for comment %s' \
+ % (attr, old, new, self.uuid)
+ if self.alt_id == self.uuid:
+ self.alt_id = None
+ for estr in other.extra_strings:
+ if not estr in self.extra_strings:
+ if accept_extra_strings == True:
+ self.extra_strings.append(estr)
+ elif change_exception == True:
+ raise ValueError, \
+ 'Merge would add extra string "%s" to comment %s' \
+ % (estr, self.uuid)
+
def string(self, indent=0, shortname=None):
"""
>>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n")
@@ -674,7 +709,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 +719,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):
diff --git a/libbe/diff.py b/libbe/diff.py
index cce3b0f..6e830c6 100644
--- a/libbe/diff.py
+++ b/libbe/diff.py
@@ -207,7 +207,7 @@ class Diff (object):
added = []
removed = []
modified = []
- for uuid in self.new_bugdir.list_uuids():
+ for uuid in self.new_bugdir.uuids():
new_bug = self.new_bugdir.bug_from_uuid(uuid)
try:
old_bug = self.old_bugdir.bug_from_uuid(uuid)
@@ -220,7 +220,7 @@ class Diff (object):
new_bug.load_comments()
if old_bug != new_bug:
modified.append((old_bug, new_bug))
- for uuid in self.old_bugdir.list_uuids():
+ for uuid in self.old_bugdir.uuids():
if not self.new_bugdir.has_bug(uuid):
old_bug = self.old_bugdir.bug_from_uuid(uuid)
removed.append(old_bug)
diff --git a/libbe/subproc.py b/libbe/subproc.py
index 3e58271..fe88206 100644
--- a/libbe/subproc.py
+++ b/libbe/subproc.py
@@ -96,7 +96,7 @@ class Pipe (object):
>>> p.statuses
[1, 0]
>>> p.stderrs # doctest: +ELLIPSIS
- ["find: `...': Permission denied\\n...", '']
+ [...find: ...: Permission denied..., '']
"""
def __init__(self, cmds, stdin=None):
# spawn processes