Moved bugdir, bug, and comment over to new id implementation.
authorW. Trevor King <wking@drexel.edu>
Fri, 11 Dec 2009 00:31:47 +0000 (19:31 -0500)
committerW. Trevor King <wking@drexel.edu>
Fri, 11 Dec 2009 00:31:47 +0000 (19:31 -0500)
libbe/bug.py
libbe/bugdir.py
libbe/comment.py
libbe/util/id.py

index 5755b6395ce8763aadc8f55e384073c502b32aa5..1f9677971c61f37dba23affc6842e63b819547da 100644 (file)
@@ -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
index 02ff96f03915630d8588a8358a72dd2884b8d1e6..39eface6ba04e73f160d40c5fb09fcc070546d5a 100644 (file)
@@ -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()
index f5a630989d11a46b8ee5eccdcced071172e9f129..ebfde23c091da0bd1eb8006c8407f4d892301768 100644 (file)
@@ -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
index d4437063d427d523bdf2da621d66f9597e5805d5..ab623597d53bb93db7e78e6c6521e6b1896d34a1 100644 (file)
@@ -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):
     """