Fixed libbe.command.diff + ugly BugDir.duplicate_bugdir implementation
authorW. Trevor King <wking@drexel.edu>
Tue, 15 Dec 2009 11:44:20 +0000 (06:44 -0500)
committerW. Trevor King <wking@drexel.edu>
Tue, 15 Dec 2009 11:44:20 +0000 (06:44 -0500)
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.

36 files changed:
libbe/bug.py
libbe/bugdir.py
libbe/command/base.py
libbe/command/commit.py
libbe/command/diff.py
libbe/command/html.py
libbe/command/import_xml.py
libbe/command/list.py
libbe/command/merge.py
libbe/command/set.py
libbe/command/severity.py
libbe/command/show.py
libbe/command/status.py
libbe/command/subscribe.py
libbe/command/tag.py
libbe/command/target.py
libbe/command/util.py
libbe/comment.py
libbe/diff.py
libbe/storage/base.py
libbe/storage/util/config.py
libbe/storage/util/mapfile.py
libbe/storage/util/settings_object.py
libbe/storage/util/upgrade.py
libbe/storage/vcs/arch.py
libbe/storage/vcs/base.py
libbe/storage/vcs/bzr.py
libbe/storage/vcs/darcs.py
libbe/storage/vcs/git.py
libbe/storage/vcs/hg.py
libbe/ui/command_line.py
libbe/util/encoding.py
libbe/util/id.py
libbe/util/plugin.py
libbe/util/subproc.py
libbe/util/utility.py

index da9a1a2dc634266e42bab195647363b7604685f7..6ab4d78a699a45fee4b6e128c3b9328a5404c886 100644 (file)
@@ -24,6 +24,7 @@ import copy
 import os
 import os.path
 import errno
+import sys
 import time
 import types
 try: # import core module, Python >= 2.5
@@ -170,7 +171,7 @@ class Bug(settings_object.SavedSettingsObject):
                          check_fn=lambda s: s in status_values,
                          require_save=True)
     def status(): return {}
-    
+
     @property
     def active(self):
         return self.status in active_status_values
@@ -249,7 +250,7 @@ class Bug(settings_object.SavedSettingsObject):
         if self.bugdir != None:
             self.storage = self.bugdir.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 __repr__(self):
@@ -294,7 +295,7 @@ class Bug(settings_object.SavedSettingsObject):
             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:
             self.comment_root.sort(cmp=libbe.comment.cmp_time, reverse=True)
             comout = self.comment_root.string_thread(flatten=False)
@@ -398,7 +399,7 @@ class Bug(settings_object.SavedSettingsObject):
                 self.explicit_attrs.append(attr_name)
                 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 uuid != self.uuid:
             if not hasattr(self, 'alt_id') or self.alt_id == None:
@@ -492,7 +493,7 @@ class Bug(settings_object.SavedSettingsObject):
             except KeyError:
                 if ignore_missing_references == True:
                     print >> sys.stderr, \
-                        "Ignoring missing reference to %s" % c.in_reply_to
+                        'Ignoring missing reference to %s' % c.in_reply_to
                     parent = default_parent
                     if parent.uuid != comment.INVALID_UUID:
                         c.in_reply_to = parent.uuid
@@ -628,7 +629,11 @@ class Bug(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 bug %s\n'
+                            '(BE version missmatch?)' % self.id.user())
         self._setup_saved_settings()
 
     def save_settings(self):
@@ -639,7 +644,7 @@ class Bug(settings_object.SavedSettingsObject):
         """
         Save any loaded contents to storage.  Because of lazy loading
         of comments, this is actually not too inefficient.
-        
+
         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
@@ -666,14 +671,14 @@ class Bug(settings_object.SavedSettingsObject):
             # next _get_comment_root returns a fresh version.  Turn of
             # writing temporarily so we don't write our blank comment
             # tree to disk.
-            w = self.storage.writeable 
+            w = self.storage.writeable
             self.storage.writeable = False
             self.comment_root = None
             self.storage.writeable = w
 
     def remove(self):
         self.storage.recursive_remove(self.id.storage())
-    
+
     # methods for managing comments
 
     def uuids(self):
@@ -770,7 +775,7 @@ def cmp_attr(bug_1, bug_2, attr, invert=False):
     val_2 = getattr(bug_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 :
@@ -816,7 +821,7 @@ class BugCompoundComparator (object):
             if val != 0 :
                 return val
         return 0
-        
+
 cmp_full = BugCompoundComparator()
 
 
index defa25032e48c074711b6d43f9d25f72fb7f0113..9d90a70702dc8df7834a16650956ff1efb1a6ede 100644 (file)
@@ -190,7 +190,11 @@ class BugDir (list, settings_object.SavedSettingsObject):
         if settings_mapfile == None:
             settings_mapfile = \
                 self.storage.get(self.id.storage('settings'), 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 bugdir %s\n'
+                            '(BE version missmatch?)' % self.id.user())
         self._setup_saved_settings()
         #self._setup_user_id(self.user_id)
         self._setup_severities(self.severities)
@@ -291,52 +295,66 @@ class BugDir (list, settings_object.SavedSettingsObject):
         Duplicate bugdirs are read-only copies used for generating
         diffs between revisions.
         """
