diff options
-rw-r--r-- | libbe/bug.py | 168 | ||||
-rw-r--r-- | libbe/bugdir.py | 84 | ||||
-rw-r--r-- | libbe/comment.py | 187 | ||||
-rw-r--r-- | libbe/util/id.py | 28 |
4 files changed, 171 insertions, 296 deletions
diff --git a/libbe/bug.py b/libbe/bug.py index 5755b63..1f96779 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -237,35 +237,25 @@ class Bug(settings_object.SavedSettingsObject): @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_storage(self): - if hasattr(self.bugdir, "storage"): - return self.bugdir.storage - - @Property - @cached_property(generator=_get_storage) - @local_property("storage") - @doc_property(doc="A revision control system instance.") - def storage(): return {} - def __init__(self, bugdir=None, uuid=None, from_storage=False, load_comments=False, summary=None): settings_object.SavedSettingsObject.__init__(self) self.bugdir = bugdir + self.storage = None self.uuid = uuid + self.id = libbe.util.id.ID(self, 'bug') 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.summary = summary - if set_writeable == True: - self.storage.writeable = True + dummy = self.comment_root + if self.bugdir != None: + self.storage = self.bugdir.storage + if from_storage == False: + if self.storage != None and self.storage.is_writeable(): self.save() def __repr__(self): @@ -287,20 +277,47 @@ class Bug(settings_object.SavedSettingsObject): return str(value) return value - def xml(self, indent=0, shortname=None, show_comments=False): - if shortname == None: - if self.bugdir == None: - shortname = self.uuid + def string(self, shortlist=False, show_comments=False): + if shortlist == False: + if self.time == None: + timestring = "" else: - shortname = self.bugdir.bug_shortname(self) + htime = utility.handy_time(self.time) + timestring = "%s (%s)" % (htime, self.time_string) + info = [("ID", self.uuid), + ("Short name", self.id.user()), + ("Severity", self.severity), + ("Status", self.status), + ("Assigned", self._setting_attr_string("assigned")), + ("Reporter", self._setting_attr_string("reporter")), + ("Creator", self._setting_attr_string("creator")), + ("Created", timestring)] + longest_key_len = max([len(k) for k,v in info]) + infolines = [" %*s : %s\n" %(longest_key_len,k,v) for k,v in info] + bugout = "".join(infolines) + "%s" % self.summary.rstrip('\n') + else: + statuschar = self.status[0] + severitychar = self.severity[0] + chars = "%c%c" % (statuschar, severitychar) + bugout = "%s:%s: %s" % (self.id.user(),chars,self.summary.rstrip('\n')) + + if show_comments == True: + # take advantage of the string_thread(auto_name_map=True) + # SIDE-EFFECT of sorting by comment time. + comout = self.comment_root.string_thread(flatten=False) + output = bugout + '\n' + comout.rstrip('\n') + else : + output = bugout + return output + def xml(self, indent=0, show_comments=False): if self.time == None: timestring = "" else: timestring = utility.time_to_str(self.time) info = [('uuid', self.uuid), - ('short-name', shortname), + ('short-name', self.id.user()), ('severity', self.severity), ('status', self.status), ('assigned', self.assigned), @@ -315,9 +332,7 @@ class Bug(settings_object.SavedSettingsObject): for estr in self.extra_strings: 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, - bug_shortname=shortname) + comout = self.comment_root.xml_thread(indent=indent+2) if len(comout) > 0: lines.append(comout) lines.append('</bug>') @@ -335,16 +350,16 @@ 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", show_comments=True) + >>> xml = bugA.xml(show_comments=True) >>> bugB = Bug() >>> bugB.from_xml(xml, verbose=True) - >>> bugB.xml(shortname="bug-1", show_comments=True) == xml + >>> bugB.xml(show_comments=True) == xml False >>> bugB.uuid = bugB.alt_id >>> for comm in bugB.comments(): ... comm.uuid = comm.alt_id ... comm.alt_id = None - >>> bugB.xml(shortname="bug-1", show_comments=True) == xml + >>> bugB.xml(show_comments=True) == xml True >>> bugB.explicit_attrs # doctest: +NORMALIZE_WHITESPACE ['severity', 'status', 'creator', 'created', 'summary'] @@ -414,10 +429,10 @@ class Bug(settings_object.SavedSettingsObject): >>> commC.uuid = 'commC' >>> commC.in_reply_to = commA.uuid >>> bugA.add_comment(commC) - >>> print bugA.xml(shortname="bug-1", show_comments=True) # doctest: +ELLIPSIS + >>> print bugA.xml(show_comments=True) # doctest: +ELLIPSIS <bug> <uuid>0123</uuid> - <short-name>bug-1</short-name> + <short-name>/012</short-name> <severity>minor</severity> <status>open</status> <creator>Jack</creator> @@ -425,7 +440,7 @@ class Bug(settings_object.SavedSettingsObject): <summary>Need to test Bug.add_comment()</summary> <comment> <uuid>commA</uuid> - <short-name>bug-1:1</short-name> + <short-name>/012/commA</short-name> <author></author> <date>...</date> <content-type>text/plain</content-type> @@ -433,7 +448,7 @@ class Bug(settings_object.SavedSettingsObject): </comment> <comment> <uuid>commC</uuid> - <short-name>bug-1:2</short-name> + <short-name>/012/commC</short-name> <in-reply-to>commA</in-reply-to> <author></author> <date>...</date> @@ -442,7 +457,7 @@ class Bug(settings_object.SavedSettingsObject): </comment> <comment> <uuid>commB</uuid> - <short-name>bug-1:3</short-name> + <short-name>/012/commB</short-name> <author></author> <date>...</date> <content-type>text/plain</content-type> @@ -546,7 +561,7 @@ class Bug(settings_object.SavedSettingsObject): >>> print bugA.xml(show_comments=True) # doctest: +ELLIPSIS <bug> <uuid>0123</uuid> - <short-name>0123</short-name> + <short-name>/012</short-name> <severity>minor</severity> <status>open</status> <creator>John</creator> @@ -557,7 +572,7 @@ class Bug(settings_object.SavedSettingsObject): <extra-string>TAG: very helpful</extra-string> <comment> <uuid>uuid-commA</uuid> - <short-name>0123:1</short-name> + <short-name>/012/uuid-commA</short-name> <author></author> <date>...</date> <content-type>text/plain</content-type> @@ -565,7 +580,7 @@ class Bug(settings_object.SavedSettingsObject): </comment> <comment> <uuid>uuid-commB</uuid> - <short-name>0123:2</short-name> + <short-name>/012/uuid-commB</short-name> <author></author> <date>...</date> <content-type>text/plain</content-type> @@ -603,6 +618,7 @@ class Bug(settings_object.SavedSettingsObject): if accept_comments == True: o_comm_copy = copy.copy(o_comm) o_comm_copy.bug = self + o_comm_copy.id = libbe.util.id.ID(o_comm_copy, 'comment') self.comment_root.add_reply(o_comm_copy) elif change_exception == True: raise ValueError, \ @@ -613,63 +629,18 @@ class Bug(settings_object.SavedSettingsObject): accept_extra_strings=accept_extra_strings, change_exception=change_exception) - def string(self, shortlist=False, show_comments=False): - if self.bugdir == None: - shortname = self.uuid - else: - shortname = self.bugdir.bug_shortname(self) - if shortlist == False: - if self.time == None: - timestring = "" - else: - htime = utility.handy_time(self.time) - timestring = "%s (%s)" % (htime, self.time_string) - info = [("ID", self.uuid), - ("Short name", shortname), - ("Severity", self.severity), - ("Status", self.status), - ("Assigned", self._setting_attr_string("assigned")), - ("Reporter", self._setting_attr_string("reporter")), - ("Creator", self._setting_attr_string("creator")), - ("Created", timestring)] - longest_key_len = max([len(k) for k,v in info]) - infolines = [" %*s : %s\n" %(longest_key_len,k,v) for k,v in info] - bugout = "".join(infolines) + "%s" % self.summary.rstrip('\n') - else: - statuschar = self.status[0] - severitychar = self.severity[0] - chars = "%c%c" % (statuschar, severitychar) - bugout = "%s:%s: %s" % (shortname,chars,self.summary.rstrip('\n')) - - if show_comments == True: - # take advantage of the string_thread(auto_name_map=True) - # SIDE-EFFECT of sorting by comment time. - comout = self.comment_root.string_thread(flatten=False, - auto_name_map=True, - bug_shortname=shortname) - output = bugout + '\n' + comout.rstrip('\n') - else : - output = bugout - return output - # 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"], str(args) - return libbe.util.id.bug_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): """ @@ -683,11 +654,11 @@ class Bug(settings_object.SavedSettingsObject): """ assert self.storage != None, "Can't save without storage" if self.bugdir != None: - parent = self.bugdir.id() + parent = self.bugdir.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.storage(), parent=parent) + self.storage.add(self.id.storage('values'), parent=self.id.storage()) self.save_settings() if len(self.comment_root) > 0: comment.save_comments(self) @@ -707,10 +678,14 @@ class Bug(settings_object.SavedSettingsObject): self.storage.writeable = w def remove(self): - self.storage.recursive_remove(self.id()) + self.storage.recursive_remove(self.id.storage()) # methods for managing comments + def uuids(self): + for comment in self.comments(): + yield comment.uuid + def comments(self): for comment in self.comment_root.traverse(): yield comment @@ -719,20 +694,15 @@ class Bug(settings_object.SavedSettingsObject): comm = self.comment_root.new_reply(body=body) return comm - def comment_from_shortname(self, shortname, *args, **kwargs): - return self.comment_root.comment_from_shortname(shortname, - *args, **kwargs) - def comment_from_uuid(self, uuid, *args, **kwargs): return self.comment_root.comment_from_uuid(uuid, *args, **kwargs) - def comment_shortnames(self, shortname=None): - """ - SIDE-EFFECT : Comment.comment_shortnames will sort the comment - tree by comment.time - """ - for id, comment in self.comment_root.comment_shortnames(shortname): - yield (id, comment) + # methods for id generation + + def sibling_uuids(self): + if self.bugdir != None: + return self.bugdir.uuids() + return [] # The general rule for bug sorting is that "more important" bugs are diff --git a/libbe/bugdir.py b/libbe/bugdir.py index 02ff96f..39eface 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -173,6 +173,7 @@ class BugDir (list, settings_object.SavedSettingsObject): list.__init__(self) settings_object.SavedSettingsObject.__init__(self) self.storage = storage + self.id = libbe.util.id.ID(self, 'bugdir') if from_storage == True: self.load_settings() else: @@ -185,16 +186,10 @@ class BugDir (list, settings_object.SavedSettingsObject): # methods for saving/loading/accessing settings and properties. - def id(self, *args): - assert len(args) <= 1, str(args) - if len(args) == 1: - assert args[0] in ['settings'], str(args) - return libbe.util.id.bugdir_id(self, *args) - def load_settings(self, settings_mapfile=None): if settings_mapfile == None: settings_mapfile = \ - self.storage.get(self.id('settings'), default='\n') + self.storage.get(self.id.storage('settings'), default='\n') self.settings = mapfile.parse(settings_mapfile) self._setup_saved_settings() self._setup_user_id(self.user_id) @@ -204,7 +199,7 @@ class BugDir (list, settings_object.SavedSettingsObject): def save_settings(self): mf = mapfile.generate(self._get_saved_settings()) - self.storage.set(self.id('settings'), mf) + self.storage.set(self.id.storage('settings'), mf) def load_all_bugs(self): """ @@ -224,9 +219,8 @@ class BugDir (list, settings_object.SavedSettingsObject): happen, so calling this method will just waste time (unless something else has been messing with your stored files). """ - self.uuid = 'BD' - self.storage.add(self.id()) - self.storage.add(self.id('settings'), parent=self.id()) + self.storage.add(self.id.storage()) + self.storage.add(self.id.storage('settings'), parent=self.id.storage()) self.save_settings() for bug in self: bug.save() @@ -241,10 +235,11 @@ class BugDir (list, settings_object.SavedSettingsObject): yield bug.uuid if self.storage != None and self.storage.is_readable(): # and the ones that are still just in storage - for id in self.storage.children(self.id()): - parsed = libbe.util.id.parse_id(id) - if parsed['type'] == 'bug' and parsed['bug'] not in uuids: - yield parsed['bug'] + child_uuids = libbe.util.id.child_uuids( + self.storage.children(self.id.storage())) + for id in child_uuids: + if id not in uuids: + yield id def _clear_bugs(self): while len(self) > 0: @@ -258,7 +253,8 @@ class BugDir (list, settings_object.SavedSettingsObject): return bg def new_bug(self, summary=None, _uuid=None): - bg = bug.Bug(bugdir=self, uuid=_uuid, summary=summary) + bg = bug.Bug(bugdir=self, uuid=_uuid, summary=summary, + from_storage=False) self.append(bg) self._bug_map_gen() return bg @@ -268,45 +264,6 @@ class BugDir (list, settings_object.SavedSettingsObject): if self.storage.is_writeable(): bug.remove() - def bug_shortname(self, bug): - """ - Generate short names from uuids. Picks the minimum number of - characters (>=3) from the beginning of the uuid such that the - short names are unique. - - Obviously, as the number of bugs in the database grows, these - short names will cease to be unique. The complete uuid should be - used for long term reference. - """ - chars = 3 - for uuid in self._bug_map.keys(): - if bug.uuid == uuid: - continue - while (bug.uuid[:chars] == uuid[:chars]): - chars+=1 - return bug.uuid[:chars] - - def bug_from_shortname(self, shortname): - """ - >>> bd = SimpleBugDir(memory=True) - >>> bug_a = bd.bug_from_shortname('a') - >>> print type(bug_a) - <class 'libbe.bug.Bug'> - >>> print bug_a - a:om: Bug A - >>> bd.cleanup() - """ - matches = [] - self._bug_map_gen() - for uuid in self._bug_map.keys(): - if uuid.startswith(shortname): - matches.append(uuid) - if len(matches) > 1: - raise MultipleBugMatches(shortname, matches) - if len(matches) == 1: - return self.bug_from_uuid(matches[0]) - raise NoBugMatches(shortname) - def bug_from_uuid(self, uuid): if not self.has_bug(uuid): raise KeyError("No bug matches %s\n bug map: %s\n root: %s" \ @@ -322,6 +279,11 @@ class BugDir (list, settings_object.SavedSettingsObject): return False return True + # methods for id generation + + def sibling_uuids(self): + return [] + # methods for managing duplicate BugDirs def duplicate_bugdir(self, revision): @@ -344,7 +306,7 @@ class BugDir (list, settings_object.SavedSettingsObject): if parsed['type'] == 'bugdir': assert parsed['remaining'] == ['settings'], parsed['remaining'] dbd._settings = copy.copy(self._settings) - mf = self.storage.get(self.id('settings'), default='\n', + mf = self.storage.get(self.id.storage('settings'), default='\n', revision=revision) dbd.load_settings(mf) else: @@ -357,7 +319,7 @@ class BugDir (list, settings_object.SavedSettingsObject): dbd._bug_map[parsed['bug']] = bug if parsed['type'] == 'bug': assert parsed['remaining'] == ['values'], parsed['remaining'] - mf = self.storage.get(self.id('values'), default='\n', + mf = self.storage.get(self.id.storage('values'), default='\n', revision=revision) bug.load_settings(mf) elif parsed['type'] == 'comment': @@ -366,11 +328,11 @@ class BugDir (list, settings_object.SavedSettingsObject): bug.comment_root = copy.deepcopy(bug.comment_root) comment = bug.comment_from_uuid(parsed['comment']) if parsed['remaining'] == ['values']: - mf = self.storage.get(self.id('values'), default='\n', + mf = self.storage.get(self.id.storage('values'), default='\n', revision=revision) comment.load_settings(mf) else: - body = self.storage.get(self.id('body'), default='\n', + body = self.storage.get(self.id.storage('body'), default='\n', revision=revision) comment.body = body else: @@ -552,6 +514,8 @@ if libbe.TESTING == True: self.storage.disconnect() # flush self.storage.connect() bugdir._clear_bugs() + uuids = sorted(bugdir.uuids()) + self.failUnless(uuids == ['a', 'b'], uuids) uuids = sorted([bug.uuid for bug in bugdir]) self.failUnless(uuids == [], uuids) bugdir.load_all_bugs() @@ -570,6 +534,8 @@ if libbe.TESTING == True: uuids = sorted([bug.uuid for bug in bugdir]) self.failUnless(uuids == ['a', 'b'], uuids) bugdir._clear_bugs() + uuids = sorted(bugdir.uuids()) + self.failUnless(uuids == [], uuids) uuids = sorted([bug.uuid for bug in bugdir]) self.failUnless(uuids == [], uuids) bugdir.cleanup() 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 <jdoe@example.com>" >>> print comm --------- Comment --------- - Name: com-1 + Name: //com From: Jane Doe <jdoe@example.com> Date: Thu, 20 Nov 2008 15:55:11 +0000 <BLANKLINE> @@ -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) <comment> <uuid>0123</uuid> - <short-name>com-1</short-name> + <short-name>//012</short-name> <author></author> <date>Thu, 01 Jan 1970 00:00:00 +0000</date> <content-type>text/plain</content-type> @@ -285,8 +272,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): remarks</body> </comment> """ - 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() <comment> <uuid>0123</uuid> - <short-name>0123</short-name> + <short-name>//012</short-name> <author>John</author> <date>Thu, 01 Jan 1970 00:00:00 +0000</date> <content-type>text/plain</content-type> @@ -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 <BLANKLINE> @@ -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 <BLANKLINE> Insightful remarks --------- Comment --------- - Name: b + Name: //b From: Date: Thu, 20 Nov 2008 02:00:00 +0000 <BLANKLINE> Critique original comment --------- Comment --------- - Name: c + Name: //c From: Date: Thu, 20 Nov 2008 03:00:00 +0000 <BLANKLINE> Begin flamewar :p --------- Comment --------- - Name: d + Name: //d From: Date: Thu, 20 Nov 2008 04:00:00 +0000 <BLANKLINE> 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 <BLANKLINE> Insightful remarks --------- Comment --------- - Name: bug-1:2 + Name: //b From: Date: Thu, 20 Nov 2008 02:00:00 +0000 <BLANKLINE> Critique original comment --------- Comment --------- - Name: bug-1:3 + Name: //c From: Date: Thu, 20 Nov 2008 03:00:00 +0000 <BLANKLINE> Begin flamewar :p --------- Comment --------- - Name: bug-1:4 + Name: //d From: Date: Thu, 20 Nov 2008 04:00:00 +0000 <BLANKLINE> 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 diff --git a/libbe/util/id.py b/libbe/util/id.py index d443706..ab62359 100644 --- a/libbe/util/id.py +++ b/libbe/util/id.py @@ -137,7 +137,7 @@ class ID (object): referenced object. It would be hard to find bug 'XYZ' if that's all you knew. Much easier with 'ABC/XYZ', where ABC is the bugdir. Each project can publish a list of bugdir-id -x - to - location mappings, e.g. + - to - location mappings, e.g. ABC...(full uuid)...DEF https://server.com/projectX/be/ which is easier than publishing all-object-ids-to-location mappings. @@ -169,9 +169,9 @@ x - to - location mappings, e.g. self._object = object self._type = type assert self._type in HIERARCHY, self._type - self.uuid = self._object.uuid def storage(self, *args): + import libbe.comment return _assemble(self._object.uuid, *args) def _ancestors(self): @@ -182,7 +182,7 @@ x - to - location mappings, e.g. o = self._object for i in range(index, 0, -1): parent_name = HIERARCHY[i-1] - o = getattr(o, parent_name) + o = getattr(o, parent_name, None) ret.insert(0, o) return ret @@ -190,8 +190,26 @@ x - to - location mappings, e.g. return _assemble(*[o.uuid for o in self._ancestors()]) def user(self): - return _assemble(*[_truncate(o.uuid, o.sibling_uuids()) - for o in self._ancestors()]) + ids = [] + for o in self._ancestors(): + if o == None: + ids.append(None) + else: + ids.append(_truncate(o.uuid, o.sibling_uuids())) + return _assemble(*ids) + +def child_uuids(child_storage_ids): + """ + Extract uuid children from other children generated by the + ID.storage() method. + >>> list(child_uuids(['abc123/values', '123abc', '123def'])) + ['123abc', '123def'] + """ + for id in child_storage_ids: + fields = libbe.util.id._split(id) + if len(fields) == 1: + yield fields[0] + def parse_user(id): """ |