From 501014a3f86bdfb7be7b52c6d1d0eb4fd7a75f02 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 20 Oct 2009 09:30:42 -0400 Subject: Pass content_type to Comment.new_reply() to fix error adding non-text/ comments --- libbe/comment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 41bc7e6..02bcc93 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -604,7 +604,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): reply.in_reply_to = self.uuid self.append(reply) - def new_reply(self, body=None): + def new_reply(self, body=None, content_type=None): """ >>> comm = Comment(bug=None, body="Some insightful remarks") >>> repA = comm.new_reply("Critique original comment") @@ -613,6 +613,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): True """ reply = Comment(self.bug, body=body) + if content_type != None: # set before saving body to decide binary format + reply.content_type = content_type if self.bug != None: reply.set_sync_with_disk(self.bug.sync_with_disk) if reply.sync_with_disk == True: -- cgit From 2f867266d4b47cc8ca0c7ccb3768d240e5096f11 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 17 Nov 2009 08:43:44 -0500 Subject: Don't attempt to convert unicode objects to strings in *._setting_attr_string() --- libbe/comment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 02bcc93..17daf62 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -308,7 +308,9 @@ class Comment(Tree, settings_object.SavedSettingsObject): value = getattr(self, setting) if value == None: return "" - return str(value) + if type(value) not in types.StringTypes: + return str(value) + return value def xml(self, indent=0, shortname=None): """ -- cgit From 4c6a1e6439293c7e584aef4fda0da1a3968fe7c9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 19 Nov 2009 17:00:02 -0500 Subject: Ran the new update_copyright.py --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 17daf62..5f67878 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -1,5 +1,5 @@ # Bugs Everywhere, a distributed bugtracker -# Copyright (C) 2008-2009 Chris Ball +# Copyright (C) 2008-2009 Gianluca Montecchi # Thomas Habets # W. Trevor King # -- cgit From 75fedab07f9e566ca1c984051d7deece4d910e2c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 20 Nov 2009 17:09:08 -0500 Subject: Added Bug.from_xml() + some .from_xml() fixups. Moved comment.InvalidXML to utility.InvalidXML, so that bug and comment can share it. Added docstring explaining the __init__ arguments. Added indent and shortname options to Bug.xml() to match Comment.xml(). Added .extra_strings export to Comment.xml(). Converted Bug.xml() from string addition to list joining, which avoids a bunch of memory allocation/deallocation. Assorted " -> ' replacements. Elaborated doctests to check UTF-8, extra_strings, ... Added new comparison cmp_extra_strings for both bug. and comment.DEFAULT_CMP_FULL_CMP_LIST. --- libbe/comment.py | 97 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 49 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 5f67878..678d8ba 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -51,14 +51,6 @@ class InvalidShortname(KeyError): self.shortname = shortname self.shortnames = shortnames -class InvalidXML(ValueError): - def __init__(self, element, comment): - msg = "Invalid comment xml: %s\n %s\n" \ - % (comment, ElementTree.tostring(element)) - ValueError.__init__(self, msg) - self.element = element - self.comment = comment - class MissingReference(ValueError): def __init__(self, comment): msg = "Missing reference to %s" % (comment.in_reply_to) @@ -331,27 +323,29 @@ class Comment(Tree, settings_object.SavedSettingsObject): """ if shortname == None: shortname = self.uuid - if self.content_type.startswith("text/"): - body = (self.body or "").rstrip('\n') + if self.content_type.startswith('text/'): + body = (self.body or '').rstrip('\n') else: maintype,subtype = self.content_type.split('/',1) msg = email.mime.base.MIMEBase(maintype, subtype) - msg.set_payload(self.body or "") + msg.set_payload(self.body or '') email.encoders.encode_base64(msg) - body = base64.encodestring(self.body or "") - info = [("uuid", self.uuid), - ("alt-id", self.alt_id), - ("short-name", shortname), - ("in-reply-to", self.in_reply_to), - ("author", self._setting_attr_string("author")), - ("date", self.date), - ("content-type", self.content_type), - ("body", body)] - lines = [""] + body = base64.encodestring(self.body or '') + info = [('uuid', self.uuid), + ('alt-id', self.alt_id), + ('short-name', shortname), + ('in-reply-to', self.in_reply_to), + ('author', self._setting_attr_string('author')), + ('date', self.date), + ('content-type', self.content_type), + ('body', body)] + lines = [''] for (k,v) in info: if v != None: lines.append(' <%s>%s' % (k,xml.sax.saxutils.escape(v),k)) - lines.append("") + for estr in self.extra_strings: + lines.append(' %s\n' % estr) + lines.append('') istring = ' '*indent sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') @@ -363,58 +357,61 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") >>> commA.uuid = "0123" >>> commA.date = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> commA.author = u'Fran\xe7ois' + >>> commA.extra_strings += ['TAG: very helpful'] >>> xml = commA.xml(shortname="com-1") >>> commB = Comment() - >>> commB.from_xml(xml) - >>> attrs=['uuid','alt_id','in_reply_to','author','date','content_type','body'] - >>> for attr in attrs: # doctest: +ELLIPSIS - ... if getattr(commB, attr) != getattr(commA, attr): - ... estr = "Mismatch on %s: '%s' should be '%s'" - ... args = (attr, getattr(commB, attr), getattr(commA, attr)) - ... print estr % args - Mismatch on uuid: '...' should be '0123' - Mismatch on alt_id: '0123' should be 'None' - >>> print commB.alt_id - 0123 - >>> commA.author - >>> commB.author + >>> commB.from_xml(xml, verbose=True) + >>> commB.xml(shortname="com-1") == xml + False + >>> commB.uuid = commB.alt_id + >>> commB.alt_id = None + >>> commB.xml(shortname="com-1") == xml + True """ if type(xml_string) == types.UnicodeType: - xml_string = xml_string.strip().encode("unicode_escape") + xml_string = xml_string.strip().encode('unicode_escape') comment = ElementTree.XML(xml_string) - if comment.tag != "comment": - raise InvalidXML(comment, "root element must be ") - tags=['uuid','alt-id','in-reply-to','author','date','content-type','body'] + if comment.tag != 'comment': + raise utility.InvalidXML( \ + 'comment', comment, 'root element must be ') + tags=['uuid','alt-id','in-reply-to','author','date','content-type', + 'body','extra-string'] uuid = None body = None + estrs = [] for child in comment.getchildren(): - if child.tag == "short-name": + if child.tag == 'short-name': pass 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 = unicode(text).decode("unicode_escape").strip() - if child.tag == "uuid": + text = text.decode('unicode_escape').strip() + if child.tag == 'uuid': uuid = text - continue # don't set the bug's uuid tag. - if child.tag == "body": + continue # don't set the comment's uuid tag. + if child.tag == 'body': body = text - continue # don't set the bug's body yet. + continue # don't set the comment's body yet. + if child.tag == 'extra-string': + estrs.append(text) + continue # don't set the comment's extra_string yet. else: attr_name = child.tag.replace('-','_') setattr(self, attr_name, text) elif verbose == True: - print >> sys.stderr, "Ignoring unknown tag %s in %s" \ + 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]: self.alt_id = uuid if body != None: - if self.content_type.startswith("text/"): - self.body = body+"\n" # restore trailing newline + if self.content_type.startswith('text/'): + self.body = body+'\n' # restore trailing newline else: self.body = base64.decodestring(body) + self.extra_strings = estrs def string(self, indent=0, shortname=None): """ @@ -726,12 +723,14 @@ cmp_author = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "autho cmp_in_reply_to = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "in_reply_to") cmp_content_type = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "content_type") cmp_body = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "body") +cmp_extra_strings = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "extra_strings") # chronological rankings (newer < older) cmp_time = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "time", invert=True) + DEFAULT_CMP_FULL_CMP_LIST = \ (cmp_time, cmp_author, cmp_content_type, cmp_body, cmp_in_reply_to, - cmp_uuid) + cmp_uuid, cmp_extra_strings) class CommentCompoundComparator (object): def __init__(self, cmp_list=DEFAULT_CMP_FULL_CMP_LIST): -- cgit From 3b168403ff5e50d767476c4c0f037d1841bb2bf9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 21 Nov 2009 10:53:04 -0500 Subject: Broke `be comment --xml` out and extended into `be import-xml`. It should currently do everything that `be comment --xml` did, but it still has a way to go before it lives up to it's longhelp string, mostly figuring out bug/comment merging. The allowed XML format also changed a bit, becoming a bit more structured. cmdutil.bug_from_shortname() renamed to cmdutil.bug_from_id(). New functions cmdutil.parse_id() and cmdutil.bug_comment_from_id(). Additional doctests in libbe.comment.Comment.comment_shortnames() to show example output if bug_shortname==None. Brought be-xml-to-mbox and be-mbox-to-xml up to speed on the current -rooted format. * Added handling to their comment handling. * Moved extra strings from email bodies to X-Extra-String headers (some comment bodies are not text, and we should keep the estr location consistent between bugs and comments.) --- libbe/comment.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 678d8ba..5cc43c4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -641,6 +641,12 @@ class Comment(Tree, settings_object.SavedSettingsObject): bug-1:2 b bug-1:3 c bug-1:4 d + >>> for id,name in a.comment_shortnames(): + ... print id, name.uuid + :1 a + :2 b + :3 c + :4 d """ if bug_shortname == None: bug_shortname = "" -- cgit From 3e050db00d2ffa2c011efc4d9b47d8edeac5c43c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 28 Nov 2009 07:41:13 -0500 Subject: 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(). --- libbe/comment.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 7 deletions(-) (limited to 'libbe/comment.py') 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): -- cgit From 832843d26eed9023f4cf4fc431527c63ca1d533d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 28 Nov 2009 22:07:16 -0500 Subject: Added comment import to Bug.from_xml(). This is a pretty critical feature, dunno how I missed it before. I also added a little check to both Bug and Comment.from_xml() so that xml_string can take an ElementTree Element as well as the usual raw string/unicode. This avoids repeated string <-> Element conversions. Added Bug.add_comment() which handles the addition of a Comment instance, matching .in_reply_to, checking .uuid uniqueness, etc. --- libbe/comment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 1adb6f4..e3dfea0 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -373,7 +373,10 @@ 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 ') -- cgit From 759c69d8c8a4bbd7ba9c42bb3a813cd0d06a52b7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 29 Nov 2009 03:19:30 -0500 Subject: Moved comment.list_to_root() to Bug.add_comments() with some cleanups. This makes Bug.add_comment simpler. Also makes Bug.from_xml() more robust, since it no longer depends on the order in which the XML file lists the comments. The previous Bug.from_xml() would have choked on B A A because when B was being added, the referenced A hadn't yet been noticed. --- libbe/comment.py | 51 +++------------------------------------------------ 1 file changed, 3 insertions(+), 48 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index e3dfea0..45134e0 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: -- cgit From 19cea054def7997bb13ecc77269b7b612f658d16 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 30 Nov 2009 06:05:03 -0500 Subject: Changed Bug and Comment.merge() kwargs. The old allow_changes and allow_new_comments didn't have separate handling for extra_strings, like import_xml will need. It also didn't have a way to specify what to do if an illegal change occurs. Sometimes you'll want to raise an exception, but sometimes you'll want to ?silently? ignore the change. --- libbe/comment.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 45134e0..9502adf 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -376,7 +376,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.body = base64.decodestring(body) self.extra_strings = estrs - def merge(self, other, allow_changes=True): + 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. @@ -393,11 +394,26 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> commB.explicit_attrs = ['author', 'body'] >>> commB.extra_strings += ['TAG: very helpful'] >>> commB.extra_strings += ['TAG: useful'] - >>> commA.merge(commB, allow_changes=False) + >>> 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) + >>> 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() 0123 @@ -415,18 +431,20 @@ class Comment(Tree, settings_object.SavedSettingsObject): old = getattr(self, attr) new = getattr(other, attr) if old != new: - if allow_changes == True: + if accept_changes == True: setattr(self, attr, new) - else: + elif change_exception == True: 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) + 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): """ -- cgit From c1fc4595171fa6eec802eb65a0fde0b53878a077 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 30 Nov 2009 06:26:49 -0500 Subject: Avoid redundant Comment.alt_ids --- libbe/comment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 9502adf..c5f1cc9 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -366,7 +366,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): elif verbose == True: print >> sys.stderr, 'Ignoring unknown tag %s in %s' \ % (child.tag, comment.tag) - if self.alt_id == None: + if uuid != self.uuid and self.alt_id == None: self.explicit_attrs.append('alt_id') self.alt_id = uuid if body != None: @@ -437,6 +437,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): 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: -- cgit From 2ba535acb1f03fb7d1bdb57e4173d55661d300da Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 3 Dec 2009 21:19:54 -0500 Subject: Added libbe.TESTING (defaults to False). This flag allows us to skip unittest and testsuite declaration if we woln't need them. It speeds up simple be calls a suprising amount. With Testing=True (the old behavior): wking@thor:be.wtk$ time ./be > /dev/null real 0m0.393s user 0m0.340s sys 0m0.048s With TESTING=False (the new behavior): be.wtk$ time ./be > /dev/null real 0m0.216s user 0m0.152s sys 0m0.064s This adjustment was inspired by Jakub Wilk's Debian bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559295 --- libbe/comment.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index c5f1cc9..32536d4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -32,8 +32,8 @@ try: # import core module, Python >= 2.5 except ImportError: # look for non-core module from elementtree import ElementTree import xml.sax.saxutils -import doctest +import libbe from beuuid import uuid_gen from properties import Property, doc_property, local_property, \ defaulting_property, checked_property, cached_property, \ @@ -42,6 +42,8 @@ import settings_object import mapfile from tree import Tree import utility +if libbe.TESTING == True: + import doctest class InvalidShortname(KeyError): @@ -796,4 +798,5 @@ class CommentCompoundComparator (object): cmp_full = CommentCompoundComparator() -suite = doctest.DocTestSuite() +if libbe.TESTING == True: + suite = doctest.DocTestSuite() -- cgit From df3a136010c3a05166a4a77028fd00a948d7cdeb Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 8 Dec 2009 04:10:10 -0500 Subject: Transitioned comment.py to new storage format. --- libbe/comment.py | 126 +++++++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 32536d4..0e23d3c 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -1,4 +1,3 @@ -# Bugs Everywhere, a distributed bugtracker # Copyright (C) 2008-2009 Gianluca Montecchi # Thomas Habets # W. Trevor King @@ -34,14 +33,15 @@ except ImportError: # look for non-core module import xml.sax.saxutils import libbe -from beuuid import uuid_gen -from properties import Property, doc_property, local_property, \ +import libbe.util.id +from libbe.storage.properties import Property, doc_property, local_property, \ defaulting_property, checked_property, cached_property, \ primed_property, change_hook_property, settings_property -import settings_object -import mapfile -from tree import Tree -import utility +import libbe.storage.settings_object as settings_object +import libbe.storage.util.mapfile as mapfile +from libbe.util.tree import Tree +import libbe.util.utility as utility + if libbe.TESTING == True: import doctest @@ -72,17 +72,14 @@ def loadComments(bug, load_full=False): Set load_full=True when you want to load the comment completely from disk *now*, rather than waiting and lazy loading as required. """ - if bug.sync_with_disk == False: - raise DiskAccessRequired("load comments") - path = bug.get_path("comments") - if not os.path.exists(path): - return Comment(bug, uuid=INVALID_UUID) + uuids = [] + for id in bug.storage.children(): + parsed = libbe.util.id.parse_id(id) + if parsed['type'] == 'comment': + uuids.append(parsed['comment']) comments = [] - for uuid in os.listdir(path): - if uuid.startswith('.'): - continue - comm = Comment(bug, uuid, from_disk=True) - comm.set_sync_with_disk(bug.sync_with_disk) + for uuid in uuids: + comm = Comment(bug, uuid, from_storage=True) if load_full == True: comm.load_settings() dummy = comm.body # force the body to load @@ -92,8 +89,6 @@ def loadComments(bug, load_full=False): return bug.comment_root def saveComments(bug): - if bug.sync_with_disk == False: - raise DiskAccessRequired("save comments") for comment in bug.comment_root.traverse(): comment.save() @@ -154,15 +149,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): doc="An integer version of .date") def _get_comment_body(self): - if self.vcs != None and self.sync_with_disk == True: - import vcs - binary = not self.content_type.startswith("text/") - return self.vcs.get_file_contents(self.get_path("body"), binary=binary) + if self.storage != None and self.storage.readable: + return self.storage.get(self.id("body"), + decode=self.content_type.startswith("text/")) def _set_comment_body(self, old=None, new=None, force=False): - if (self.vcs != None and self.sync_with_disk == True) or force==True: + if (self.storage != None and self.storage.writeable == True) \ + or force==True: assert new != None, "Can't save empty comment" - binary = not self.content_type.startswith("text/") - self.vcs.set_file_contents(self.get_path("body"), new, binary=binary) + self.storage.set(self.id("body"), new) @Property @change_hook_property(hook=_set_comment_body) @@ -171,15 +165,15 @@ class Comment(Tree, settings_object.SavedSettingsObject): @doc_property(doc="The meat of the comment") def body(): return {} - def _get_vcs(self): - if hasattr(self.bug, "vcs"): - return self.bug.vcs + def _get_storage(self): + if hasattr(self.bug, "storage"): + return self.bug.storage @Property - @cached_property(generator=_get_vcs) - @local_property("vcs") + @cached_property(generator=_get_storage) + @local_property("storage") @doc_property(doc="A revision control system instance.") - def vcs(): return {} + def storage(): return {} def _extra_strings_check_fn(value): return utility.iterable_full_of_strings(value, \ @@ -213,15 +207,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): settings_object.SavedSettingsObject.__init__(self) self.bug = bug self.uuid = uuid - if from_disk == True: - self.sync_with_disk = True - else: - self.sync_with_disk = False + if from_disk == False: if uuid == None: - self.uuid = uuid_gen() + self.uuid = libbe.util.id.uuid_gen() + self.settings = {} + self._setup_saved_settings() self.time = int(time.time()) # only save to second precision - if self.vcs != None: - self.author = self.vcs.get_user_id() + if self.bug != None: + self.author = self.bug.get_user_id() self.in_reply_to = in_reply_to self.body = body @@ -587,53 +580,42 @@ class Comment(Tree, settings_object.SavedSettingsObject): # methods for saving/loading/acessing settings and properties. - def get_path(self, *args): - dir = os.path.join(self.bug.get_path("comments"), self.uuid) - if len(args) == 0: - return dir + def id(self, *args): + assert len(args) <= 1, str(args) assert args[0] in ["values", "body"], str(args) - return os.path.join(dir, *args) - - def set_sync_with_disk(self, value): - self.sync_with_disk = value + return libbe.util.id.comment_id(self, args) def load_settings(self): - if self.sync_with_disk == False: - raise DiskAccessRequired("load settings") - self.settings = mapfile.map_load(self.vcs, self.get_path("values")) + mf = self.storage.get(self.id("values"), default="\n") + self.settings = mapfile.parse(mf) self._setup_saved_settings() def save_settings(self): - if self.sync_with_disk == False: - raise DiskAccessRequired("save settings") - self.vcs.mkdir(self.get_path()) - path = self.get_path("values") - mapfile.map_save(self.vcs, path, self._get_saved_settings()) + mf = mapfile.generate(self._get_saved_settings()) + self.storage.set(self.id("values"), mf) def save(self): """ - Save any loaded contents to disk. + Save any loaded contents to storage. - However, if self.sync_with_disk = True, then any changes are - automatically written to disk as soon as they happen, so - calling this method will just waste time (unless something - else has been messing with your on-disk files). + However, if self.storage.writeable = True, then any changes + are automatically written to storage as soon as they happen, + so calling this method will just waste time (unless something + else has been messing with your stored files). """ - sync_with_disk = self.sync_with_disk - if sync_with_disk == False: - self.set_sync_with_disk(True) + assert self.storage != None, "Can't save without storage" assert self.body != None, "Can't save blank comment" + self.storage.add(self.id()) + self.storage.add(self.id('values')) + self.storage.add(self.id('body')) self.save_settings() self._set_comment_body(new=self.body, force=True) - if sync_with_disk == False: - self.set_sync_with_disk(False) def remove(self): - if self.sync_with_disk == False and self.uuid != INVALID_UUID: - raise DiskAccessRequired("remove") - for comment in self.traverse(): - path = comment.get_path() - self.vcs.recursive_remove(path) + for comment in self: + comment.remove() + if self.uuid != INVALID_UUID: + self.storage.recursive_remove(self.id()) def add_reply(self, reply, allow_time_inversion=False): if self.uuid != INVALID_UUID: @@ -651,9 +633,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): reply = Comment(self.bug, body=body) if content_type != None: # set before saving body to decide binary format reply.content_type = content_type - if self.bug != None: - reply.set_sync_with_disk(self.bug.sync_with_disk) - if reply.sync_with_disk == True: + if reply.storage != None and reply.storage.is_writeable(): reply.save() self.add_reply(reply) return reply -- cgit From 7a8b1223fac612ef8b3dffd0e4c6832a97aa222d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 8 Dec 2009 04:33:49 -0500 Subject: Transitioned bug.py to new storage format. --- libbe/comment.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 0e23d3c..fc87c9d 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -120,8 +120,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): doc="Alternate ID for linking imported comments. Internally comments are linked (via In-reply-to) to the parent's UUID. However, these UUIDs are generated internally, so Alt-id is provided as a user-controlled linking target.") def alt_id(): return {} + def _get_user_id(self): + if self.bug != None: + return self.bug._get_user_id() + return None + @_versioned_property(name="Author", - doc="The author of the comment") + doc="The author of the comment", + generator=_get_user_id) def author(): return {} @_versioned_property(name="In-reply-to", @@ -213,8 +219,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.settings = {} self._setup_saved_settings() self.time = int(time.time()) # only save to second precision - if self.bug != None: - self.author = self.bug.get_user_id() self.in_reply_to = in_reply_to self.body = body @@ -598,10 +602,10 @@ class Comment(Tree, settings_object.SavedSettingsObject): """ Save any loaded contents to storage. - However, if self.storage.writeable = True, then any changes - are automatically written to storage as soon as they happen, - so calling this method will just waste time (unless something - else has been messing with your stored files). + However, if self.storage.is_writeable() == True, then any + changes are automatically written to storage as soon as they + happen, so calling this method will just waste time (unless + something else has been messing with your stored files). """ assert self.storage != None, "Can't save without storage" assert self.body != None, "Can't save blank comment" -- cgit From 44b4e3f8b6405d0e1e0ebf6cb526ab62cdbbdb25 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 8 Dec 2009 08:54:50 -0500 Subject: Transitioned bugdir.py to new storage format. --- libbe/comment.py | 57 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 19 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index fc87c9d..e77235a 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -67,7 +67,7 @@ class DiskAccessRequired (Exception): INVALID_UUID = "!!~~\n INVALID-UUID \n~~!!" -def loadComments(bug, load_full=False): +def load_comments(bug, load_full=False): """ Set load_full=True when you want to load the comment completely from disk *now*, rather than waiting and lazy loading as required. @@ -88,7 +88,7 @@ def loadComments(bug, load_full=False): bug.add_comments(comments) return bug.comment_root -def saveComments(bug): +def save_comments(bug): for comment in bug.comment_root.traverse(): comment.save() @@ -155,10 +155,12 @@ class Comment(Tree, settings_object.SavedSettingsObject): doc="An integer version of .date") def _get_comment_body(self): - if self.storage != None and self.storage.readable: + if self.storage != None and self.storage.is_readable() \ + and self.uuid != INVALID_UUID: return self.storage.get(self.id("body"), decode=self.content_type.startswith("text/")) def _set_comment_body(self, old=None, new=None, force=False): + assert self.uuid != INVALID_UUID, self if (self.storage != None and self.storage.writeable == True) \ or force==True: assert new != None, "Can't save empty comment" @@ -195,17 +197,17 @@ class Comment(Tree, settings_object.SavedSettingsObject): mutable=True) def extra_strings(): return {} - def __init__(self, bug=None, uuid=None, from_disk=False, + def __init__(self, bug=None, uuid=None, from_storage=False, in_reply_to=None, body=None): """ - Set from_disk=True to load an old comment. - Set from_disk=False to create a new comment. + Set from_storage=True to load an old comment. + Set from_storage=False to create a new comment. - The uuid option is required when from_disk==True. + The uuid option is required when from_storage==True. The in_reply_to and body options are only used if - from_disk==False (the default). When from_disk==True, they are - loaded from the bug database. + from_storage==False (the default). When from_storage==True, + they are loaded from the bug database. in_reply_to should be the uuid string of the parent comment. """ @@ -213,14 +215,22 @@ class Comment(Tree, settings_object.SavedSettingsObject): settings_object.SavedSettingsObject.__init__(self) self.bug = bug self.uuid = uuid - if from_disk == False: + if from_storage == False: if uuid == None: self.uuid = libbe.util.id.uuid_gen() self.settings = {} self._setup_saved_settings() + if self.storage != None and self.storage.is_writeable(): + self.storage.writeable = False + set_writeable = True + else: + set_writeable = False self.time = int(time.time()) # only save to second precision self.in_reply_to = in_reply_to self.body = body + if set_writeable == True: + self.storage.writeable = True + self.save() def __cmp__(self, other): return cmp_full(self, other) @@ -586,12 +596,15 @@ class Comment(Tree, settings_object.SavedSettingsObject): def id(self, *args): assert len(args) <= 1, str(args) - assert args[0] in ["values", "body"], str(args) - return libbe.util.id.comment_id(self, args) - - def load_settings(self): - mf = self.storage.get(self.id("values"), default="\n") - self.settings = mapfile.parse(mf) + if len(args) == 1: + assert args[0] in ["values", "body"], str(args) + return libbe.util.id.comment_id(self, *args) + + def load_settings(self, settings_mapfile=None): + if settings_mapfile == None: + settings_mapfile = \ + self.storage.get(self.id("values"), default="\n") + self.settings = mapfile.parse(settings_mapfile) self._setup_saved_settings() def save_settings(self): @@ -607,11 +620,17 @@ class Comment(Tree, settings_object.SavedSettingsObject): happen, so calling this method will just waste time (unless something else has been messing with your stored files). """ + if self.uuid == INVALID_UUID: + return assert self.storage != None, "Can't save without storage" assert self.body != None, "Can't save blank comment" - self.storage.add(self.id()) - self.storage.add(self.id('values')) - self.storage.add(self.id('body')) + if self.bug != None: + parent = self.bug.id() + else: + parent = None + self.storage.add(self.id(), parent=parent) + self.storage.add(self.id('values'), parent=self.id()) + self.storage.add(self.id('body'), parent=self.id()) self.save_settings() self._set_comment_body(new=self.body, force=True) -- cgit From 79154201c1c012063aa3fe1881ff06a3f239fdc5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 8 Dec 2009 09:01:26 -0500 Subject: Moved properties.py and settings_object.py to libbe/storage/util/ --- libbe/comment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index e77235a..987e39c 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -34,8 +34,8 @@ import xml.sax.saxutils import libbe import libbe.util.id -from libbe.storage.properties import Property, doc_property, local_property, \ - defaulting_property, checked_property, cached_property, \ +from libbe.storage.util.properties import Property, doc_property, + local_property, defaulting_property, checked_property, cached_property, \ primed_property, change_hook_property, settings_property import libbe.storage.settings_object as settings_object import libbe.storage.util.mapfile as mapfile -- cgit From 3e6096fb5bcb9c9e8a50faa76461da96d145ca8f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 8 Dec 2009 20:02:34 -0500 Subject: Reworked test.py to handle deeper directory structure --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 987e39c..f5a6309 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -34,7 +34,7 @@ import xml.sax.saxutils import libbe import libbe.util.id -from libbe.storage.util.properties import Property, doc_property, +from libbe.storage.util.properties import Property, doc_property, \ local_property, defaulting_property, checked_property, cached_property, \ primed_property, change_hook_property, settings_property import libbe.storage.settings_object as settings_object -- cgit From a153347564e4c6baa0388fda05530f5548d16ac5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 10 Dec 2009 19:31:47 -0500 Subject: Moved bugdir, bug, and comment over to new id implementation. --- libbe/comment.py | 187 ++++++++++++++++--------------------------------------- 1 file changed, 54 insertions(+), 133 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index f5a6309..ebfde23 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -73,10 +73,8 @@ def load_comments(bug, load_full=False): from disk *now*, rather than waiting and lazy loading as required. """ uuids = [] - for id in bug.storage.children(): - parsed = libbe.util.id.parse_id(id) - if parsed['type'] == 'comment': - uuids.append(parsed['comment']) + for id in libbe.util.id.child_uuids(bug.storage.children()): + uuids.append(id) comments = [] for uuid in uuids: comm = Comment(bug, uuid, from_storage=True) @@ -157,14 +155,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): def _get_comment_body(self): if self.storage != None and self.storage.is_readable() \ and self.uuid != INVALID_UUID: - return self.storage.get(self.id("body"), + return self.storage.get(self.id.storage("body"), decode=self.content_type.startswith("text/")) def _set_comment_body(self, old=None, new=None, force=False): assert self.uuid != INVALID_UUID, self if (self.storage != None and self.storage.writeable == True) \ or force==True: assert new != None, "Can't save empty comment" - self.storage.set(self.id("body"), new) + self.storage.set(self.id.storage("body"), new) @Property @change_hook_property(hook=_set_comment_body) @@ -173,16 +171,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): @doc_property(doc="The meat of the comment") def body(): return {} - def _get_storage(self): - if hasattr(self.bug, "storage"): - return self.bug.storage - - @Property - @cached_property(generator=_get_storage) - @local_property("storage") - @doc_property(doc="A revision control system instance.") - def storage(): return {} - def _extra_strings_check_fn(value): return utility.iterable_full_of_strings(value, \ alternative=settings_object.EMPTY) @@ -214,22 +202,21 @@ class Comment(Tree, settings_object.SavedSettingsObject): Tree.__init__(self) settings_object.SavedSettingsObject.__init__(self) self.bug = bug + self.storage = None self.uuid = uuid + self.id = libbe.util.id.ID(self, 'comment') if from_storage == False: if uuid == None: self.uuid = libbe.util.id.uuid_gen() self.settings = {} self._setup_saved_settings() - if self.storage != None and self.storage.is_writeable(): - self.storage.writeable = False - set_writeable = True - else: - set_writeable = False self.time = int(time.time()) # only save to second precision self.in_reply_to = in_reply_to self.body = body - if set_writeable == True: - self.storage.writeable = True + if self.bug != None: + self.storage = self.bug.storage + if from_storage == False: + if self.storage != None and self.storage.is_writeable(): self.save() def __cmp__(self, other): @@ -243,7 +230,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> comm.author = "Jane Doe " >>> print comm --------- Comment --------- - Name: com-1 + Name: //com From: Jane Doe Date: Thu, 20 Nov 2008 15:55:11 +0000 @@ -268,15 +255,15 @@ class Comment(Tree, settings_object.SavedSettingsObject): return str(value) return value - def xml(self, indent=0, shortname=None): + def xml(self, indent=0): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") >>> comm.uuid = "0123" >>> comm.date = "Thu, 01 Jan 1970 00:00:00 +0000" - >>> print comm.xml(indent=2, shortname="com-1") + >>> print comm.xml(indent=2) 0123 - com-1 + //012 Thu, 01 Jan 1970 00:00:00 +0000 text/plain @@ -285,8 +272,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): remarks """ - if shortname == None: - shortname = self.uuid if self.content_type.startswith('text/'): body = (self.body or '').rstrip('\n') else: @@ -297,7 +282,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): body = base64.encodestring(self.body or '') info = [('uuid', self.uuid), ('alt-id', self.alt_id), - ('short-name', shortname), + ('short-name', self.id.user()), ('in-reply-to', self.in_reply_to), ('author', self._setting_attr_string('author')), ('date', self.date), @@ -323,16 +308,16 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> commA.date = "Thu, 01 Jan 1970 00:00:00 +0000" >>> commA.author = u'Fran\xe7ois' >>> commA.extra_strings += ['TAG: very helpful'] - >>> xml = commA.xml(shortname="com-1") + >>> xml = commA.xml() >>> commB = Comment() >>> commB.from_xml(xml, verbose=True) >>> commB.explicit_attrs ['author', 'date', 'content_type', 'body', 'alt_id'] - >>> commB.xml(shortname="com-1") == xml + >>> commB.xml() == xml False >>> commB.uuid = commB.alt_id >>> commB.alt_id = None - >>> commB.xml(shortname="com-1") == xml + >>> commB.xml() == xml True """ if type(xml_string) == types.UnicodeType: @@ -426,7 +411,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> print commA.xml() 0123 - 0123 + //012 John Thu, 01 Jan 1970 00:00:00 +0000 text/plain @@ -457,13 +442,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): 'Merge would add extra string "%s" to comment %s' \ % (estr, self.uuid) - def string(self, indent=0, shortname=None): + def string(self, indent=0): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") + >>> comm.uuid = 'abcdef' >>> comm.date = "Thu, 01 Jan 1970 00:00:00 +0000" - >>> print comm.string(indent=2, shortname="com-1") + >>> print comm.string(indent=2) --------- Comment --------- - Name: com-1 + Name: //abc From: Date: Thu, 01 Jan 1970 00:00:00 +0000 @@ -471,11 +457,9 @@ class Comment(Tree, settings_object.SavedSettingsObject): insightful remarks """ - if shortname == None: - shortname = self.uuid lines = [] lines.append("--------- Comment ---------") - lines.append("Name: %s" % shortname) + lines.append("Name: %s" % self.id.user()) lines.append("From: %s" % (self._setting_attr_string("author"))) lines.append("Date: %s" % self.date) lines.append("") @@ -488,9 +472,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') - def string_thread(self, string_method_name="string", name_map={}, - indent=0, flatten=True, - auto_name_map=False, bug_shortname=None): + def string_thread(self, string_method_name="string", + indent=0, flatten=True): """ Return a string displaying a thread of comments. bug_shortname is only used if auto_name_map == True. @@ -522,94 +505,77 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> a.sort(key=lambda comm : comm.time) >>> print a.string_thread(flatten=True) --------- Comment --------- - Name: a + Name: //a From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- - Name: b + Name: //b From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- - Name: c + Name: //c From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- - Name: d + Name: //d From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples - >>> print a.string_thread(auto_name_map=True, bug_shortname="bug-1") + >>> print a.string_thread() --------- Comment --------- - Name: bug-1:1 + Name: //a From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- - Name: bug-1:2 + Name: //b From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- - Name: bug-1:3 + Name: //c From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- - Name: bug-1:4 + Name: //d From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples """ - if auto_name_map == True: - name_map = {} - for shortname,comment in self.comment_shortnames(bug_shortname): - name_map[comment.uuid] = shortname stringlist = [] for depth,comment in self.thread(flatten=flatten): ind = 2*depth+indent - if comment.uuid in name_map: - sname = name_map[comment.uuid] - else: - sname = None string_fn = getattr(comment, string_method_name) - stringlist.append(string_fn(indent=ind, shortname=sname)) + stringlist.append(string_fn(indent=ind)) return '\n'.join(stringlist) - def xml_thread(self, name_map={}, indent=0, - auto_name_map=False, bug_shortname=None): - return self.string_thread(string_method_name="xml", name_map=name_map, - indent=indent, auto_name_map=auto_name_map, - bug_shortname=bug_shortname) + def xml_thread(self, indent=0): + return self.string_thread(string_method_name="xml", indent=indent) # methods for saving/loading/acessing settings and properties. - def id(self, *args): - assert len(args) <= 1, str(args) - if len(args) == 1: - assert args[0] in ["values", "body"], str(args) - return libbe.util.id.comment_id(self, *args) - def load_settings(self, settings_mapfile=None): if settings_mapfile == None: settings_mapfile = \ - self.storage.get(self.id("values"), default="\n") + self.storage.get(self.id.storage("values"), default="\n") self.settings = mapfile.parse(settings_mapfile) self._setup_saved_settings() def save_settings(self): mf = mapfile.generate(self._get_saved_settings()) - self.storage.set(self.id("values"), mf) + self.storage.set(self.id.storage("values"), mf) def save(self): """ @@ -625,12 +591,12 @@ class Comment(Tree, settings_object.SavedSettingsObject): assert self.storage != None, "Can't save without storage" assert self.body != None, "Can't save blank comment" if self.bug != None: - parent = self.bug.id() + parent = self.bug.id.storage() else: parent = None - self.storage.add(self.id(), parent=parent) - self.storage.add(self.id('values'), parent=self.id()) - self.storage.add(self.id('body'), parent=self.id()) + self.storage.add(self.id.storage(), parent=parent) + self.storage.add(self.id.storage('values'), parent=self.id.storage()) + self.storage.add(self.id.storage('body'), parent=self.id.storage()) self.save_settings() self._set_comment_body(new=self.body, force=True) @@ -638,7 +604,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): for comment in self: comment.remove() if self.uuid != INVALID_UUID: - self.storage.recursive_remove(self.id()) + self.storage.recursive_remove(self.id.storage()) def add_reply(self, reply, allow_time_inversion=False): if self.uuid != INVALID_UUID: @@ -661,62 +627,9 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.add_reply(reply) return reply - def comment_shortnames(self, bug_shortname=None): - """ - Iterate through (id, comment) pairs, in time order. - (This is a user-friendly id, not the comment uuid). - - SIDE-EFFECT : will sort the comment tree by comment.time - - >>> a = Comment(bug=None, uuid="a") - >>> b = a.new_reply() - >>> b.uuid = "b" - >>> c = b.new_reply() - >>> c.uuid = "c" - >>> d = a.new_reply() - >>> d.uuid = "d" - >>> for id,name in a.comment_shortnames("bug-1"): - ... print id, name.uuid - bug-1:1 a - bug-1:2 b - bug-1:3 c - bug-1:4 d - >>> for id,name in a.comment_shortnames(): - ... print id, name.uuid - :1 a - :2 b - :3 c - :4 d - """ - if bug_shortname == None: - bug_shortname = "" - self.sort(key=lambda comm : comm.time) - for num,comment in enumerate(self.traverse()): - yield ("%s:%d" % (bug_shortname, num+1), comment) - - def comment_from_shortname(self, comment_shortname, *args, **kwargs): - """ - Use a comment shortname to look up a comment. - >>> a = Comment(bug=None, uuid="a") - >>> b = a.new_reply() - >>> b.uuid = "b" - >>> c = b.new_reply() - >>> c.uuid = "c" - >>> d = a.new_reply() - >>> d.uuid = "d" - >>> comm = a.comment_from_shortname("bug-1:3", bug_shortname="bug-1") - >>> id(comm) == id(c) - True - """ - for cur_name, comment in self.comment_shortnames(*args, **kwargs): - if comment_shortname == cur_name: - return comment - raise InvalidShortname(comment_shortname, - list(self.comment_shortnames(*args, **kwargs))) - def comment_from_uuid(self, uuid, match_alt_id=True): """ - Use a comment shortname to look up a comment. + Use a uuid to look up a comment. >>> a = Comment(bug=None, uuid="a") >>> b = a.new_reply() >>> b.uuid = "b" @@ -744,6 +657,14 @@ class Comment(Tree, settings_object.SavedSettingsObject): return comment raise KeyError(uuid) + # methods for id generation + + def sibling_uuids(self): + if self.bug != None: + return self.bug.uuids() + return [] + + def cmp_attr(comment_1, comment_2, attr, invert=False): """ Compare a general attribute between two comments using the conventional -- cgit From dff6bd9bf89ca80e2265696a478e540476718c9c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 12 Dec 2009 20:57:59 -0500 Subject: Moved be to libbe.ui.command_line and transitioned to Command format. --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index ebfde23..bf69a69 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -37,7 +37,7 @@ import libbe.util.id from libbe.storage.util.properties import Property, doc_property, \ local_property, defaulting_property, checked_property, cached_property, \ primed_property, change_hook_property, settings_property -import libbe.storage.settings_object as settings_object +import libbe.storage.util.settings_object as settings_object import libbe.storage.util.mapfile as mapfile from libbe.util.tree import Tree import libbe.util.utility as utility -- cgit From 4d057dab603f42ec40b911dbee6792dcf107bd14 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 13 Dec 2009 06:19:23 -0500 Subject: Converted libbe.storage.vcs.base to new Storage format. --- libbe/comment.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index bf69a69..7b318cb 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -594,9 +594,11 @@ class Comment(Tree, settings_object.SavedSettingsObject): parent = self.bug.id.storage() else: parent = None - self.storage.add(self.id.storage(), parent=parent) - self.storage.add(self.id.storage('values'), parent=self.id.storage()) - self.storage.add(self.id.storage('body'), parent=self.id.storage()) + self.storage.add(self.id.storage(), parent=parent, directory=True) + self.storage.add(self.id.storage('values'), parent=self.id.storage(), + directory=False) + self.storage.add(self.id.storage('body'), parent=self.id.storage(), + directory=False) self.save_settings() self._set_comment_body(new=self.body, force=True) -- cgit From 19fe0817ba7c2cd04caea3adfa82d4490288a548 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 14 Dec 2009 07:37:51 -0500 Subject: Transitioned comment to Command format --- libbe/comment.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 7b318cb..1582632 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -73,7 +73,9 @@ def load_comments(bug, load_full=False): from disk *now*, rather than waiting and lazy loading as required. """ uuids = [] - for id in libbe.util.id.child_uuids(bug.storage.children()): + for id in libbe.util.id.child_uuids( + bug.storage.children( + bug.id.storage())): uuids.append(id) comments = [] for uuid in uuids: @@ -118,14 +120,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): doc="Alternate ID for linking imported comments. Internally comments are linked (via In-reply-to) to the parent's UUID. However, these UUIDs are generated internally, so Alt-id is provided as a user-controlled linking target.") def alt_id(): return {} - def _get_user_id(self): - if self.bug != None: - return self.bug._get_user_id() - return None - @_versioned_property(name="Author", - doc="The author of the comment", - generator=_get_user_id) + doc="The author of the comment") def author(): return {} @_versioned_property(name="In-reply-to", @@ -613,7 +609,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): reply.in_reply_to = self.uuid self.append(reply) - def new_reply(self, body=None, content_type=None): + def new_reply(self, body=None): """ >>> comm = Comment(bug=None, body="Some insightful remarks") >>> repA = comm.new_reply("Critique original comment") @@ -622,10 +618,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): True """ reply = Comment(self.bug, body=body) - if content_type != None: # set before saving body to decide binary format - reply.content_type = content_type - if reply.storage != None and reply.storage.is_writeable(): - reply.save() self.add_reply(reply) return reply -- cgit From 0f87a22c20a019f49455005542d4c60216ce39d2 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 14 Dec 2009 20:13:30 -0500 Subject: Transitioned merge to Command-format --- libbe/comment.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 1582632..3b8a9c7 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -155,6 +155,8 @@ class Comment(Tree, settings_object.SavedSettingsObject): decode=self.content_type.startswith("text/")) def _set_comment_body(self, old=None, new=None, force=False): assert self.uuid != INVALID_UUID, self + if self.bug != None and self.bug.bugdir != None: + new = libbe.util.id.short_to_long_user([self.bug.bugdir], new) if (self.storage != None and self.storage.writeable == True) \ or force==True: assert new != None, "Can't save empty comment" @@ -460,7 +462,10 @@ class Comment(Tree, settings_object.SavedSettingsObject): lines.append("Date: %s" % self.date) lines.append("") if self.content_type.startswith("text/"): - lines.extend((self.body or "").splitlines()) + body = (self.body or "") + if self.bug != None and self.bug.bugdir != None: + body = libbe.util.id.long_to_short_user([self.bug.bugdir], body) + lines.extend(body.splitlines()) else: lines.append("Content type %s not printable. Try XML output instead" % self.content_type) -- cgit From 89b7a1411e4658e831f5d635534b24355dbb941d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 15 Dec 2009 06:44:20 -0500 Subject: Fixed libbe.command.diff + ugly BugDir.duplicate_bugdir implementation duplicate_bugdir() works, but for the vcs backends, it could require shelling out for _every_ file read. This could, and probably will, be horribly slow. Still it works ;). I'm not sure what a better implementation would be. The old implementation checked out the entire earlier state into a temporary directory pros: single shell out, simple upgrade implementation cons: wouldn't work well for HTTP backens I think a good solution would run along the lines of the currently commented out code in duplicate_bugdir(), where a VersionedStorage.changed_since(revision) call would give you a list of changed files. diff could work off of that directly, without the need to generate a whole duplicate bugdir. I'm stuck on how to handle upgrades though... Also removed trailing whitespace from all python files. --- libbe/comment.py | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 3b8a9c7..d899aa8 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -190,18 +190,18 @@ class Comment(Tree, settings_object.SavedSettingsObject): Set from_storage=False to create a new comment. The uuid option is required when from_storage==True. - + The in_reply_to and body options are only used if from_storage==False (the default). When from_storage==True, they are loaded from the bug database. - + in_reply_to should be the uuid string of the parent comment. """ Tree.__init__(self) settings_object.SavedSettingsObject.__init__(self) self.bug = bug self.storage = None - self.uuid = uuid + self.uuid = uuid self.id = libbe.util.id.ID(self, 'comment') if from_storage == False: if uuid == None: @@ -214,7 +214,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): if self.bug != None: self.storage = self.bug.storage if from_storage == False: - if self.storage != None and self.storage.is_writeable(): + if self.storage != None and self.storage.is_writeable(): self.save() def __cmp__(self, other): @@ -368,7 +368,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.body = base64.decodestring(body) self.extra_strings = estrs - def merge(self, other, accept_changes=True, + def merge(self, other, accept_changes=True, accept_extra_strings=True, change_exception=False): """ Merge info from other into this comment. Overrides any @@ -448,7 +448,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> print comm.string(indent=2) --------- Comment --------- Name: //abc - From: + From: Date: Thu, 01 Jan 1970 00:00:00 +0000 Some @@ -468,7 +468,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): lines.extend(body.splitlines()) else: lines.append("Content type %s not printable. Try XML output instead" % self.content_type) - + istring = ' '*indent sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') @@ -478,12 +478,12 @@ class Comment(Tree, settings_object.SavedSettingsObject): """ Return a string displaying a thread of comments. bug_shortname is only used if auto_name_map == True. - + string_method_name (defaults to "string") is the name of the Comment method used to generate the output string for each Comment in the thread. The method must take the arguments indent and shortname. - + SIDE-EFFECT: if auto_name_map==True, calls comment_shortnames() which will sort the tree by comment.time. Avoid by calling name_map = {} @@ -507,50 +507,50 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> print a.string_thread(flatten=True) --------- Comment --------- Name: //a - From: + From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- Name: //b - From: + From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- Name: //c - From: + From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- Name: //d - From: + From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples >>> print a.string_thread() --------- Comment --------- Name: //a - From: + From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- Name: //b - From: + From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- Name: //c - From: + From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- Name: //d - From: + From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples @@ -571,7 +571,11 @@ class Comment(Tree, settings_object.SavedSettingsObject): if settings_mapfile == None: settings_mapfile = \ self.storage.get(self.id.storage("values"), default="\n") - self.settings = mapfile.parse(settings_mapfile) + try: + self.settings = mapfile.parse(settings_mapfile) + except mapfile.InvalidMapfileContents, e: + raise Exception('Invalid settings file for comment %s\n' + '(BE version missmatch?)' % self.id.user()) self._setup_saved_settings() def save_settings(self): @@ -581,7 +585,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): def save(self): """ Save any loaded contents to storage. - + However, if self.storage.is_writeable() == True, then any changes are automatically written to storage as soon as they happen, so calling this method will just waste time (unless @@ -688,7 +692,7 @@ def cmp_attr(comment_1, comment_2, attr, invert=False): val_2 = getattr(comment_2, attr) if val_1 == None: val_1 = None if val_2 == None: val_2 = None - + if invert == True : return -cmp(val_1, val_2) else : @@ -718,7 +722,7 @@ class CommentCompoundComparator (object): if val != 0 : return val return 0 - + cmp_full = CommentCompoundComparator() if libbe.TESTING == True: -- cgit From 292f341109b31c16213146f9184d0b93a425f316 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 28 Dec 2009 07:10:47 -0500 Subject: Add most comments with ignore_missing_references=True. --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index d899aa8..e386796 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -85,7 +85,7 @@ def load_comments(bug, load_full=False): dummy = comm.body # force the body to load comments.append(comm) bug.comment_root = Comment(bug, uuid=INVALID_UUID) - bug.add_comments(comments) + bug.add_comments(comments, ignore_missing_references=True) return bug.comment_root def save_comments(bug): -- cgit From 9a62c4beea7c89905dc487bdbe2e46fed4b83f21 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 29 Dec 2009 19:19:15 -0500 Subject: Restored post-colon spaces in doctests --- libbe/comment.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index e386796..ad22683 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -448,7 +448,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> print comm.string(indent=2) --------- Comment --------- Name: //abc - From: + From: Date: Thu, 01 Jan 1970 00:00:00 +0000 Some @@ -507,50 +507,50 @@ class Comment(Tree, settings_object.SavedSettingsObject): >>> print a.string_thread(flatten=True) --------- Comment --------- Name: //a - From: + From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- Name: //b - From: + From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- Name: //c - From: + From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- Name: //d - From: + From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples >>> print a.string_thread() --------- Comment --------- Name: //a - From: + From: Date: Thu, 20 Nov 2008 01:00:00 +0000 Insightful remarks --------- Comment --------- Name: //b - From: + From: Date: Thu, 20 Nov 2008 02:00:00 +0000 Critique original comment --------- Comment --------- Name: //c - From: + From: Date: Thu, 20 Nov 2008 03:00:00 +0000 Begin flamewar :p --------- Comment --------- Name: //d - From: + From: Date: Thu, 20 Nov 2008 04:00:00 +0000 Useful examples -- cgit From 6ab6b8d0255ccf3557453fc0762d1529d39462ed Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 29 Dec 2009 19:48:33 -0500 Subject: Propogate long_to_short_user() -> long_to_short_text() and inverse I'd missed some calls when I made the changes. --- libbe/comment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index ad22683..fab1f54 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -156,7 +156,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): def _set_comment_body(self, old=None, new=None, force=False): assert self.uuid != INVALID_UUID, self if self.bug != None and self.bug.bugdir != None: - new = libbe.util.id.short_to_long_user([self.bug.bugdir], new) + new = libbe.util.id.short_to_long_text([self.bug.bugdir], new) if (self.storage != None and self.storage.writeable == True) \ or force==True: assert new != None, "Can't save empty comment" @@ -464,7 +464,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): if self.content_type.startswith("text/"): body = (self.body or "") if self.bug != None and self.bug.bugdir != None: - body = libbe.util.id.long_to_short_user([self.bug.bugdir], body) + body = libbe.util.id.long_to_short_text([self.bug.bugdir], body) lines.extend(body.splitlines()) else: lines.append("Content type %s not printable. Try XML output instead" % self.content_type) -- cgit From 4d4283ecd654f1efb058cd7f7dba6be88b70ee92 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 08:11:08 -0500 Subject: Updated copyright information --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index fab1f54..b80188b 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -1,4 +1,4 @@ -# Copyright (C) 2008-2009 Gianluca Montecchi +# Copyright (C) 2008-2010 Gianluca Montecchi # Thomas Habets # W. Trevor King # -- cgit From 6abea58e9359f14b256edb2dca0690b9c3ffe875 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 21 Jan 2010 20:54:00 -0500 Subject: Add Comment.safe_in_reply_to to improve comment xml output. Now be show --xml ID | be-xml-to-mbox | catmutt shows appropriate linking regardless of missing references or references to alt-ids in the original comments. On the other hand, be show --xml ID | be import-xml could alter alt-ids. If that's a problem we could turn off save_in_reply_to usage via an option to the xml methods in the future. --- libbe/comment.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index b80188b..f0cc45c 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -253,6 +253,23 @@ class Comment(Tree, settings_object.SavedSettingsObject): return str(value) return value + def safe_in_reply_to(self): + """ + Return self.in_reply_to, except... + * if no comment matches that id, in which case return None. + * if that id matches another comments .alt_id, in which case + return the matching comments .uuid. + """ + if self.in_reply_to == None: + return None + else: + try: + irt_comment = self.bug.comment_from_uuid( + self.in_reply_to, match_alt_id=True) + return irt_comment.uuid + except KeyError: + return None + def xml(self, indent=0): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") @@ -281,7 +298,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): info = [('uuid', self.uuid), ('alt-id', self.alt_id), ('short-name', self.id.user()), - ('in-reply-to', self.in_reply_to), + ('in-reply-to', self.safe_in_reply_to()), ('author', self._setting_attr_string('author')), ('date', self.date), ('content-type', self.content_type), -- cgit From bda68bb5d93f4b608fb1dd17c5a0cf1bb406daf9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 22 Jan 2010 11:30:26 -0500 Subject: Reworked settings_object module, but command.init tests still fail: $ python test.py libbe.command.init Doctest: libbe.command.init.Init ... FAIL ... ----------------------- File ".../libbe/command/init.py", line 47, in libbe.command.init.Init Failed example: ui.run(cmd) Exception raised: Traceback (most recent call last): ... File "/tmp/be.wtk/libbe/command/init.py", line 97, in _run bd = libbe.bugdir.BugDir(storage, from_storage=False) File "/tmp/be.wtk/libbe/bugdir.py", line 185, in __init__ self.save() File "/tmp/be.wtk/libbe/bugdir.py", line 228, in save self.save_settings() File "/tmp/be.wtk/libbe/bugdir.py", line 204, in save_settings mf = mapfile.generate(self._get_saved_settings()) File "/tmp/be.wtk/libbe/storage/util/settings_object.py", line 230, in _get_saved_settings self, self._setting_name_to_attr_name(k)) File "/tmp/be.wtk/libbe/storage/util/properties.py", line 194, in _fget value = fget(self) File "/tmp/be.wtk/libbe/storage/util/properties.py", line 329, in _fget primer(self) File "/tmp/be.wtk/libbe/storage/util/settings_object.py", line 69, in prop_load_settings self.load_settings() File "/tmp/be.wtk/libbe/bugdir.py", line 194, in load_settings self.settings = mapfile.parse(settings_mapfile) File "/tmp/be.wtk/libbe/storage/util/mapfile.py", line 123, in parse c = yaml.load(contents) ... File "/usr/lib/python2.6/site-packages/yaml/reader.py", line 213, in update_raw data = self.stream.read(size) AttributeError: 'NoneType' object has no attribute 'read' ... --- libbe/comment.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index f0cc45c..f1b979e 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -206,8 +206,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): if from_storage == False: if uuid == None: self.uuid = libbe.util.id.uuid_gen() - self.settings = {} - self._setup_saved_settings() self.time = int(time.time()) # only save to second precision self.in_reply_to = in_reply_to self.body = body -- cgit From 331a641325daf3b067ecde2a51b5308c9287444e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 22 Jan 2010 20:38:57 -0500 Subject: Move BugDir, Bug, and Comment to new _setup_saved_settings --- libbe/comment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index f1b979e..accd4df 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -93,7 +93,7 @@ def save_comments(bug): comment.save() -class Comment(Tree, settings_object.SavedSettingsObject): +class Comment (Tree, settings_object.SavedSettingsObject): """ >>> c = Comment() >>> c.uuid != None @@ -587,11 +587,11 @@ class Comment(Tree, settings_object.SavedSettingsObject): settings_mapfile = \ self.storage.get(self.id.storage("values"), default="\n") try: - self.settings = mapfile.parse(settings_mapfile) + settings = mapfile.parse(settings_mapfile) except mapfile.InvalidMapfileContents, e: raise Exception('Invalid settings file for comment %s\n' '(BE version missmatch?)' % self.id.user()) - self._setup_saved_settings() + self._setup_saved_settings(settings) def save_settings(self): mf = mapfile.generate(self._get_saved_settings()) -- cgit From 606a11d1b0a415d0b24cc56634660be2a985bb7c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 23 Jan 2010 16:44:55 -0500 Subject: Fix broken handling of non-text/plain comments --- libbe/comment.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index accd4df..21118f0 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -26,6 +26,13 @@ import os.path import sys import time import types +try: + from email.mime.base import MIMEBase + from email.encoders import encode_base64 +except ImportError: + # adjust to old python 2.4 + from email.MIMEBase import MIMEBase + from email.Encoders import encode_base64 try: # import core module, Python >= 2.5 from xml.etree import ElementTree except ImportError: # look for non-core module @@ -289,9 +296,9 @@ class Comment (Tree, settings_object.SavedSettingsObject): body = (self.body or '').rstrip('\n') else: maintype,subtype = self.content_type.split('/',1) - msg = email.mime.base.MIMEBase(maintype, subtype) + msg = MIMEBase(maintype, subtype) msg.set_payload(self.body or '') - email.encoders.encode_base64(msg) + encode_base64(msg) body = base64.encodestring(self.body or '') info = [('uuid', self.uuid), ('alt-id', self.alt_id), -- cgit From 7d01fa142b05149479e633525fc4d7ddfa2addf0 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 24 Jan 2010 20:39:00 -0500 Subject: Fixed #bea/8fc# : be crashes on outdated id-cache Also explicitly avoid loading or saving settings for root comments. --- libbe/comment.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 21118f0..f850042 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -590,6 +590,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): # methods for saving/loading/acessing settings and properties. def load_settings(self, settings_mapfile=None): + if self.uuid == INVALID_UUID: + return if settings_mapfile == None: settings_mapfile = \ self.storage.get(self.id.storage("values"), default="\n") @@ -601,6 +603,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): self._setup_saved_settings(settings) def save_settings(self): + if self.uuid == INVALID_UUID: + return mf = mapfile.generate(self._get_saved_settings()) self.storage.set(self.id.storage("values"), mf) -- cgit From d98052a067923616efd3a27626dca29301d74246 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 2 Feb 2010 12:32:53 -0500 Subject: Restore "content_type" kwarg to Comment.new_reply(). It had been removed in revno: 473.1.43 committer: W. Trevor King branch nick: be.restructure timestamp: Mon 2009-12-14 07:37:51 -0500 message: Transitioned comment to Command format when we pushed unicode encoding/decoding back to the Storage backend. However, with the addition of libbe.util.id.short_to_long_text(), we need it again. Also add a Doctest showing a non-text/* comment, so utilities dealing with them can see what they'll be working with. --- libbe/comment.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index f850042..a6a4ebd 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -162,7 +162,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): decode=self.content_type.startswith("text/")) def _set_comment_body(self, old=None, new=None, force=False): assert self.uuid != INVALID_UUID, self - if self.bug != None and self.bug.bugdir != None: + if self.content_type.startswith('text/') \ + and self.bug != None and self.bug.bugdir != None: new = libbe.util.id.short_to_long_text([self.bug.bugdir], new) if (self.storage != None and self.storage.writeable == True) \ or force==True: @@ -191,16 +192,19 @@ class Comment (Tree, settings_object.SavedSettingsObject): def extra_strings(): return {} def __init__(self, bug=None, uuid=None, from_storage=False, - in_reply_to=None, body=None): + in_reply_to=None, body=None, content_type=None): """ Set from_storage=True to load an old comment. Set from_storage=False to create a new comment. The uuid option is required when from_storage==True. - The in_reply_to and body options are only used if - from_storage==False (the default). When from_storage==True, - they are loaded from the bug database. + The in_reply_to, body, and content_type options are only used + if from_storage==False (the default). When + from_storage==True, they are loaded from the bug database. + content_type decides if the body should be run through + libbe.util.id.short_to_long_text() before saving. See + ._set_comment_body() for details. in_reply_to should be the uuid string of the parent comment. """ @@ -215,6 +219,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): self.uuid = libbe.util.id.uuid_gen() self.time = int(time.time()) # only save to second precision self.in_reply_to = in_reply_to + if content_type != None: + self.content_type = content_type self.body = body if self.bug != None: self.storage = self.bug.storage @@ -291,6 +297,17 @@ class Comment (Tree, settings_object.SavedSettingsObject): insightful remarks + >>> comm.content_type = 'image/png' + >>> print comm.xml() + + 0123 + //012 + + Thu, 01 Jan 1970 00:00:00 +0000 + image/png + U29tZQppbnNpZ2h0ZnVsCnJlbWFya3MK + + """ if self.content_type.startswith('text/'): body = (self.body or '').rstrip('\n') @@ -644,7 +661,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): reply.in_reply_to = self.uuid self.append(reply) - def new_reply(self, body=None): + def new_reply(self, body=None, content_type=None): """ >>> comm = Comment(bug=None, body="Some insightful remarks") >>> repA = comm.new_reply("Critique original comment") @@ -652,7 +669,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): >>> repB.in_reply_to == repA.uuid True """ - reply = Comment(self.bug, body=body) + reply = Comment(self.bug, body=body, content_type=content_type) self.add_reply(reply) return reply -- cgit From 3f27c5c3bbc1ecd00db51c4894a9bf9651ae4fbb Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 2 Feb 2010 14:11:14 -0500 Subject: Sort comments in `be html`. --- libbe/comment.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index a6a4ebd..dd245de 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -523,14 +523,6 @@ class Comment (Tree, settings_object.SavedSettingsObject): Comment in the thread. The method must take the arguments indent and shortname. - SIDE-EFFECT: if auto_name_map==True, calls comment_shortnames() - which will sort the tree by comment.time. Avoid by calling - name_map = {} - for shortname,comment in comm.comment_shortnames(bug_shortname): - name_map[comment.uuid] = shortname - comm.sort(key=lambda c : c.author) # your sort - comm.string_thread(name_map=name_map) - >>> a = Comment(bug=None, uuid="a", body="Insightful remarks") >>> a.time = utility.str_to_time("Thu, 20 Nov 2008 01:00:00 +0000") >>> b = a.new_reply("Critique original comment") -- cgit From 4c71190ebafd7ce198a5c0047588c4b2f7e5ef58 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 13:44:05 -0500 Subject: Added bugdir and comment modules to Sphinx docs --- libbe/comment.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index dd245de..003bf71 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -16,8 +16,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Define the Comment class for representing bug comments. +"""Define the :class:`Comment` class for representing bug comments. """ import base64 @@ -101,7 +100,10 @@ def save_comments(bug): class Comment (Tree, settings_object.SavedSettingsObject): - """ + """Comments are a notes that attach to :class:`libbe.bug.Bug`\s in + threaded trees. In mailing-list terms, a comment is analogous to + a single part of an email. + >>> c = Comment() >>> c.uuid != None True @@ -194,19 +196,19 @@ class Comment (Tree, settings_object.SavedSettingsObject): def __init__(self, bug=None, uuid=None, from_storage=False, in_reply_to=None, body=None, content_type=None): """ - Set from_storage=True to load an old comment. - Set from_storage=False to create a new comment. + Set ``from_storage=True`` to load an old comment. + Set ``from_storage=False`` to create a new comment. - The uuid option is required when from_storage==True. + The ``uuid`` option is required when ``from_storage==True``. The in_reply_to, body, and content_type options are only used - if from_storage==False (the default). When - from_storage==True, they are loaded from the bug database. - content_type decides if the body should be run through - libbe.util.id.short_to_long_text() before saving. See - ._set_comment_body() for details. + if ``from_storage==False`` (the default). When + ``from_storage==True``, they are loaded from the bug database. + ``content_type`` decides if the body should be run through + :func:`util.id.short_to_long_text` before saving. See + :meth:`_set_comment_body` for details. - in_reply_to should be the uuid string of the parent comment. + ``in_reply_to`` should be the uuid string of the parent comment. """ Tree.__init__(self) settings_object.SavedSettingsObject.__init__(self) @@ -267,6 +269,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): def safe_in_reply_to(self): """ Return self.in_reply_to, except... + * if no comment matches that id, in which case return None. * if that id matches another comments .alt_id, in which case return the matching comments .uuid. @@ -337,7 +340,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): return istring + sep.join(lines).rstrip('\n') def from_xml(self, xml_string, verbose=True): - """ + u""" Note: If alt-id is not given, translates any fields to fields. >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") @@ -412,6 +415,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): """ 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' @@ -621,7 +625,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): """ Save any loaded contents to storage. - However, if self.storage.is_writeable() == True, then any + However, if ``self.storage.is_writeable() == True``, then any changes are automatically written to storage as soon as they happen, so calling this method will just waste time (unless something else has been messing with your stored files). @@ -666,8 +670,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): return reply def comment_from_uuid(self, uuid, match_alt_id=True): - """ - Use a uuid to look up a comment. + """Use a uuid to look up a comment. + >>> a = Comment(bug=None, uuid="a") >>> b = a.new_reply() >>> b.uuid = "b" @@ -708,6 +712,7 @@ def cmp_attr(comment_1, comment_2, attr, invert=False): Compare a general attribute between two comments using the conventional comparison rule for that attribute type. If invert == True, sort *against* that convention. + >>> attr="author" >>> commentA = Comment() >>> commentB = Comment() -- cgit From 413626d3b77e9bf89389a272ed489da29f3d9877 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 16:53:57 -0500 Subject: Use numpydoc and generate-libbe-txt.py to autogenerate API documentation --- libbe/comment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libbe/comment.py') diff --git a/libbe/comment.py b/libbe/comment.py index 003bf71..d8632a4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -100,7 +100,7 @@ def save_comments(bug): class Comment (Tree, settings_object.SavedSettingsObject): - """Comments are a notes that attach to :class:`libbe.bug.Bug`\s in + """Comments are a notes that attach to :class:`~libbe.bug.Bug`\s in threaded trees. In mailing-list terms, a comment is analogous to a single part of an email. -- cgit