-        dbd = copy.copy(self)
-        dbd.storage = copy.copy(self.storage)
-        dbd._bug_map = copy.copy(self._bug_map)
-        dbd.storage.writeable = False
-        added,changed,removed = self.storage.changed_since(revision)
-        for id in added:
-            pass
-        for id in removed:
-            pass
-        for id in changed:
-            parsed = libbe.util.id.parse_id(id)
-            if parsed['type'] == 'bugdir':
-                assert parsed['remaining'] == ['settings'], parsed['remaining']
-                dbd._settings = copy.copy(self._settings)
-                mf = self.storage.get(self.id.storage('settings'), default='\n',
-                                      revision=revision)
-                dbd.load_settings(mf)
-            else:
-                if parsed['bug'] not in self:
-                    self._load_bug(parsed['bug'])
-                    dbd._load_bug(parsed['bug'])
-                else:
-                    bug = copy.copy(self._bug_map[parsed['bug']])
-                    bug.settings = copy.copy(bug.settings)
-                    dbd._bug_map[parsed['bug']] = bug
-                if parsed['type'] == 'bug':
-                    assert parsed['remaining'] == ['values'], parsed['remaining']
-                    mf = self.storage.get(self.id.storage('values'), default='\n',
-                                          revision=revision)
-                    bug.load_settings(mf)
-                elif parsed['type'] == 'comment':
-                    assert parsed['remaining'] in [['values'], ['body']], \
-                        parsed['remaining']
-                    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.storage('values'), default='\n',
-                                              revision=revision)
-                        comment.load_settings(mf)
-                    else:
-                        body = self.storage.get(self.id.storage('body'), default='\n',
-                                                revision=revision)
-                        comment.body = body                        
-                else:
-                    assert 1==0, 'Unkown type "%s" for id "%s"' % (type, id)
-        dbd.storage.readable = False # so we won't read in added bugs, etc.
+        s = copy.deepcopy(self.storage)
+        s.writeable = False
+        class RevisionedStorageGet (object):
+            def __init__(self, storage, default_revision):
+                self.s = storage
+                self.sget = self.s.get
+                self.r = default_revision
+            def get(self, *args, **kwargs):
+                if not 'revision' in kwargs or kwargs['revision'] == None:
+                    kwargs['revision'] = self.r
+                return self.sget(*args, **kwargs)
+        rsg = RevisionedStorageGet(s, revision)
+        s.get = rsg.get
+        dbd = BugDir(s, from_storage=True)
+#        dbd = copy.copy(self)
+#        dbd.storage = copy.copy(self.storage)
+#        dbd._bug_map = copy.copy(self._bug_map)
+#        dbd.storage.writeable = False
+#        added,changed,removed = self.storage.changed_since(revision)
+#        for id in added:
+#            pass
+#        for id in removed:
+#            pass
+#        for id in changed:
+#            parsed = libbe.util.id.parse_id(id)
+#            if parsed['type'] == 'bugdir':
+#                assert parsed['remaining'] == ['settings'], parsed['remaining']
+#                dbd._settings = copy.copy(self._settings)
+#                mf = self.storage.get(self.id.storage('settings'), default='\n',
+#                                      revision=revision)
+#                dbd.load_settings(mf)
+#            else:
+#                if parsed['bug'] not in self:
+#                    self._load_bug(parsed['bug'])
+#                    dbd._load_bug(parsed['bug'])
+#                else:
+#                    bug = copy.copy(self._bug_map[parsed['bug']])
+#                    bug.settings = copy.copy(bug.settings)
+#                    dbd._bug_map[parsed['bug']] = bug
+#                if parsed['type'] == 'bug':
+#                    assert parsed['remaining'] == ['values'], parsed['remaining']
+#                    mf = self.storage.get(self.id.storage('values'), default='\n',
+#                                          revision=revision)
+#                    bug.load_settings(mf)
+#                elif parsed['type'] == 'comment':
+#                    assert parsed['remaining'] in [['values'], ['body']], \
+#                        parsed['remaining']
+#                    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.storage('values'), default='\n',
+#                                              revision=revision)
+#                        comment.load_settings(mf)
+#                    else:
+#                        body = self.storage.get(self.id.storage('body'), default='\n',
+#                                                revision=revision)
+#                        comment.body = body
+#                else:
+#                    assert 1==0, 'Unkown type "%s" for id "%s"' % (type, id)
+#        dbd.storage.readable = False # so we won't read in added bugs, etc.
         return dbd
 
 if libbe.TESTING == True:
@@ -350,13 +368,16 @@ if libbe.TESTING == True:
         ['a', 'b']
         >>> bugdir.cleanup()
         """
-        def __init__(self, memory=True):
+        def __init__(self, memory=True, versioned=False):
             if memory == True:
                 storage = None
             else:
                 dir = utility.Dir()
                 self._dir_ref = dir # postpone cleanup since dir.cleanup() removes dir.
-                storage = libbe.storage.base.Storage(dir.path)
+                if versioned == False:
+                    storage = libbe.storage.base.Storage(dir.path)
+                else:
+                    storage = libbe.storage.base.VersionedStorage(dir.path)
                 storage.init()
                 storage.connect()
             BugDir.__init__(self, storage=storage, uuid='abc123')
@@ -384,7 +405,7 @@ if libbe.TESTING == True:
                 self.storage.disconnect()
                 self.storage.connect()
                 self._clear_bugs()
-                
+
 #    class BugDirTestCase(unittest.TestCase):
 #        def setUp(self):
 #            self.dir = utility.Dir()
@@ -486,7 +507,7 @@ if libbe.TESTING == True:
 #                                "Invalid comment: %d\n%s" % (index, comment))
 #        def testSyncedComments(self):
 #            self.testComments(sync_with_disk=True)
-    
+
     class SimpleBugDirTestCase (unittest.TestCase):
         def setUp(self):
             # create a pre-existing bugdir in a temporary directory
@@ -542,7 +563,7 @@ if libbe.TESTING == True:
             uuids = sorted([bug.uuid for bug in bugdir])
             self.failUnless(uuids == [], uuids)
             bugdir.cleanup()
-    
+
     unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
     suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])
 
index 9f50632916b9bff7a6520469b81aa6df98e8ba14..6a4941359f46083dbab5fe9023106a722a4c0b12 100644 (file)
@@ -263,7 +263,7 @@ class Command (object):
         self.stdout = codecs.getwriter(output_encoding)(sys.stdout)
         self.stdout.encoding = output_encoding
 
-    def help(self, *args):       
+    def help(self, *args):
         return '\n\n'.join([self._usage(),
                             self._option_help(),
                             self._long_help().rstrip('\n')])
@@ -345,7 +345,7 @@ class Command (object):
     def _get_storage(self):
         """
         Callback for use by commands that need it.
-        
+
         Note that with the current implementation,
         _get_unconnected_storage() will not work after this method
         runs, but that shouldn't be an issue for any command I can
index 4ef619c0bdb34393acf265339d50ccde9ad71b03..7d82e7dc4042b01085893d9161604ea0588bc941 100644 (file)
@@ -27,30 +27,19 @@ import libbe.ui.util.editor
 class Commit (libbe.command.Command):
     """Commit the currently pending changes to the repository
 
-    >>> import os, sys
-    >>> import libbe.storage.vcs
-    >>> import libbe.storage.vcs.base
-    >>> import libbe.util.utility
+    >>> import sys
+    >>> import libbe.bugdir
+    >>> bd = libbe.bugdir.SimpleBugDir(memory=False, versioned=True)
     >>> cmd = Commit()
+    >>> cmd._storage = bd.storage
     >>> cmd._setup_io = lambda i_enc,o_enc : None
     >>> cmd.stdout = sys.stdout
 
-    >>> dir = libbe.util.utility.Dir()
-    >>> vcs = libbe.storage.vcs.installed_vcs()
-    >>> vcs.repo = dir.path
-    >>> vcs.init()
-    >>> vcs.connect()
-    >>> cmd._storage = vcs
-    >>> if vcs.name in libbe.storage.vcs.base.VCS_ORDER:
-    ...     bd = libbe.bugdir.BugDir(vcs, from_storage=False)
-    ...     bd.extra_strings = ['hi there']
-    ...     cmd.run({'user-id':'Joe'}, ['Making a commit']) # doctest: +ELLIPSIS
-    ... else:
-    ...     print 'Committed ...'
+    >>> bd.extra_strings = ['hi there']
+    >>> bd.flush_reload()
+    >>> cmd.run({'user-id':'Joe'}, ['Making a commit']) # doctest: +ELLIPSIS
     Committed ...
-    >>> vcs.disconnect()
-    >>> vcs.destroy()
-    >>> dir.cleanup()
+    >>> bd.cleanup()
     """
     name = 'commit'
 
index c8b5777cde9bc805e75e3347345ccec50075dc78..d945f9687140a89e33465c63c63d00c5f49489be 100644 (file)
@@ -29,7 +29,7 @@ class Diff (libbe.command.Command):
 
     >>> import sys
     >>> import libbe.bugdir
-    >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
+    >>> bd = libbe.bugdir.SimpleBugDir(memory=False, versioned=True)
     >>> cmd = Diff()
     >>> cmd._storage = bd.storage
     >>> cmd._setup_io = lambda i_enc,o_enc : None
@@ -39,23 +39,15 @@ class Diff (libbe.command.Command):
     >>> bug = bd.bug_from_uuid('a')
     >>> bug.status = 'closed'
     >>> changed = bd.storage.commit('Closed bug a')
-    >>> if bd.storage.versioned == True:
-    ...     ret = cmd.run(args=[original])
-    ... else:
-    ...     print 'Modified bugs:\\n  a:cm: Bug A\\n    Changed bug settings:\\n      status: open -> closed'
+    >>> ret = cmd.run(args=[original])
     Modified bugs:
-      a:cm: Bug A
+      abc/a:cm: Bug A
         Changed bug settings:
           status: open -> closed
-    >>> if bd.storage.versioned == True:
-    ...     ret = cmd.run({'subscribe':'%(bugdir_id)s:mod', 'uuids':True}, [original])
-    ... else:
-    ...     print 'a'
+    >>> ret = cmd.run({'subscribe':'%(bugdir_id)s:mod', 'uuids':True}, [original])
     a
-    >>> if bd.storage.versioned == False:
-    ...     ret = cmd.run(args=[original])
-    ... else:
-    ...     raise libbe.command.UserError('This repository not revision-controlled.')
+    >>> bd.storage.versioned = False
+    >>> ret = cmd.run(args=[original])
     Traceback (most recent call last):
       ...
     UserError: This repository is not revision-controlled.
@@ -101,7 +93,7 @@ class Diff (libbe.command.Command):
         if params['repo'] == None:
             if params['revision'] == None: # get the most recent revision
                 params['revision'] = bugdir.storage.revision_id(-1)
-            old_bd = bugdir.duplicate_bugdir(params['revision']) # TODO
+            old_bd = bugdir.duplicate_bugdir(params['revision'])
         else:
             old_storage = libbe.storage.get_storage(params['repo'])
             old_storage.connect()
@@ -113,8 +105,8 @@ class Diff (libbe.command.Command):
                     raise libbe.command.UserError(
                         '%s is not revision-controlled.'
                         % storage.repo)
-                old_bd = old_bd_current.duplicate_bugdir(revision) # TODO
-        d = libbe.diff.Diff(old_bd, bugir)
+                old_bd = old_bd_current.duplicate_bugdir(revision)
+        d = libbe.diff.Diff(old_bd, bugdir)
         tree = d.report_tree(subscriptions)
 
         if params['uuids'] == True:
index 0f993ae60bdfb23f1bcfa3a4ceeff5ef516a7d0c..ec818c020c58d2d1747fb33f392a7aaa83b0ab84 100644 (file)
@@ -60,7 +60,7 @@ class HTML (libbe.command.Command):
     >>> bd.cleanup()
     """
     name = 'html'
-    
+
     def __init__(self, *args, **kwargs):
         libbe.command.Command.__init__(self, *args, **kwargs)
         self.options.extend([
index e73d90f8ebc73c9e533e675c23774697fef22415..2e96848bd2b81b3a08e10939d066fd7ae0b832f9 100644 (file)
@@ -110,9 +110,9 @@ class Import_XML (libbe.command.Command):
                 new.explicit_attrs = []
         else:
             croot_bug,croot_comment = (None, None)
-    
+
         if params['xml-file'] == '-':
-            xml = self.stdin.read().encode(self.stdin.encoding)                
+            xml = self.stdin.read().encode(self.stdin.encoding)
         else:
             self._check_restricted_access(storage, params['xml-file'])
             xml = libbe.util.encoding.get_file_contents(
@@ -147,7 +147,7 @@ class Import_XML (libbe.command.Command):
             else:
                 print >> sys.stderr, 'ignoring unknown tag %s in %s' \
                     % (child.tag, comment_list.tag)
-    
+
         # merge the new root_comments
         if params['add-only'] == True:
             accept_changes = False
@@ -172,7 +172,7 @@ class Import_XML (libbe.command.Command):
             croot_bug.merge(new_croot_bug, accept_changes=accept_changes,
                             accept_extra_strings=accept_extra_strings,
                             accept_comments=accept_comments)
-    
+
         # merge the new croot_bugs
         merged_bugs = []
         old_bugs = []
@@ -190,7 +190,7 @@ class Import_XML (libbe.command.Command):
                           accept_comments=accept_comments)
                 merged_bugs.append(new)
                 old_bugs.append(old)
-    
+
         # protect against programmer error causing data loss:
         if croot_bug != None:
             comms = [c.uuid for c in croot_comment.traverse()]
@@ -201,7 +201,7 @@ class Import_XML (libbe.command.Command):
             if not new in merged_bugs:
                 assert bugdir.has_bug(new.uuid), \
                     "bug %s wasn't added" % (new.uuid)
-    
+
         # save new information
         bugdir.storage.writeable = writeable
         if croot_bug != None:
index 8bdeaaefc6294ac7cc8079172423e47c4fbaadfa..d48c7ee43aa3c1f119ffd478c57c82e991b3c634 100644 (file)
@@ -25,7 +25,7 @@ import libbe.bug
 import libbe.command
 import libbe.command.util
 
-# get a list of * for cmp_*() comparing two bugs. 
+# get a list of * for cmp_*() comparing two bugs.
 AVAILABLE_CMPS = [fn[4:] for fn in dir(libbe.bug) if fn[:4] == 'cmp_']
 AVAILABLE_CMPS.remove('attr') # a cmp_* template.
 
@@ -129,7 +129,7 @@ class List (libbe.command.Command):
 #        parser.add_option(short, long, action="store_true",
 #                          dest=attr, help=help, default=False)
 #    return parser
-#                
+#
 #                ])
 
     def _run(self, **params):
@@ -144,7 +144,7 @@ class List (libbe.command.Command):
         self.result = bugs
         if len(bugs) == 0 and params['xml'] == False:
             print >> self.stdout, "No matching bugs found"
-    
+
         # sort bugs
         bugs = self._sort_bugs(bugs, cmp_list)
 
index e3bf943e12bf824fc9982774bfbb936de270261c..447b4aeb06597c05ba58a86b53b0b20f93b1f9c9 100644 (file)
@@ -63,8 +63,8 @@ class Merge (libbe.command.Command):
       Short name : abc/a
         Severity : minor
           Status : open
-        Assigned : 
-        Reporter : 
+        Assigned :
+        Reporter :
          Creator : John Doe <jdoe@example.com>
          Created : ...
     Bug A
@@ -109,8 +109,8 @@ class Merge (libbe.command.Command):
       Short name : abc/b
         Severity : minor
           Status : closed
-        Assigned : 
-        Reporter : 
+        Assigned :
+        Reporter :
          Creator : Jane Doe <jdoe@example.com>
          Created : ...
     Bug B
@@ -168,7 +168,7 @@ class Merge (libbe.command.Command):
                 comment.storage = None
                 comment.alt_id = comment.uuid
                 comment.storage = bugdir.storage
-            comment.uuid = libbe.util.id.uuid_gen() 
+            comment.uuid = libbe.util.id.uuid_gen()
             comment.save() # force onto disk under bugA
 
         for comment in newCommTree: # just the child comments
index aaf2b5845dae642ad9abc0ba1f36857fe0666a28..4fe0117f22a9b6addb33ff68c03ae08bb5dcc2cb 100644 (file)
@@ -87,11 +87,11 @@ class Set (libbe.command.Command):
 
     def _long_help(self):
         return """
-Show or change per-tree settings. 
+Show or change per-tree settings.
 
 If name and value are supplied, the name is set to a new value.
 If no value is specified, the current value is printed.
-If no arguments are provided, all names and values are listed. 
+If no arguments are provided, all names and values are listed.
 
 To unset a setting, set it to "none".
 
index 92891388fb05d336f4c6216b1dbd31cd5ba3d37c..7c1d3052353570567199b479d90a8ce42190e3f0 100644 (file)
@@ -59,7 +59,7 @@ class Severity (libbe.command.Command):
                     repeatable=True,
                     completion_callback=libbe.command.util.complete_bug_id),
                 ])
-    
+
     def _run(self, **params):
         bugdir = self._get_bugdir()
         for bug_id in params['bug-id']:
index 1a569a6102ea3e6478e6b4db71bf7a0d2bd7b30a..1b498aaf3608d474647d9f8231c5bfa7fea6567b 100644 (file)
@@ -45,8 +45,8 @@ class Show (libbe.command.Command):
       Short name : abc/a
         Severity : minor
           Status : open
-        Assigned : 
-        Reporter : 
+        Assigned :
+        Reporter :
          Creator : John Doe <jdoe@example.com>
          Created : ...
     Bug A
index 7cf58588edcac9675783addd6656b33db48c8b65..323963af513fa1f6367c9f5a05a5f337240c4f6b 100644 (file)
@@ -56,7 +56,7 @@ class Status (libbe.command.Command):
                     repeatable=True,
                     completion_callback=libbe.command.util.complete_bug_id),
                 ])
-    
+
     def _run(self, **params):
         bugdir = self._get_bugdir()
         for bug_id in params['bug-id']:
index 4f72624c856cc22215c30f28bda047c685693851..a837f99b46afd5d825115ab2654f531f6c710378 100644 (file)
@@ -131,7 +131,7 @@ class Subscribe (libbe.command.Command):
                 params['types'] = 'all'
         servers = params['servers'].split(',')
         types = params['types'].split(',')
-    
+
         if len(params['id']) == 0:
             params['id'] = [libbe.diff.BUGDIR_ID]
         for _id in params['id']:
@@ -159,7 +159,7 @@ class Subscribe (libbe.command.Command):
                 else: # add the tag
                     estrs = subscribe(estrs, subscriber, types, servers, type_root)
                 entity.extra_strings = estrs # reassign to notice change
-        
+
             if params['list-all'] == True:
                 bugdir.load_all_bugs()
                 subscriptions = get_bugdir_subscribers(bugdir, servers[0])
@@ -168,7 +168,7 @@ class Subscribe (libbe.command.Command):
                 for estr in entity.extra_strings:
                     if estr.startswith(TAG):
                         subscriptions.append(estr[len(TAG):])
-    
+
             if len(subscriptions) > 0:
                 print >> self.stdout, 'Subscriptions for %s:' % entity_name
                 print >> self.stdout, '\n'.join(subscriptions)
index 26ff1b5474177e0182f917ea402726eeb7ea095e..bdb3f312b2a68ca5748bf22bb48262fe224279e9 100644 (file)
@@ -134,7 +134,7 @@ class Tag (libbe.command.Command):
         for estr in bug.extra_strings:
             if estr.startswith(TAG_TAG):
                 tags.append(estr[len(TAG_TAG):])
-    
+
         if len(tags) > 0:
             print "Tags for %s:" % bug.id.user()
             print '\n'.join(tags)
index 034c532b37ab56c32c46cec80fddb06128702aa7..df836db24e3376c529a4fd08a97db2082bd274b1 100644 (file)
@@ -58,7 +58,7 @@ class Target (libbe.command.Command):
     >>> bd.cleanup()
     """
     name = 'target'
-    
+
     def __init__(self, *args, **kwargs):
         libbe.command.Command.__init__(self, *args, **kwargs)
         self.options.extend([
index a4aaf5f18204993a6e5c003214f46841bfaf0158..3bd02d0d7beff937d0cc729d457ac6b8293a3aec 100644 (file)
@@ -32,7 +32,7 @@ def complete_path(command, argument, fragment=None):
     if len(comps) == 1 and os.path.isdir(comps[0]):
         comps.extend(glob.glob(comps[0]+'/*'))
     return comps
-    
+
 def complete_status(command, argument, fragment=None):
     return [fragment]
 def complete_severity(command, argument, fragment=None):
index 3b8a9c7fed1c895e5badd21fa479d0650e1e4f4b..d899aa8a57b622177966aaf435435ea807a0072c 100644 (file)
@@ -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
         <BLANKLINE>
           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
         <BLANKLINE>
         Insightful remarks
           --------- Comment ---------
           Name: //b
-          From: 
+          From:
           Date: Thu, 20 Nov 2008 02:00:00 +0000
         <BLANKLINE>
           Critique original comment
           --------- Comment ---------
           Name: //c
-          From: 
+          From:
           Date: Thu, 20 Nov 2008 03:00:00 +0000
         <BLANKLINE>
           Begin flamewar :p
         --------- Comment ---------
         Name: //d
-        From: 
+        From:
         Date: Thu, 20 Nov 2008 04:00:00 +0000
         <BLANKLINE>
         Useful examples
         >>> print a.string_thread()
         --------- Comment ---------
         Name: //a
-        From: 
+        From:
         Date: Thu, 20 Nov 2008 01:00:00 +0000
         <BLANKLINE>
         Insightful remarks
           --------- Comment ---------
           Name: //b
-          From: 
+          From:
           Date: Thu, 20 Nov 2008 02:00:00 +0000
         <BLANKLINE>
           Critique original comment
           --------- Comment ---------
           Name: //c
-          From: 
+          From:
           Date: Thu, 20 Nov 2008 03:00:00 +0000
         <BLANKLINE>
           Begin flamewar :p
         --------- Comment ---------
         Name: //d
-        From: 
+        From:
         Date: Thu, 20 Nov 2008 04:00:00 +0000
         <BLANKLINE>
         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:
index 32e8836949ef186d9c60299665f6fb9366c62b55..f82dbfa76e816c360be5948a11dac2a48664b01a 100644 (file)
@@ -90,9 +90,9 @@ class Subscription (object):
     def __init__(self, id, subscription_type, **kwargs):
         if 'type_root' not in kwargs:
             if id == BUGDIR_ID:
-                kwargs['type_root'] = BUGDIR_TYPE_ALL 
+                kwargs['type_root'] = BUGDIR_TYPE_ALL
             else:
-                kwargs['type_root'] = BUG_TYPE_ALL 
+                kwargs['type_root'] = BUG_TYPE_ALL
         if type(subscription_type) in types.StringTypes:
             subscription_type = type_from_name(subscription_type, **kwargs)
         self.id = id
index dd35586e5bc6a2546bbb2f6a691ea2c2fbfe0413..97c8b29c41c277dce307a0cc572dbf61fa1dd67f 100644 (file)
@@ -256,8 +256,10 @@ class Storage (object):
         else:
             decode = False
         value = self._get(*args, **kwargs)
-        if decode == True:
+        if decode == True and type(value) != types.UnicodeType:
             return unicode(value, self.encoding)
+        if decode == False and type(value) != types.StringType:
+            return value.encode(self.encoding)
         return value
 
     def _get(self, id, default=InvalidObject, revision=None):
@@ -673,7 +675,7 @@ if TESTING == True:
             self.failUnless(s == val,
                     "%s.get() returned %s not %s"
                     % (vars(self.Class)['name'], s, self.val))
-            
+
 
     class Storage_persistence_TestCase (StorageTestCase):
         """Test cases for Storage.disconnect and .connect methods."""
@@ -767,7 +769,7 @@ if TESTING == True:
                 revs.append(self.s.commit('%s: %d' % (self.commit_msg, i),
                                           self.commit_body))
             for i in range(10):
-                rev = self.s.revision_id(i+1) 
+                rev = self.s.revision_id(i+1)
                 self.failUnless(rev == revs[i],
                                 "%s.revision_id(%d) returned %s not %s"
                                 % (vars(self.Class)['name'], i+1, rev, revs[i]))
@@ -794,7 +796,7 @@ if TESTING == True:
                 self.failUnless(ret == val(i),
                                 "%s.get() returned %s not %s for revision %s"
                                 % (vars(self.Class)['name'], ret, val(i), revs[i]))
-        
+
     def make_storage_testcase_subclasses(storage_class, namespace):
         """Make StorageTestCase subclasses for storage_class in namespace."""
         storage_testcase_classes = [
index a0a252e6b96c41f0ab96a2e3fb8b6cfa94ddd79c..9f95d1424b20c12a73f6f599f8dae671e4233405 100644 (file)
@@ -46,7 +46,7 @@ def set_val(name, value, section="DEFAULT", encoding=None):
     if encoding == None:
         encoding = default_encoding
     config = ConfigParser.ConfigParser()
-    if os.path.exists(path()) == False: # touch file or config 
+    if os.path.exists(path()) == False: # touch file or config
         open(path(), 'w').close()       # read chokes on missing file
     f = codecs.open(path(), 'r', encoding)
     config.readfp(f, path())
index a8d55163e7bddd18a8fd274402413d6bb1a36f1e..35ae1a0635d87b41b6506842533f64c8982d8a22 100644 (file)
@@ -24,6 +24,7 @@ independent/conflicting changes.
 
 import errno
 import os.path
+import types
 import yaml
 
 import libbe
@@ -39,32 +40,37 @@ class IllegalKey(Exception):
 class IllegalValue(Exception):
     def __init__(self, value):
         Exception.__init__(self, 'Illegal value "%s"' % value)
-        self.value = value 
+        self.value = value
+
+class InvalidMapfileContents(Exception):
+    def __init__(self, contents):
+        Exception.__init__(self, 'Invalid YAML contents')
+        self.contents = contents
 
 def generate(map):
     """Generate a YAML mapfile content string.
-    >>> generate({"q":"p"})
+    >>> generate({'q':'p'})
     'q: p\\n\\n'
-    >>> generate({"q":u"Fran\u00e7ais"})
+    >>> generate({'q':u'Fran\u00e7ais'})
     'q: Fran\\xc3\\xa7ais\\n\\n'
-    >>> generate({"q":u"hello"})
+    >>> generate({'q':u'hello'})
     'q: hello\\n\\n'
-    >>> generate({"q=":"p"})
+    >>> generate({'q=':'p'})
     Traceback (most recent call last):
     IllegalKey: Illegal key "q="
-    >>> generate({"q:":"p"})
+    >>> generate({'q:':'p'})
     Traceback (most recent call last):
     IllegalKey: Illegal key "q:"
-    >>> generate({"q\\n":"p"})
+    >>> generate({'q\\n':'p'})
     Traceback (most recent call last):
     IllegalKey: Illegal key "q\\n"
-    >>> generate({"":"p"})
+    >>> generate({'':'p'})
     Traceback (most recent call last):
     IllegalKey: Illegal key ""
-    >>> generate({">q":"p"})
+    >>> generate({'>q':'p'})
     Traceback (most recent call last):
     IllegalKey: Illegal key ">q"
-    >>> generate({"q":"p\\n"})
+    >>> generate({'q':'p\\n'})
     Traceback (most recent call last):
     IllegalValue: Illegal value "p\\n"
     """
@@ -97,20 +103,28 @@ def parse(contents):
     'p'
     >>> parse('q: \\'p\\'\\n\\n')['q']
     'p'
-    >>> contents = generate({"a":"b", "c":"d", "e":"f"})
+    >>> contents = generate({'a':'b', 'c':'d', 'e':'f'})
     >>> dict = parse(contents)
-    >>> dict["a"]
+    >>> dict['a']
     'b'
-    >>> dict["c"]
+    >>> dict['c']
     'd'
-    >>> dict["e"]
+    >>> dict['e']
     'f'
-    >>> contents = generate({"q":u"Fran\u00e7ais"})
+    >>> contents = generate({'q':u'Fran\u00e7ais'})
     >>> dict = parse(contents)
-    >>> dict["q"]
+    >>> dict['q']
     u'Fran\\xe7ais'
+    >>> dict = parse('a!')
+    Traceback (most recent call last):
+      ...
+    InvalidMapfileContents: Invalid YAML contents
     """
-    return yaml.load(contents) or {}
+    c = yaml.load(contents)
+    if type(c) == types.StringType:
+        raise InvalidMapfileContents(
+            'Unable to parse YAML (BE format missmatch?):\n\n%s' % contents)
+    return c or {}
 
 if libbe.TESTING == True:
     suite = doctest.DocTestSuite()
index 760df03ee93cce9eed66239bb0cf3e81b36a3579..8b868290279be07393e9b7d8a6a8e1ada57b147e 100644 (file)
@@ -197,9 +197,8 @@ class SavedSettingsObject(object):
         settings as primed.
         """
         for property in self.settings_properties:
-            if property not in self.settings:
-                self.settings[property] = EMPTY
-            elif self.settings[property] == UNPRIMED:
+            if property not in self.settings \
+                    or self.settings[property] == UNPRIMED:
                 self.settings[property] = EMPTY
         if flag_as_loaded == True:
             self._settings_loaded = True
index 7ef760eea58e533b70b2fe4d6e2e6a0259f62ddd..c94f171c570ede45c1edc7c52d75f74fd5d985ff 100644 (file)
@@ -24,17 +24,16 @@ import sys
 
 import libbe
 import libbe.bug as bug
-import libbe.util.encoding as encoding
 import libbe.storage.util.mapfile as mapfile
+import libbe.util.encoding as encoding
+import libbe.util.id
 
-if libbe.TESTING == True:
-    import doctest
 
 # a list of all past versions
-BUGDIR_DISK_VERSIONS = ["Bugs Everywhere Tree 1 0",
-                        "Bugs Everywhere Directory v1.1",
-                        "Bugs Everywhere Directory v1.2",
-                        "Bugs Everywhere Directory v1.3"]
+BUGDIR_DISK_VERSIONS = ['Bugs Everywhere Tree 1 0',
+                        'Bugs Everywhere Directory v1.1',
+                        'Bugs Everywhere Directory v1.2',
+                        'Bugs Everywhere Directory v1.3']
 
 # the current version
 BUGDIR_DISK_VERSION = BUGDIR_DISK_VERSIONS[-1]
@@ -43,13 +42,17 @@ class Upgrader (object):
     "Class for converting between different on-disk BE storage formats."
     initial_version = None
     final_version = None
-    def __init__(self, root):
-        self.root = root
+    def __init__(self, repo):
+        self.repo = repo
 
-    def get_path(self, *args):
+    def get_path(self, id):
         """
-        Return a path relative to .root.
+        Return a path relative to .repo.
         """
+        if id == 'version':
+            return os.path.join(self.repo, id)
+
+TODO
         dir = os.path.join(self.root, '.be')
         if len(args) == 0:
             return dir
@@ -58,15 +61,15 @@ class Upgrader (object):
 
     def check_initial_version(self):
         path = self.get_path('version')
-        version = self.vcs.get_file_contents(path).rstrip('\n')
+        version = encoding.get_file_contents(path).rstrip('\n')
         assert version == self.initial_version, version
 
     def set_version(self):
-        path = self.get_path("version")
-        self.vcs.set_file_contents(path, self.final_version+"\n")
+        path = self.get_path('version')
+        encoding.set_file_contents(path, self.final_version+'\n')
 
     def upgrade(self):
-        print >> sys.stderr, "upgrading bugdir from '%s' to '%s'" \
+        print >> sys.stderr, 'upgrading bugdir from "%s" to "%s"' \
             % (self.initial_version, self.final_version)
         self.check_initial_version()
         self.set_version()
@@ -237,6 +240,3 @@ def upgrade(path, current_version,
             if version_b == target_version:
                 break
             i += 1
-
-if libbe.TESTING == True:
-    suite = doctest.DocTestSuite()
index 8afdca972d2124f0bf3a2a3674c538b52bfe2415..f1b5b7b3033a6c113a786c2441c376c33319abb3 100644 (file)
@@ -167,7 +167,7 @@ class Arch(base.VCS):
           http://regexps.srparish.net/tutorial-tla/naming-conventions.html
         Since our bug directory '.be' doesn't satisfy these conventions,
         we need to adjust them.
-        
+
         The conventions are specified in
           project-root/{arch}/=tagging-method
         """
@@ -211,7 +211,7 @@ class Arch(base.VCS):
             dirname = path
         status,output,error = self._u_invoke_client('tree-root', dirname)
         root = output.rstrip('\n')
-        
+
         self._get_archive_project_name(root)
 
         return root
index 768a85ffb47b52bd46a2b066445aa6190746f03e..3bdb4acc1322f78182b5c5c53ceefe7552da5dc6 100644 (file)
@@ -195,6 +195,9 @@ class CachedPathID (object):
                 id = self.id(dirpath)
                 relpath = dirpath[len(self._root)+1:]
                 if id.count('/') == 0:
+                    if id in self._cache:
+                        import sys
+                        print >> sys.stderr, 'Multiple paths for %s: \n  %s\n  %s' % (id, self._cache[id], relpath)
                     self._cache[id] = relpath
             except InvalidPath:
                 pass
@@ -521,7 +524,7 @@ os.listdir(self.get_path("bugs")):
         dumping VCS-specific files into the .be directory.
 
         If you do need to implement this method (e.g. Arch), set
-          self.interspersed_vcs_files = True 
+          self.interspersed_vcs_files = True
         """
         assert self.interspersed_vcs_files == False
         raise NotImplementedError
index 04cc6c1c4d0ee52bdd871767ee55045ebda08ca6..6f3e8409f653670e8fc3fef2da3697f378d3488d 100644 (file)
@@ -49,7 +49,7 @@ class Bzr(base.VCS):
 
     def _vcs_version(self):
         status,output,error = self._u_invoke_client('--version')
-        return output        
+        return output
 
     def _vcs_get_user_id(self):
         status,output,error = self._u_invoke_client('whoami')
@@ -88,7 +88,7 @@ class Bzr(base.VCS):
             return base.VCS._vcs_get_file_contents(self, path, revision)
         else:
             status,output,error = \
-                self._u_invoke_client('cat', '-r', revision,path)
+                self._u_invoke_client('cat', '-r', revision, path)
             return output
 
     def _vcs_commit(self, commitfile, allow_empty=False):
@@ -123,7 +123,7 @@ class Bzr(base.VCS):
             return str(index) # bzr commit 0 is the empty tree.
         return str(current_revision+index+1)
 
-\f    
+\f
 if libbe.TESTING == True:
     base.make_vcs_testcase_subclasses(Bzr, sys.modules[__name__])
 
index 97e31ffbb35b03db9cbbfc85032e0ac095d95abb..9a371d9d6e1a521a4667284c51cfa70e99cac602 100644 (file)
@@ -76,7 +76,7 @@ class Darcs(base.VCS):
     def _vcs_detect(self, path):
         if self._u_search_parent_directories(path, "_darcs") != None :
             return True
-        return False 
+        return False
 
     def _vcs_root(self, path):
         """Find the root of the deepest repository containing path."""
@@ -129,7 +129,7 @@ class Darcs(base.VCS):
                     'diff', '--unified', '--patch', revision, path,
                     unicode_output=False)
                 target_patch = output
-                
+
                 # '--output -' to be supported in GNU patch > 2.5.9
                 # but that hasn't been released as of June 30th, 2009.
 
@@ -206,7 +206,7 @@ class Darcs(base.VCS):
         except IndexError:
             return None
 
-\f    
+\f
 if libbe.TESTING == True:
     base.make_vcs_testcase_subclasses(Darcs, sys.modules[__name__])
 
index 29abda78c913ed67fa11ef11b71fe4c51bc98128..8d1b529252181af55f1d30b8809dcae9013a1373 100644 (file)
@@ -76,7 +76,7 @@ class Git(base.VCS):
     def _vcs_detect(self, path):
         if self._u_search_parent_directories(path, '.git') != None :
             return True
-        return False 
+        return False
 
     def _vcs_root(self, path):
         """Find the root of the deepest repository containing path."""
@@ -154,7 +154,7 @@ class Git(base.VCS):
         except IndexError:
             return None
 
-\f    
+\f
 if libbe.TESTING == True:
     base.make_vcs_testcase_subclasses(Git, sys.modules[__name__])
 
index 7e0643b574e280f8d71bb79c2df7f7d830081bd9..d2d328123d58ef549216101077a6e9feb36f3a7b 100644 (file)
@@ -124,7 +124,7 @@ class Hg(base.VCS):
             return id
         return None
 
-\f    
+\f
 if libbe.TESTING == True:
     base.make_vcs_testcase_subclasses(Hg, sys.modules[__name__])
 
index ce0e55e9170e27a67ee1d469819a638dafaaae46..6eead671b2bea455df6a379ea2ae8271bc3580ce 100755 (executable)
@@ -259,7 +259,7 @@ def main():
     if options['no-pager'] == True:
         paginate = 'never'
     libbe.ui.util.pager.run_pager(paginate)
-    
+
     command_name = args[0]
     try:
         module = libbe.command.get_command(command_name)
index dcc41f8559f9bb82cce1d89645bfafa3c7ef2a60..77061058970876db685a9a63f713d876d6bef93a 100644 (file)
@@ -72,7 +72,7 @@ def get_file_contents(path, mode='r', encoding=None, decode=False):
             encoding = get_filesystem_encoding()
         f = codecs.open(path, mode, encoding)
     else:
-        f = open(path, mode) 
+        f = open(path, mode)
     contents = f.read()
     f.close()
     return contents
index 3838259fafd461bb31a064eb65aba4ff40ccff99..adc827c5f557080b0f6756eb2590fbce0145d350 100644 (file)
@@ -50,7 +50,7 @@ except ImportError:
                 q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
             else:
                 # win32 don't have os.execvp() so have to run command in a shell
-                q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, 
+                q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
                           shell=True, cwd=cwd)
         except OSError, e :
             strerror = "%s\nwhile executing %s" % (e.args[1], args)
@@ -210,7 +210,7 @@ def child_uuids(child_storage_ids):
         fields = _split(id)
         if len(fields) == 1:
             yield fields[0]
-    
+
 
 REGEXP = '#([-a-f0-9]*)(/[-a-g0-9]*)?(/[-a-g0-9]*)?#'
 
@@ -298,7 +298,7 @@ if libbe.TESTING == True:
             self._siblings = siblings
         def sibling_uuids(self):
             return self._siblings
-        
+
     class IDtestCase(unittest.TestCase):
         def setUp(self):
             self.bugdir = DummyObject('1234abcd')
@@ -342,11 +342,11 @@ if libbe.TESTING == True:
             self.bug = DummyObject('abcdef', ['a1234', 'ab9876'])
             self.bug.bugdir = self.bugdir
             self.bugdir.bug_from_uuid = lambda uuid: self.bug
-            self.bugdir.uuids = lambda : self.bug.sibling_uuids() + [self.bug.uuid] 
+            self.bugdir.uuids = lambda : self.bug.sibling_uuids() + [self.bug.uuid]
             self.comment = DummyObject('12345678', ['1234abcd', '1234cdef'])
             self.comment.bug = self.bug
             self.bug.comment_from_uuid = lambda uuid: self.comment
-            self.bug.uuids = lambda : self.comment.sibling_uuids() + [self.comment.uuid] 
+            self.bug.uuids = lambda : self.comment.sibling_uuids() + [self.comment.uuid]
             self.bd_id = ID(self.bugdir, 'bugdir')
             self.b_id = ID(self.bug, 'bug')
             self.c_id = ID(self.comment, 'comment')
index 982c5ca6c767e6dc564f9d2157222b50600e2bd4..0326cda3089357aa11a1fd3f4be5fad64686f61c 100644 (file)
@@ -57,7 +57,7 @@ def modnames(prefix):
     >>> 'plugin' in [n for n in modnames('libbe.util')]
     True
     """
-    components = prefix.split('.')    
+    components = prefix.split('.')
     modfiles = os.listdir(os.path.join(_PLUGIN_PATH, *components))
     modfiles.sort()
     for modfile in modfiles:
index 8806e26497b8999bac640961cf0d1c986dfbab0f..06716b30774b635c6aa766b848aa02458a4f764c 100644 (file)
@@ -61,7 +61,7 @@ def invoke(args, stdin=None, stdout=PIPE, stderr=PIPE, expect=(0,),
         else:
             assert _MSWINDOWS==True, 'invalid platform'
             # win32 don't have os.execvp() so have to run command in a shell
-            q = Popen(args, stdin=PIPE, stdout=stdout, stderr=stderr, 
+            q = Popen(args, stdin=PIPE, stdout=stdout, stderr=stderr,
                       shell=True, cwd=cwd)
     except OSError, e:
         raise CommandError(args, status=e.args[0], stderr=e)
@@ -133,7 +133,7 @@ class Pipe (object):
                 thread.start()
                 threads.append(thread)
                 std_X_arrays.append(stderr_array)
-    
+
             # also listen to the last processes stdout
             stdout_array = []
             thread = Thread(target=proc._readerthread,
@@ -142,11 +142,11 @@ class Pipe (object):
             thread.start()
             threads.append(thread)
             std_X_arrays.append(stdout_array)
-    
+
             # join threads as they die
             for thread in threads:
                 thread.join()
-    
+
             # read output from reader threads
             std_X_strings = []
             for std_X_array in std_X_arrays:
index 779eaa5185a27283cce43818480c2180b8189724..31d4c147ee9484485db489248156a2f2f1ed9cd1 100644 (file)
@@ -51,7 +51,7 @@ def search_parent_directories(path, filename):
     """
     Find the file (or directory) named filename in path or in any
     of path's parents.
-    
+
     e.g.
     search_parent_directories("/a/b/c", ".be")
     will return the path to the first existing file from
@@ -112,7 +112,7 @@ def str_to_time(str_time):
     time_val = calendar.timegm(time.strptime(str_time, RFC_2822_TIME_FMT))
     timesign = -int(timezone_str[0]+"1") # "+" -> time_val ahead of GMT
     timezone_tuple = time.strptime(timezone_str[1:], "%H%M")
-    timezone = timezone_tuple.tm_hour*3600 + timezone_tuple.tm_min*60 
+    timezone = timezone_tuple.tm_hour*3600 + timezone_tuple.tm_min*60
     return time_val + timesign*timezone
 
 def handy_time(time_val):
@@ -153,7 +153,7 @@ def underlined(instring):
     >>> underlined("Underlined String")
     'Underlined String\\n================='
     """
-    
+
     return "%s\n%s" % (instring, "="*len(instring))
 
 if libbe.TESTING == True: