Another major rewrite. Now BugDir, Bug, and Comment are more distinct.
authorW. Trevor King <wking@drexel.edu>
Fri, 21 Nov 2008 19:56:05 +0000 (14:56 -0500)
committerW. Trevor King <wking@drexel.edu>
Fri, 21 Nov 2008 19:56:05 +0000 (14:56 -0500)
I pushed a lot of the little helper functions into the main classes,
which makes it easier for me to keep track of what's going on.  I'm
now at the point where I can run through `python test.py` with each of
the backends (by changing the search order in rcs.py
_get_matching_rcs) without any unexpected errors for each backend
(except Arch).  I can also run `test_usage.sh` without non-Arch errors
either.

However, don't consider this a stable commit yet.  The bzr backend is
*really*slow*, and the other's aren't blazingly fast either.  I think
I'm rewriting the entire database every time I save it :p.  Still, it
passes the checks. and I don't like it when zounds of changes build up.

46 files changed:
.be/bugs/02223264-e28a-4720-9f20-1e7a27a7041d/values
.be/bugs/2103f60c-36e5-4b05-b57c-8c6fee2d80d4/comments/b8bbd433-9017-4c04-a038-2a7370a3adc7/values
.be/bugs/2929814b-2163-45d0-87ba-f7d1ef0a32a9/comments/6d7072de-89b6-4c53-a435-6879c644a0e8/values
.be/bugs/381555eb-f2e3-4ef0-8303-d759c00b390a/comments/9e33512e-e3cb-42ec-bc99-8e77587d0d3f/values
.be/bugs/6eb8141f-b0b1-4d5b-b4e6-d0860d844ada/comments/f2011471-56cb-46e2-813b-1ac336ee7bbc/values
.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values
.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values
.be/bugs/7bfc591e-584a-476e-8e11-b548f1afcaa6/comments/5a6b44f5-9d1d-4e2e-a42c-f5423c43a1dc/values
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/body [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/values [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/body [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/values [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/body [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/values [new file with mode: 0644]
.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/values [new file with mode: 0644]
.be/bugs/9a942b1d-a3b5-441d-8aef-b844700e1efa/comments/37650981-1908-4c39-bae2-48e69c771120/values
AUTHORS
be
becommands/__init__.py
becommands/assign.py
becommands/close.py
becommands/comment.py
becommands/diff.py
becommands/help.py
becommands/list.py
becommands/new.py
becommands/open.py
becommands/remove.py
becommands/set.py
becommands/set_root.py
becommands/severity.py
becommands/show.py
becommands/status.py
becommands/target.py
becommands/upgrade.py [deleted file]
libbe/arch.py
libbe/bug.py
libbe/bugdir.py
libbe/cmdutil.py
libbe/diff.py
libbe/mapfile.py
libbe/names.py [deleted file]
libbe/plugin.py
libbe/rcs.py
libbe/utility.py
test.py

index bb8f7f38bc9b57dc69469985a7a5d4cf00c84a1e..ac2fa4eed3ff0c399590bd924e67dab4724647ea 100644 (file)
@@ -15,7 +15,7 @@ severity=minor
 
 
 
-status=open
+status=fixed
 
 
 
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/body b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/body
new file mode 100644 (file)
index 0000000..d10b444
--- /dev/null
@@ -0,0 +1,38 @@
+File "/home/wking/src/fun/be-bugfix/becommands/status.py", line 25, in becommands.status.execute
+Failed example:
+    bd = bugdir.simple_bug_dir()
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.status.execute[1]>", line 1, in <module>
+        bd = bugdir.simple_bug_dir()
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 293, in simple_bug_dir
+        bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 99, in __init__
+        rcs = self.guess_rcs(allow_rcs_init)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 165, in guess_rcs
+        rcs = installed_rcs()
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 53, in installed_rcs
+        return _get_matching_rcs(lambda rcs: rcs.installed())
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 37, in _get_matching_rcs
+        if matchfn(rcs) == True:
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 53, in <lambda>
+        return _get_matching_rcs(lambda rcs: rcs.installed())
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 180, in installed
+        self._rcs_help()
+      File "/home/wking/src/fun/be-bugfix/libbe/bzr.py", line 32, in _rcs_help
+        status,output,error = self._u_invoke_client("--help")
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 362, in _u_invoke_client
+        return self._u_invoke(cl_args, expect, cwd=directory)
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 355, in _u_invoke
+        raise CommandError(error, status)
+    CommandError: Command failed (1): 'import site' failed; use -v for traceback
+    bzr: ERROR: Couldn't import bzrlib and dependencies.
+    Please check bzrlib is on your PYTHONPATH.
+
+    Traceback (most recent call last):
+      File "/usr/bin/bzr", line 64, in <module>
+        import bzrlib
+    ImportError: No module named bzrlib
+
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/values b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/13e88b64-117b-4f8b-8cba-8f4a9bc394f5/values
new file mode 100644 (file)
index 0000000..f109f3e
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+
+Content-type=text/plain
+
+
+
+
+
+
+Date=Fri, 21 Nov 2008 18:41:47 +0000
+
+
+
+
+
+
+From=W. Trevor King <wking@drexel.edu>
+
+
+
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/body b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/body
new file mode 100644 (file)
index 0000000..3d7d3aa
--- /dev/null
@@ -0,0 +1,2 @@
+Aha, a final os.chdir('/') line is required to clean up after the
+set_root.py doctest.
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/values b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/2ae039de-5b0d-4a4f-aa80-6c81d1345367/values
new file mode 100644 (file)
index 0000000..e0e3783
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+
+Content-type=text/plain
+
+
+
+
+
+
+Date=Fri, 21 Nov 2008 19:12:42 +0000
+
+
+
+
+
+
+From=W. Trevor King <wking@drexel.edu>
+
+
+
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/body b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/body
new file mode 100644 (file)
index 0000000..1fe5ce3
--- /dev/null
@@ -0,0 +1,170 @@
+Hysteretic!  test.py severity passes, then fails.
+
+Problem caused somewhere in set_root?  Doctest?  Bzr?
+
+libbe/plugin.py adds the BE-path to sys.path, but it is done by the
+time the TestRunner fires up...  Wierd.
+
+$ python test.py severity set_root severity
+Doctest: becommands.severity.execute ... ok
+Doctest: becommands.set_root.execute ... FAIL
+Doctest: becommands.severity.execute ... FAIL
+
+======================================================================
+FAIL: Doctest: becommands.set_root.execute
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "/usr/lib/python2.5/doctest.py", line 2128, in runTest
+    raise self.failureException(self.format_failure(new.getvalue()))
+AssertionError: Failed doctest test for becommands.set_root.execute
+  File "/home/wking/src/fun/be-bugfix/becommands/set_root.py", line 22, in execute
+
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/set_root.py", line 41, in becommands.set_root.execute
+Failed example:
+    print rcs.name
+Expected:
+    Arch
+Got:
+    bzr
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/set_root.py", line 43, in becommands.set_root.execute
+Failed example:
+    execute([])
+Expected:
+    Using Arch for revision control.
+    Directory initialized.
+Got:
+    Using bzr for revision control.
+    Directory initialized.
+
+
+======================================================================
+FAIL: Doctest: becommands.severity.execute
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "/usr/lib/python2.5/doctest.py", line 2128, in runTest
+    raise self.failureException(self.format_failure(new.getvalue()))
+AssertionError: Failed doctest test for becommands.severity.execute
+  File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 22, in execute
+
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 25, in becommands.severity.execute
+Failed example:
+    bd = bugdir.simple_bug_dir()
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[1]>", line 1, in <module>
+        bd = bugdir.simple_bug_dir()
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 293, in simple_bug_dir
+        bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 99, in __init__
+        rcs = self.guess_rcs(allow_rcs_init)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 165, in guess_rcs
+        rcs = installed_rcs()
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 53, in installed_rcs
+        return _get_matching_rcs(lambda rcs: rcs.installed())
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 37, in _get_matching_rcs
+        if matchfn(rcs) == True:
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 53, in <lambda>
+        return _get_matching_rcs(lambda rcs: rcs.installed())
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 180, in installed
+        self._rcs_help()
+      File "/home/wking/src/fun/be-bugfix/libbe/bzr.py", line 32, in _rcs_help
+        status,output,error = self._u_invoke_client("--help")
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 362, in _u_invoke_client
+        return self._u_invoke(cl_args, expect, cwd=directory)
+      File "/home/wking/src/fun/be-bugfix/libbe/rcs.py", line 355, in _u_invoke
+        raise CommandError(error, status)
+    CommandError: Command failed (1): 'import site' failed; use -v for traceback
+    bzr: ERROR: Couldn't import bzrlib and dependencies.
+    Please check bzrlib is on your PYTHONPATH.
+
+    Traceback (most recent call last):
+      File "/usr/bin/bzr", line 64, in <module>
+        import bzrlib
+    ImportError: No module named bzrlib
+
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 26, in becommands.severity.execute
+Failed example:
+    os.chdir(bd.root)
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[2]>", line 1, in <module>
+        os.chdir(bd.root)
+    NameError: name 'bd' is not defined
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 27, in becommands.severity.execute
+Failed example:
+    execute(["a"])
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[3]>", line 1, in <module>
+        execute(["a"])
+      File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 40, in execute
+        bd = bugdir.BugDir(loadNow=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 85, in __init__
+        root = os.getcwd()
+    OSError: [Errno 2] No such file or directory
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 29, in becommands.severity.execute
+Failed example:
+    execute(["a", "wishlist"])
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[4]>", line 1, in <module>
+        execute(["a", "wishlist"])
+      File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 40, in execute
+        bd = bugdir.BugDir(loadNow=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 85, in __init__
+        root = os.getcwd()
+    OSError: [Errno 2] No such file or directory
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 30, in becommands.severity.execute
+Failed example:
+    execute(["a"])
+Exception raised:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[5]>", line 1, in <module>
+        execute(["a"])
+      File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 40, in execute
+        bd = bugdir.BugDir(loadNow=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 85, in __init__
+        root = os.getcwd()
+    OSError: [Errno 2] No such file or directory
+----------------------------------------------------------------------
+File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 32, in becommands.severity.execute
+Failed example:
+    execute(["a", "none"])
+Expected:
+    Traceback (most recent call last):
+    UserError: Invalid severity level: none
+Got:
+    Traceback (most recent call last):
+      File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+        compileflags, 1) in test.globs
+      File "<doctest becommands.severity.execute[6]>", line 1, in <module>
+        execute(["a", "none"])
+      File "/home/wking/src/fun/be-bugfix/becommands/severity.py", line 40, in execute
+        bd = bugdir.BugDir(loadNow=True)
+      File "/home/wking/src/fun/be-bugfix/libbe/bugdir.py", line 85, in __init__
+        root = os.getcwd()
+    OSError: [Errno 2] No such file or directory
+
+
+----------------------------------------------------------------------
+Ran 3 tests in 8.719s
+
+FAILED (failures=2)
+
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/values b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/comments/a492508e-0be7-4403-bbd0-9cdc0a46b06b/values
new file mode 100644 (file)
index 0000000..e5498c9
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+
+Content-type=text/plain
+
+
+
+
+
+
+Date=Fri, 21 Nov 2008 19:01:19 +0000
+
+
+
+
+
+
+From=W. Trevor King <wking@drexel.edu>
+
+
+
diff --git a/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/values b/.be/bugs/8e83da06-26f1-4763-a972-dae7e7062233/values
new file mode 100644 (file)
index 0000000..0af1cb1
--- /dev/null
@@ -0,0 +1,35 @@
+
+
+
+creator=W. Trevor King <wking@drexel.edu>
+
+
+
+
+
+
+severity=minor
+
+
+
+
+
+
+status=open
+
+
+
+
+
+
+summary=test.py removes path to bzrlib
+
+
+
+
+
+
+time=Fri, 21 Nov 2008 18:41:03 +0000
+
+
+
diff --git a/AUTHORS b/AUTHORS
index a96c875de012dcae44d994273d4d7ae7d5cc0912..4cab4e534c126b861ccf7045f567fa581ef79331 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,3 +3,4 @@ Aaron Bentley
 Oleg Romanyshyn
 Thomas Gerigk
 Marien Zwart
+W. Trevor King
diff --git a/be b/be
index ea7f65a30495e42d4f14e4f7b3f25eaa90c11e88..1ef7b3a2e47989459d7984d979c5a2d7ff282741 100755 (executable)
--- a/be
+++ b/be
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
-from libbe.cmdutil import *
-from libbe.bugdir import tree_root, create_bug_dir
-from libbe import names, plugin, cmdutil
 import sys
-import os
-import becommands
+from libbe import cmdutil
 
 __doc__ == cmdutil.help()
 
@@ -33,13 +29,13 @@ else:
         try:
             sys.exit(cmdutil.execute(sys.argv[1], sys.argv[2:]))
         except KeyError, e:
-            raise UserError("Unknown command \"%s\"" % e.args[0])
+            raise cmdutil.UserError("Unknown command \"%s\"" % e.args[0])
         except cmdutil.GetHelp:
             print cmdutil.help(sys.argv[1])
             sys.exit(0)
         except cmdutil.UsageError:
             print cmdutil.help(sys.argv[1])
             sys.exit(1)
-    except UserError, e:
+    except cmdutil.UserError, e:
         print e
         sys.exit(1)
index 6b0737845c493b601d7edec4ced24f53b3f61fca..02c977e14881e52d635db1f960fea6c8b6d1d6ca 100644 (file)
@@ -2,7 +2,7 @@
 
 __all__ = ["set_root", "set", "new", "remove", "list", "show", "close", "open",
            "assign", "severity", "status", "target", "comment", "diff",
-           "upgrade", "help"]
+           "help"]
 
 def import_all():
     for i in __all__:
index 3513ab181886b35fca877c2ac8e37226a19f9d84..2cdcf4ce90028703e02cb1699fcb328113a04cea 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Assign an individual or group to fix a bug"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> dir.get_bug("a").assigned is None
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> bd.bug_from_shortname("a").assigned is None
     True
+
     >>> execute(["a"])
-    >>> dir.get_bug("a").assigned == dir.rcs.get_user_id()
+    >>> bd.load()
+    >>> bd.bug_from_shortname("a").assigned == bd.rcs.get_user_id()
     True
+
     >>> execute(["a", "someone"])
-    >>> dir.get_bug("a").assigned
-    u'someone'
+    >>> bd.load()
+    >>> print bd.bug_from_shortname("a").assigned
+    someone
+
     >>> execute(["a","none"])
-    >>> dir.get_bug("a").assigned is None
+    >>> bd.load()
+    >>> bd.bug_from_shortname("a").assigned is None
     True
     """
     options, args = get_parser().parse_args(args)
     assert(len(args) in (0, 1, 2))
     if len(args) == 0:
-        print help()
-        return
-    bug = cmdutil.get_bug(args[0])
+        raise cmdutil.UserError("Please specify a bug id.")
+    if len(args) > 2:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     if len(args) == 1:
         bug.assigned = bug.rcs.get_user_id()
     elif len(args) == 2:
@@ -49,7 +57,7 @@ def execute(args):
             bug.assigned = None
         else:
             bug.assigned = args[1]
-    bug.save()
+    bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be assign bug-id [assignee]")
index 9e6987c5253b3d5c685d27f7c680bb4e4c0cc8d4..38a5613787d626bcc31e9bdf78c34bbf2e579e25 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Close a bug"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
     >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> dir.get_bug("a").status
-    u'open'
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> print bd.bug_from_shortname("a").status
+    open
     >>> execute(["a"])
-    >>> dir.get_bug("a").status
-    u'closed'
+    >>> bd.load()
+    >>> print bd.bug_from_shortname("a").status
+    closed
     """
     options, args = get_parser().parse_args(args)
-    if len(args) !=1:
+    if len(args) == 0:
         raise cmdutil.UserError("Please specify a bug id.")
-    bug = cmdutil.get_bug(args[0])
+    if len(args) > 1:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     bug.status = "closed"
-    bug.save()
+    bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be close bug-id")
index ec932624ced778d0194eb3d796e6eb51bee9044e..045b33143bb047351aa034b8f1765fdc6e9c2f63 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Add a comment to a bug"""
-from libbe import cmdutil, utility
+from libbe import cmdutil, bugdir, utility
 import os
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
-    >>> import os, time
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
+    >>> import time
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
     >>> execute(["a", "This is a comment about a"])
-    >>> comment = dir.get_bug("a").list_comments()[0]
-    >>> comment.body
-    u'This is a comment about a\\n'
-    >>> comment.From == dir.rcs.get_user_id()
+    >>> bd.load()
+    >>> comment = bd.bug_from_shortname("a").comment_root[0]
+    >>> print comment.body
+    This is a comment about a
+    <BLANKLINE>
+    >>> comment.From == bd.rcs.get_user_id()
     True
     >>> comment.time <= int(time.time())
     True
     >>> comment.in_reply_to is None
     True
+
     >>> if 'EDITOR' in os.environ:
     ...     del os.environ["EDITOR"]
     >>> execute(["b"])
     Traceback (most recent call last):
     UserError: No comment supplied, and EDITOR not specified.
+
     >>> os.environ["EDITOR"] = "echo 'I like cheese' > "
     >>> execute(["b"])
-    >>> dir.get_bug("b").list_comments()[0].body
-    u'I like cheese\\n'
+    >>> bd.load()
+    >>> print bd.bug_from_shortname("b").comment_root[0].body
+    I like cheese
+    <BLANKLINE>
     """
     options, args = get_parser().parse_args(args)
-    if len(args) < 1:
-        raise cmdutil.UsageError()
-    bug, parent_comment = cmdutil.get_bug_and_comment(args[0])
+    if len(args) == 0:
+        raise cmdutil.UserError("Please specify a bug or comment id.")
+    if len(args) > 2:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    
+    shortname = args[0]
+    if shortname.count(':') > 1:
+        raise cmdutil.UserError("Invalid id '%s'." % shortname)        
+    elif shortname.count(':') == 1:
+        # Split shortname generated by Comment.comment_shortnames()
+        bugname = shortname.split(':')[0]
+        is_reply = True
+    else:
+        bugname = shortname
+        is_reply = False
+    
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(bugname)
+    if is_reply:
+        parent = bug.comment_root.comment_from_shortname(shortname, bug_shortname=bugname)
+    else:
+        parent = bug.comment_root
+    
     if len(args) == 1:
         try:
             body = utility.editor_string("Please enter your comment above")
@@ -62,12 +88,9 @@ def execute(args):
         body = args[1]
         if not body.endswith('\n'):
             body+='\n'
-
-    comment = bug.new_comment(body)
-    if parent_comment is not None:
-        comment.in_reply_to = parent_comment.uuid
-    comment.save()
-
+    
+    comment = parent.new_reply(body=body)
+    bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be comment ID COMMENT")
index 5a3a7cfa5a17514829c91561f818ff0ba6823b9f..9d8d3b590a9d2f8e1980154c49ef3e4b5388c028 100644 (file)
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 """Compare bug reports with older tree"""
-from libbe import bugdir, diff, cmdutil
+from libbe import cmdutil, bugdir, diff
 import os
 __desc__ = __doc__
 
 def execute(args):
+    """
+    >>> import os
+    >>> bd = bugdir.simple_bug_dir()
+    >>> original = bd.rcs.commit("Original status")
+    >>> bug = bd.bug_from_uuid("a")
+    >>> bug.status = "closed"
+    >>> bd.save()
+    >>> changed = bd.rcs.commit("Closed bug a")
+    >>> os.chdir(bd.root)
+    >>> if bd.rcs.versioned == True:
+    ...     execute([original])
+    ... else:
+    ...     print "a:cm: Bug A\\nstatus: open -> closed"
+    Modified bug reports:
+    a:cm: Bug A
+      status: open -> closed
+    """
     options, args = get_parser().parse_args(args)
     if len(args) == 0:
-        spec = None
-    elif len(args) == 1:
-        spec = args[0]
-    else:
-        raise cmdutil.UsageError
-    tree = bugdir.tree_root(".")
-    if tree.rcs_name == "None":
+        revision = None
+    if len(args) == 1:
+        revision = args[0]
+    if len(args) > 1:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
+    if bd.rcs.versioned == False:
         print "This directory is not revision-controlled."
     else:
-        diff.diff_report(diff.reference_diff(tree, spec), tree)
-
+        old_bd = bd.duplicate_bugdir(revision)
+        r,m,a = diff.diff(old_bd, bd)
+        diff.diff_report((r,m,a), bd)
+        bd.remove_duplicate_bugdir()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be diff [specifier]")
index f6cfe3f50b3f5fcff11b1a40d0d1f81f3dbc89f0..bf0b4fc921d30b8a4a16050f98c92df8cb4f860e 100644 (file)
@@ -15,7 +15,7 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Print help for given subcommand"""
-from libbe import cmdutil, names, utility
+from libbe import cmdutil, utility
 __desc__ = __doc__
 
 def execute(args):
index d43b5734b6037df5e4955f5a1dc46cefbb3f64fb..22d73d92621eb1da636a46077585d3a6fbb444bc 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """List bugs"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 from libbe.bug import cmp_full, severity_values, status_values, \
     active_status_values, inactive_status_values
 import os
 __desc__ = __doc__
 
 def execute(args):
+    """
+    >>> import os
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> execute([])
+    a:om: Bug A
+    >>> execute(["--status", "all"])
+    a:om: Bug A
+    b:cm: Bug B
+    """
     options, args = get_parser().parse_args(args)
     if len(args) > 0:
-        raise cmdutil.UsageError
-    tree = cmdutil.bug_tree()
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
     # select status
     if options.status != None:
         if options.status == "all":
@@ -73,7 +84,7 @@ def execute(args):
             assigned = "all"
     for i in range(len(assigned)):
         if assigned[i] == '-':
-            assigned[i] = tree.rcs.get_user_id()
+            assigned[i] = bd.rcs.get_user_id()
     # select target
     if options.target != None:
         if options.target == "all":
@@ -83,7 +94,7 @@ def execute(args):
     else:
         target = []
         if options.cur_target == True:
-            target.append(tree.target)
+            target.append(bd.target)
         if target == []: # set the default value
             target = "all"
     
@@ -98,8 +109,7 @@ def execute(args):
             return False
         return True
 
-    all_bugs = list(tree.list())
-    bugs = [b for b in all_bugs if filter(b) ]
+    bugs = [b for b in bd if filter(b) ]
     if len(bugs) == 0:
         print "No matching bugs found"
     
@@ -109,7 +119,7 @@ def execute(args):
             if title != None:
                 print cmdutil.underlined(title)
             for bug in cur_bugs:
-                print bug.string(all_bugs, shortlist=True)
+                print bug.string(shortlist=True)
     
     list_bugs(bugs, no_target=False)
 
index 0f9928ab1160dff6a306c59949ea79bfdb9b088f..c9688b9353482ed32ecb5ccde1889ec79992cb66 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Create a new bug"""
-from libbe import cmdutil, names
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
     >>> import os, time
-    >>> from libbe import bugdir
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> names.uuid = lambda: "X"
+    >>> from libbe import bug
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> bug.uuid_gen = lambda: "X"
     >>> execute (["this is a test",])
     Created bug with ID X
-    >>> bug = cmdutil.get_bug("X", dir)
+    >>> bd.load()
+    >>> bug = bd.bug_from_uuid("X")
     >>> bug.summary
     u'this is a test'
-    >>> bug.creator = os.environ["LOGNAME"]
     >>> bug.time <= int(time.time())
     True
     >>> bug.severity
@@ -41,12 +41,10 @@ def execute(args):
     options, args = get_parser().parse_args(args)
     if len(args) != 1:
         raise cmdutil.UserError("Please supply a summary message")
-    dir = cmdutil.bug_tree()
-    bug = dir.new_bug()
-    bug.summary = args[0]
-    bug.save()
-    bugs = (dir.list())
-    print "Created bug with ID %s" % names.unique_name(bug, bugs)
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.new_bug(summary=args[0])
+    bd.save()
+    print "Created bug with ID %s" % bd.bug_shortname(bug)
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be new SUMMARY")
index 24639698401b6a479c1863c4a61ad9ce0bfb10b0..736b601167a065cfcd6597eb09e3178234faf2e5 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Re-open a bug"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> dir.get_bug("b").status
-    u'closed'
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> print bd.bug_from_shortname("b").status
+    closed
     >>> execute(["b"])
-    >>> dir.get_bug("b").status
-    u'open'
+    >>> bd.load()
+    >>> print bd.bug_from_shortname("b").status
+    open
     """
     options, args = get_parser().parse_args(args)
-    if len(args) !=1:
+    if len(args) == 0:
         raise cmdutil.UserError("Please specify a bug id.")
-    bug = cmdutil.get_bug(args[0])
+    if len(args) > 1:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     bug.status = "open"
-    bug.save()
+    bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be open BUG-ID")
index 172fb96caaf7670a85791f7a872ae20a9d865d26..7ba5e23c4aad1082bd262479aee945973552a0a0 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Remove (delete) a bug and its comments"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
+    >>> from libbe import mapfile
     >>> import os
-    >>> from libbe import bugdir, mapfile
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> dir.get_bug("b").status
-    u'closed'
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> print bd.bug_from_shortname("b").status
+    closed
     >>> execute (["b"])
     Removed bug b
+    >>> bd.load()
     >>> try:
-    ...     dir.get_bug("b")
-    ... except mapfile.NoSuchFile:
+    ...     bd.bug_from_shortname("b")
+    ... except KeyError:
     ...     print "Bug not found"
     Bug not found
     """
     options, args = get_parser().parse_args(args)
     if len(args) != 1:
         raise cmdutil.UserError("Please specify a bug id.")
-    bug = cmdutil.get_bug(args[0])
-    bug.remove()
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
+    bd.remove_bug(bug)
+    bd.save()
     print "Removed bug %s" % bug.uuid
 
 def get_parser():
index 368aa65bdc4b0819d0acddc592d984f47930dae8..7951c8beffcaf70b3dc3588c9aebbdf2b1b5ad7e 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Change tree settings"""
-from libbe import cmdutil 
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
-    >>> execute(["a"])
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> execute(["target"])
     None
-    >>> execute(["a", "tomorrow"])
-    >>> execute(["a"])
+    >>> execute(["target", "tomorrow"])
+    >>> execute(["target"])
     tomorrow
-    >>> execute(["a", "none"])
-    >>> execute(["a"])
+    >>> execute(["target", "none"])
+    >>> execute(["target"])
     None
     """
     options, args = get_parser().parse_args(args)
     if len(args) > 2:
+        help()
         raise cmdutil.UserError("Too many arguments.")
-    tree = cmdutil.bug_tree()
+    bd = bugdir.BugDir(loadNow=True)
     if len(args) == 0:
-        keys = tree.settings.keys()
+        keys = bd.settings.keys()
         keys.sort()
         for key in keys:
-            print "%16s: %s" % (key, tree.settings[key])
+            print "%16s: %s" % (key, bd.settings[key])
     elif len(args) == 1:
-        print tree.settings.get(args[0])
+        print bd.settings.get(args[0])
     else:
         if args[1] != "none":
-            tree.settings[args[0]] = args[1]
+            bd.settings[args[0]] = args[1]
         else:
-            del tree.settings[args[0]]
-        tree.save_settings()
+            del bd.settings[args[0]]
+        bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be set [name] [value]")
index 1c731da2b1bb2a5f04956b25f5bb7bff4cea40fd..d8fcdf3c18e416bfcfdfb413a48be3480bd6efd9 100644 (file)
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Assign the root directory for bug tracking"""
 import os.path
-from libbe import bugdir, cmdutil, rcs
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import utility
+    >>> from libbe import utility, rcs
     >>> import os
     >>> dir = utility.Dir()
     >>> try:
-    ...     bugdir.tree_root(dir.path)
+    ...     bugdir.BugDir(dir.path)
     ... except bugdir.NoBugDir, e:
     ...     True
     True
     >>> execute([dir.path])
     No revision control detected.
     Directory initialized.
-    >>> bd = bugdir.tree_root(dir.path)
-    >>> bd.root = dir.path
-    >>> dir_rcs = rcs.installed_rcs()
-    >>> dir_rcs.init(bd.dir)
-    >>> bd.rcs_name = dir_rcs.name
-    >>> del(dir_rcs)
-    >>> os.chdir(bd.dir)
-    >>> execute(['.'])
+    >>> del(dir)
+
+    >>> dir = utility.Dir()
+    >>> os.chdir(dir.path)
+    >>> rcs = rcs.installed_rcs()
+    >>> rcs.init('.')
+    >>> print rcs.name
+    Arch
+    >>> execute([])
     Using Arch for revision control.
     Directory initialized.
+
     >>> try:
     ...     execute(['.'])
     ... except cmdutil.UserError, e:
@@ -50,29 +52,34 @@ def execute(args):
     >>> execute(['/highly-unlikely-to-exist'])
     Traceback (most recent call last):
     UserError: No such directory: /highly-unlikely-to-exist
+    >>> os.chdir('/')
     """
     options, args = get_parser().parse_args(args)
-    basedir = args[0]
-    if len(args) != 1:
-        raise cmdutil.UsageError
+    if len(args) > 1:
+        print help()
+        raise cmdutil.UserError, "Too many arguments"
+    if len(args) == 1:
+        basedir = args[0]
+    else:
+        basedir = "."
     if os.path.exists(basedir) == False:
-        raise cmdutil.UserError, "No such directory: %s" % basedir
-    dir_rcs = rcs.detect_rcs(basedir)
-    dir_rcs.root(basedir)
+        pass
+        #raise cmdutil.UserError, "No such directory: %s" % basedir
     try:
-        bugdir.create_bug_dir(basedir, dir_rcs)
+        bd = bugdir.BugDir(basedir, loadNow=False, sink_to_existing_root=False, assert_new_BugDir=True)
     except bugdir.NoRootEntry:
         raise cmdutil.UserError("No such directory: %s" % basedir)
     except bugdir.AlreadyInitialized:
         raise cmdutil.UserError("Directory already initialized: %s" % basedir)
-    if dir_rcs.name is not "None":
-        print "Using %s for revision control." % dir_rcs.name
+    bd.save()
+    if bd.rcs.name is not "None":
+        print "Using %s for revision control." % bd.rcs.name
     else:
         print "No revision control detected."
     print "Directory initialized."
 
 def get_parser():
-    parser = cmdutil.CmdOptionParser("be set-root DIRECTORY")
+    parser = cmdutil.CmdOptionParser("be set-root [DIRECTORY]")
     return parser
 
 longhelp="""
@@ -80,6 +87,8 @@ This command initializes Bugs Everywhere support for the specified directory
 and all its subdirectories.  It will auto-detect any supported revision control
 system.  You can use "be set rcs_name" to change the rcs being used.
 
+DIRECTORY defaults to your current working directory.
+
 It is usually a good idea to put the Bugs Everywhere root at the source code
 root, but you can put it anywhere.  If you run "be set-root" in a subdirectory,
 then only bugs created in that subdirectory (and its children) will appear
index b0556951c8d65163e1f192d49a6972d9d9fb818d..d7df13dcb6f37b285ee8928a9a58b991e66a41d4 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Show or change a bug's severity level"""
-from libbe import cmdutil 
+from libbe import cmdutil, bugdir
 from libbe.bug import severity_values, severity_description
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
     >>> execute(["a"])
     minor
     >>> execute(["a", "wishlist"])
@@ -35,11 +34,11 @@ def execute(args):
     UserError: Invalid severity level: none
     """
     options, args = get_parser().parse_args(args)
-    assert(len(args) in (0, 1, 2))
-    if len(args) == 0:
+    if len(args) not in (1,2):
         print help()
         return
-    bug = cmdutil.get_bug(args[0])
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     if len(args) == 1:
         print bug.severity
     elif len(args) == 2:
@@ -49,7 +48,7 @@ def execute(args):
             if e.name != "severity":
                 raise
             raise cmdutil.UserError ("Invalid severity level: %s" % e.value)
-        bug.save()
+        bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be severity bug-id [severity]")
index 669a81d404653388aa571554196d861a1b7e3427..ab296e32f3c7cf0769a8f4fb29f447e37c409dc4 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Show a particular bug"""
-from libbe import cmdutil, names, utility
-from libbe.bug import thread_comments
-import os
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
+    """
+    >>> import os
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
+    >>> execute (["a",])
+              ID : a
+      Short name : a
+        Severity : minor
+          Status : open
+        Assigned : 
+          Target : 
+         Creator : John Doe <jdoe@example.com>
+         Created : Wed, 31 Dec 1969 19:00 (Thu, 01 Jan 1970 00:00:00 +0000)
+    Bug A
+    <BLANKLINE>
+    """
     options, args = get_parser().parse_args(args)
-    if len(args) !=1:
+    if len(args) == 0:
         raise cmdutil.UserError("Please specify a bug id.")
-    bug_dir = cmdutil.bug_tree()
-    bug = cmdutil.get_bug(args[0], bug_dir)
-    print bug.string().rstrip("\n")
-    unique_name = names.unique_name(bug, bug_dir.list())
-    comments = []
-    name_map = {}
-    for c_name, comment in cmdutil.iter_comment_name(bug, unique_name):
-        name_map[comment.uuid] = c_name
-        comments.append(comment)
-    threaded = thread_comments(comments)
-    cmdutil.print_threaded_comments(threaded, name_map)
+    if len(args) > 1:
+        help()
+        raise cmdutil.UserError("Too many arguments.")
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
+    print bug.string(show_comments=True)
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be show bug-id")
index 5559e59a2c5530f993241fd1ab7da48367a3eea7..de171f58141c6e1222b54e58dcf189c11af366f6 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Show or change a bug's status"""
-from libbe import cmdutil
+from libbe import cmdutil, bugdir
 from libbe.bug import status_values, status_description
 __desc__ = __doc__
 
 def execute(args):
     """
-    >>> from libbe import bugdir
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
     >>> execute(["a"])
     open
     >>> execute(["a", "closed"])
@@ -35,11 +34,11 @@ def execute(args):
     UserError: Invalid status: none
     """
     options, args = get_parser().parse_args(args)
-    assert(len(args) in (0, 1, 2))
-    if len(args) == 0:
+    if len(args) not in (1,2):
         print help()
         return
-    bug = cmdutil.get_bug(args[0])
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     if len(args) == 1:
         print bug.status
     elif len(args) == 2:
@@ -49,7 +48,7 @@ def execute(args):
             if e.name != "status":
                 raise
             raise cmdutil.UserError ("Invalid status: %s" % e.value)
-        bug.save()
+        bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be status bug-id [status]")
index 16de8fe6171ab459f32121dc5bdc5615869c622b..2047397251ee1b22a4585dcd2620f3f2e94985bb 100644 (file)
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Show or change a bug's target for fixing"""
-from libbe import bugdir
-from libbe import cmdutil 
+from libbe import cmdutil, bugdir
 __desc__ = __doc__
 
 def execute(args):
     """
     >>> import os
-    >>> dir = bugdir.simple_bug_dir()
-    >>> os.chdir(dir.dir)
+    >>> bd = bugdir.simple_bug_dir()
+    >>> os.chdir(bd.root)
     >>> execute(["a"])
     No target assigned.
     >>> execute(["a", "tomorrow"])
@@ -38,7 +37,8 @@ def execute(args):
     if len(args) == 0:
         print help()
         return
-    bug = cmdutil.get_bug(args[0])
+    bd = bugdir.BugDir(loadNow=True)
+    bug = bd.bug_from_shortname(args[0])
     if len(args) == 1:
         if bug.target is None:
             print "No target assigned."
@@ -49,7 +49,7 @@ def execute(args):
             bug.target = None
         else:
             bug.target = args[1]
-        bug.save()
+        bd.save()
 
 def get_parser():
     parser = cmdutil.CmdOptionParser("be target bug-id [target]")
diff --git a/becommands/upgrade.py b/becommands/upgrade.py
deleted file mode 100644 (file)
index c48eaaa..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (C) 2005 Aaron Bentley and Panometrics, Inc.
-# <abentley@panoramicfeedback.com>
-#
-#    This program is free software; you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation; either version 2 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-# OUTDATED
-
-"""Upgrade the bugs to the latest format"""
-import os.path
-import errno
-from libbe import bugdir, rcs, cmdutil
-__desc__ = __doc__
-
-def execute(args):
-    options, args = get_parser().parse_args(args)
-    root = bugdir.tree_root(".", old_version=True)
-    for uuid in root.list_uuids():
-        old_bug = OldBug(root.bugs_path, uuid)
-        
-        new_bug = root.get_bug(uuid)
-        new_bug.uuid = old_bug.uuid
-        new_bug.summary = old_bug.summary
-        new_bug.creator = old_bug.creator
-        new_bug.target = old_bug.target
-        new_bug.status = old_bug.status
-        new_bug.severity = old_bug.severity
-
-        new_bug.save()
-    for uuid in root.list_uuids():
-        old_bug = OldBug(root.bugs_path, uuid)
-        old_bug.delete()
-
-    bugdir.set_version(root.dir)
-
-def file_property(name, valid=None):
-    def getter(self):
-        value = self._get_value(name) 
-        if valid is not None:
-            if value not in valid:
-                raise InvalidValue(name, value)
-        return value
-    def setter(self, value):
-        if valid is not None:
-            if value not in valid and value is not None:
-                raise InvalidValue(name, value)
-        return self._set_value(name, value)
-    return property(getter, setter)
-
-
-class OldBug(object):
-    def __init__(self, path, uuid):
-        self.path = os.path.join(path, uuid)
-        self.uuid = uuid
-
-    def get_path(self, file):
-        return os.path.join(self.path, file)
-
-    summary = file_property("summary")
-    creator = file_property("creator")
-    target = file_property("target")
-    status = file_property("status", valid=("open", "closed"))
-    severity = file_property("severity", valid=("wishlist", "minor", "serious",
-                                                "critical", "fatal"))
-    def delete(self):
-        self.summary = None
-        self.creator = None
-        self.target = None
-        self.status = None
-        self.severity = None
-        self._set_value("name", None)
-
-    def _get_active(self):
-        return self.status == "open"
-
-    active = property(_get_active)
-
-    def _get_value(self, name):
-        try:
-            return file(self.get_path(name), "rb").read().rstrip("\n")
-        except IOError, e:
-            if e.errno == errno.EEXIST:
-                return None
-
-    def _set_value(self, name, value):
-        if value is None:
-            try:
-                rcs.unlink(self.get_path(name))
-            except OSError, e:
-                if e.errno != 2:
-                    raise
-        else:
-            rcs.set_file_contents(self.get_path(name), "%s\n" % value)
-
-def get_parser():
-    parser = cmdutil.CmdOptionParser("be upgrade")
-    return parser
-
-longhelp="""
-Upgrade the bug storage to the latest format.
-"""
-
-def help():
-    return get_parser().help_str() + longhelp
index 8e7390d33226f350824fd1c14e64f8d23b50b1f9..b35a897918692ca70b6d7f11c313352228bd04eb 100644 (file)
@@ -50,14 +50,6 @@ class Arch(RCS):
         if self._u_search_parent_directories(path, "{arch}") != None :
             return True
         return False
-    def _rcs_root(self, path):
-        if not os.path.isdir(path):
-            dirname = os.path.dirname(path)
-        else:
-            dirname = path
-        status,output,error = self._u_invoke_client("tree-root", dirname)
-        # get archive name...
-        return output.rstrip('\n')
     def _rcs_init(self, path):
         self._create_archive(path)
         self._create_project(path)
@@ -121,11 +113,35 @@ class Arch(RCS):
         assert self._archive_name != None
         assert self._project_name != None
         return "%s/%s" % (self._archive_name, self._project_name)
+    def _adjust_naming_conventions(self, path):
+        """
+        By default, Arch restricts source code filenames to
+          ^[_=a-zA-Z0-9].*$
+        See
+          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
+        """
+        tagpath = os.path.join(path, "{arch}", "=tagging-method")
+        lines_out = []
+        for line in file(tagpath, "rb"):
+            line.decode("utf-8")
+            if line.startswith("source "):
+                lines_out.append("source ^[._=a-zA-X0-9].*$\n")
+            else:
+                lines_out.append(line)
+        file(tagpath, "wb").write("".join(lines_out).encode("utf-8"))
+
     def _add_project_code(self, path):
         # http://mwolson.org/projects/GettingStartedWithArch.html
-        # http://regexps.srparish.net/tutorial-tla/importing-first.html#Importing_the_First_Revision
-        self._u_invoke_client("init-tree", self._archive_project_name(),
+        # http://regexps.srparish.net/tutorial-tla/new-source.html
+        # http://regexps.srparish.net/tutorial-tla/importing-first.html
+        self._invoke_client("init-tree", self._project_name,
                               directory=path)
+        self._adjust_naming_conventions(path)
         self._invoke_client("import", "--summary", "Began versioning",
                             directory=path)
     def _rcs_cleanup(self):
@@ -133,6 +149,40 @@ class Arch(RCS):
             self._remove_project()
         if self._tmp_archive == True:
             self._remove_archive()
+
+    def _rcs_root(self, path):
+        if not os.path.isdir(path):
+            dirname = os.path.dirname(path)
+        else:
+            dirname = path
+        status,output,error = self._u_invoke_client("tree-root", dirname)
+        root = output.rstrip('\n')
+        
+        self._get_archive_project_name(root)
+
+        return root
+
+    def _get_archive_name(self, root):
+        status,output,error = self._u_invoke_client("archives")
+        lines = output.split('\n')
+        # e.g. output:
+        # jdoe@example.com--bugs-everywhere-auto-2008.22.24.52
+        #     /tmp/BEtestXXXXXX/rootdir
+        # (+ repeats)
+        for archive,location in zip(lines[::2], lines[1::2]):
+            if os.path.realpath(location) == os.path.realpath(root):
+                self._archive_name = archive
+        assert self._archive_name != None
+
+    def _get_archive_project_name(self, root):
+        # get project names
+        status,output,error = self._u_invoke_client("tree-version", directory=root)
+        # e.g output
+        # jdoe@example.com--bugs-everywhere-auto-2008.22.24.52/be--mainline--0.1
+        archive_name,project_name = output.rstrip('\n').split('/')
+        self._archive_name = archive_name
+        self._project_name = project_name
+
     def _rcs_get_user_id(self):
         try:
             status,output,error = self._u_invoke_client('my-id')
index a297b1a5ff37fefd393c8128254cc548ff760a8a..b1e8d26f59231289e5a2e50620cc99c8414075a5 100644 (file)
 import os
 import os.path
 import errno
-import names
-import mapfile
 import time
-import utility
 import doctest
 
+from beuuid import uuid_gen
+import mapfile
+import comment
+import utility
+
+
 ### Define and describe valid bug categories
 # Use a tuple of (category, description) tuples since we don't have
 # ordered dicts in Python yet http://www.python.org/dev/peps/pep-0372/
@@ -87,45 +90,46 @@ class Bug(object):
     severity = checked_property("severity", severity_values)
     status = checked_property("status", status_values)
 
-    def __init__(self, path, uuid, rcs, bugdir):
-        self.path = path
-        self.uuid = uuid
-        if uuid is not None:
-            dict = mapfile.map_load(self.get_path("values"))
-        else:
-            dict = {}
-
-        self.rcs = rcs
-        self.bugdir = bugdir
-        
-        self.summary = dict.get("summary")
-        self.creator = dict.get("creator")
-        self.target = dict.get("target")
-        self.status = dict.get("status", "open")
-        self.severity = dict.get("severity", "minor")
-        self.assigned = dict.get("assigned")
-        self.time = dict.get("time")
-        if self.time is not None:
-            self.time = utility.str_to_time(self.time)
-
-    def get_path(self, file=None):
-        if file == None:
-            return os.path.join(self.path, self.uuid)
-        else:
-            return os.path.join(self.path, self.uuid, file)
-
     def _get_active(self):
         return self.status in active_status_values
 
     active = property(_get_active)
 
+    def __init__(self, bugdir=None, uuid=None, loadNow=False, summary=None):
+        self.bugdir = bugdir
+        if bugdir != None:
+            self.rcs = bugdir.rcs
+        else:
+            self.rcs = None
+        if loadNow == True:
+            self.uuid = uuid
+            self.load()
+        else:
+            # Note: defaults should match those in Bug.load()
+            if uuid != None:
+                self.uuid = uuid
+            else:
+                self.uuid = uuid_gen()
+            self.summary = summary
+            if self.rcs != None:
+                self.creator = self.rcs.get_user_id()
+            else:
+                self.creator = None
+            self.target = None
+            self.status = "open"
+            self.severity = "minor"
+            self.assigned = None
+            self.time = time.time()
+            self.comment_root = comment.Comment(self, uuid=comment.INVALID_UUID)
+
     def __repr__(self):
         return "Bug(uuid=%r)" % self.uuid
 
-    def string(self, bugs=None, shortlist=False):
-        if bugs == None:
-            bugs = list(self.bugdir.list())
-        short_name = names.unique_name(self, bugs)
+    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 = ""
@@ -134,7 +138,7 @@ class Bug(object):
                 ftime = utility.time_to_str(self.time)
                 timestring = "%s (%s)" % (htime, ftime)
             info = [("ID", self.uuid),
-                    ("Short name", short_name),
+                    ("Short name", shortname),
                     ("Severity", self.severity),
                     ("Status", self.status),
                     ("Assigned", self.assigned),
@@ -150,12 +154,20 @@ class Bug(object):
             info = newinfo
             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]
-            return "".join(infolines) + "%s\n" % self.summary
+            bugout = "".join(infolines) + "%s" % self.summary.rstrip('\n')
         else:
             statuschar = self.status[0]
             severitychar = self.severity[0]
             chars = "%c%c" % (statuschar, severitychar)
-            return "%s:%s: %s" % (short_name, chars, self.summary)
+            bugout = "%s:%s: %s" % (shortname, chars, self.summary.rstrip('\n'))
+        
+        if show_comments == True:
+            comout = self.comment_root.string_thread(auto_name_map=True,
+                                                     bug_shortname=shortname)
+            output = bugout + '\n' + comout.rstrip('\n')
+        else :
+            output = bugout
+        return output
 
     def __str__(self):
         return self.string(shortlist=True)
@@ -163,7 +175,28 @@ class Bug(object):
     def __cmp__(self, other):
         return cmp_full(self, other)
 
-    def add_attr(self, map, name):
+    def get_path(self, name=None):
+        my_dir = os.path.join(self.bugdir.get_path("bugs"), self.uuid)
+        if name is None:
+            return my_dir
+        assert name in ["values", "comments"]
+        return os.path.join(my_dir, name)
+
+    def load(self):
+        map = mapfile.map_load(self.get_path("values"))
+        self.summary = map.get("summary")
+        self.creator = map.get("creator")
+        self.target = map.get("target")
+        self.status = map.get("status", "open")
+        self.severity = map.get("severity", "minor")
+        self.assigned = map.get("assigned")
+        self.time = map.get("time")
+        if self.time is not None:
+            self.time = utility.str_to_time(self.time)
+        
+        self.comment_root = comment.loadComments(self)
+
+    def _add_attr(self, map, name):
         value = getattr(self, name)
         if value is not None:
             map[name] = value
@@ -171,134 +204,39 @@ class Bug(object):
     def save(self):
         assert self.summary != None, "Can't save blank bug"
         map = {}
-        self.add_attr(map, "assigned")
-        self.add_attr(map, "summary")
-        self.add_attr(map, "creator")
-        self.add_attr(map, "target")
-        self.add_attr(map, "status")
-        self.add_attr(map, "severity")
+        self._add_attr(map, "assigned")
+        self._add_attr(map, "summary")
+        self._add_attr(map, "creator")
+        self._add_attr(map, "target")
+        self._add_attr(map, "status")
+        self._add_attr(map, "severity")
         if self.time is not None:
             map["time"] = utility.time_to_str(self.time)
+
+        self.rcs.mkdir(self.get_path())
         path = self.get_path("values")
         mapfile.map_save(self.rcs, path, map)
 
+        if len(self.comment_root) > 0:
+            self.rcs.mkdir(self.get_path("comments"))
+            comment.saveComments(self)
+
     def remove(self):
+        self.comment_root.remove()
         path = self.get_path()
         self.rcs.recursive_remove(path)
     
     def new_comment(self, body=None):
-        if not os.path.exists(self.get_path("comments")):
-            self.rcs.mkdir(self.get_path("comments"))
-        comm = Comment(None, self)
-        comm.uuid = names.uuid()
-        comm.rcs = self.rcs
-        comm.From = self.rcs.get_user_id()
-        comm.time = time.time()
-        comm.body = body
+        comm = comment.comment_root.new_reply(body=body)
         return comm
 
-    def get_comment(self, uuid):
-        return Comment(uuid, self)
-
-    def iter_comment_ids(self):
-        path = self.get_path("comments")
-        if not os.path.isdir(path):
-            return
-        try:
-            for uuid in os.listdir(path):
-                if (uuid.startswith('.')):
-                    continue
-                yield uuid
-        except OSError, e:
-            if e.errno != errno.ENOENT:
-                raise
-            return
-
-    def list_comments(self):
-        comments = [Comment(id, self) for id in self.iter_comment_ids()]
-        comments.sort(cmp_time)
-        return comments
-
-def add_headers(obj, map, names):
-    map_names = {}
-    for name in names:
-        map_names[name] = pyname_to_header(name)
-    add_attrs(obj, map, names, map_names)
-
-def add_attrs(obj, map, names, map_names=None):
-    if map_names is None:
-        map_names = {}
-        for name in names:
-            map_names[name] = name 
-        
-    for name in names:
-        value = getattr(obj, name)
-        if value is not None:
-            map[map_names[name]] = value
-
-
-class Comment(object):
-    def __init__(self, uuid, bug):
-        object.__init__(self)
-        self.uuid = uuid 
-        self.bug = bug
-        if self.uuid is not None and self.bug is not None:
-            map = mapfile.map_load(self.get_path("values"))
-            self.time = utility.str_to_time(map["Date"])
-            self.From = map["From"]
-            self.in_reply_to = map.get("In-reply-to")
-            self.content_type = map.get("Content-type", "text/plain")
-            self.body = file(self.get_path("body")).read().decode("utf-8")
-        else:
-            self.time = None
-            self.From = None
-            self.in_reply_to = None
-            self.content_type = "text/plain"
-            self.body = None
+    def comment_from_shortname(self, shortname, *args, **kwargs):
+        return self.comment_root.comment_from_shortname(shortname, *args, **kwargs)
 
-    def save(self):
-        map_file = {"Date": utility.time_to_str(self.time)}
-        add_headers(self, map_file, ("From", "in_reply_to", "content_type"))
-        if not os.path.exists(self.get_path()):
-            self.bug.rcs.mkdir(self.get_path())
-        mapfile.map_save(self.bug.rcs, self.get_path("values"), map_file)
-        self.bug.rcs.set_file_contents(self.get_path("body"), 
-                                       self.body.encode('utf-8'))
-
-    def get_path(self, name=None):
-        my_dir = os.path.join(self.bug.get_path("comments"), self.uuid)
-        if name is None:
-            return my_dir
-        return os.path.join(my_dir, name)
-
-
-def thread_comments(comments):
-    child_map = {}
-    top_comments = []
-    for comment in comments:
-        child_map[comment.uuid] = []
-    for comment in comments:
-        if comment.in_reply_to is None or comment.in_reply_to not in child_map:
-            top_comments.append(comment)
-            continue
-        child_map[comment.in_reply_to].append(comment)
-
-    def recurse_children(comment):
-        child_list = []
-        for child in child_map[comment.uuid]:
-            child_list.append(recurse_children(child))
-        return (comment, child_list)
-    return [recurse_children(c) for c in top_comments]
-
-def pyname_to_header(name):
-    return name.capitalize().replace('_', '-')
+    def comment_from_uuid(self, uuid):
+        return self.comment_root.comment_from_uuid(uuid)
 
 
-
-class MockBug:
-    def __init__(self, attr, value):
-        setattr(self, attr, value)
-
 # the general rule for bug sorting is that "more important" bugs are
 # less than "less important" bugs.  This way sorting a list of bugs
 # will put the most important bugs first in the list.  When relative
@@ -307,32 +245,42 @@ class MockBug:
 
 def cmp_severity(bug_1, bug_2):
     """
-    Compare the severity levels of two bugs, with more severe bugs comparing
-    as less.
-
-    >>> attr="severity"
-    >>> cmp_severity(MockBug(attr,"wishlist"), MockBug(attr,"wishlist")) == 0
+    Compare the severity levels of two bugs, with more severe bugs
+    comparing as less.
+    >>> bugA = Bug()
+    >>> bugB = Bug()
+    >>> bugA.severity = bugB.severity = "wishlist"
+    >>> cmp_severity(bugA, bugB) == 0
     True
-    >>> cmp_severity(MockBug(attr,"wishlist"), MockBug(attr,"minor")) > 0
+    >>> bugB.severity = "minor"
+    >>> cmp_severity(bugA, bugB) > 0
     True
-    >>> cmp_severity(MockBug(attr,"critical"), MockBug(attr,"wishlist")) < 0
+    >>> bugA.severity = "critical"
+    >>> cmp_severity(bugA, bugB) < 0
     True
     """
+    if not hasattr(bug_2, "severity") :
+        return 1
     return -cmp(severity_index[bug_1.severity], severity_index[bug_2.severity])
 
 def cmp_status(bug_1, bug_2):
     """
     Compare the status levels of two bugs, with more 'open' bugs
     comparing as less.
-
-    >>> attr="status"
-    >>> cmp_status(MockBug(attr,"open"), MockBug(attr,"open")) == 0
+    >>> bugA = Bug()
+    >>> bugB = Bug()
+    >>> bugA.status = bugB.status = "open"
+    >>> cmp_status(bugA, bugB) == 0
     True
-    >>> cmp_status(MockBug(attr,"open"), MockBug(attr,"closed")) < 0
+    >>> bugB.status = "closed"
+    >>> cmp_status(bugA, bugB) < 0
     True
-    >>> cmp_status(MockBug(attr,"closed"), MockBug(attr,"open")) > 0
+    >>> bugA.status = "fixed"
+    >>> cmp_status(bugA, bugB) > 0
     True
     """
+    if not hasattr(bug_2, "status") :
+        return 1
     val_2 = status_index[bug_2.status]
     return cmp(status_index[bug_1.status], status_index[bug_2.status])
 
@@ -342,13 +290,20 @@ def cmp_attr(bug_1, bug_2, attr, invert=False):
     comparison rule for that attribute type.  If invert == True, sort
     *against* that convention.
     >>> attr="severity"
-    >>> cmp_attr(MockBug(attr,1), MockBug(attr,2), attr, invert=False) < 0
+    >>> bugA = Bug()
+    >>> bugB = Bug()
+    >>> bugA.severity = "critical"
+    >>> bugB.severity = "wishlist"
+    >>> cmp_attr(bugA, bugB, attr) < 0
     True
-    >>> cmp_attr(MockBug(attr,1), MockBug(attr,2), attr, invert=True) > 0
+    >>> cmp_attr(bugA, bugB, attr, invert=True) > 0
     True
-    >>> cmp_attr(MockBug(attr,1), MockBug(attr,1), attr) == 0
+    >>> bugB.severity = "critical"
+    >>> cmp_attr(bugA, bugB, attr) == 0
     True
     """
+    if not hasattr(bug_2, attr) :
+        return 1
     if invert == True :
         return -cmp(getattr(bug_1, attr), getattr(bug_2, attr))
     else :
index 41f0fece2a9243640e73b14f9120627fd71a57c9..6152e3f6ecac11aa791480374fe5e9fefc577f57 100644 (file)
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 import os
 import os.path
-import cmdutil
 import errno
+import time
 import unittest
 import doctest
-import names
+
+from beuuid import uuid_gen
 import mapfile
-import time
+import bug
+import cmdutil
 import utility
-from rcs import rcs_by_name, installed_rcs
-from bug import Bug
+from rcs import rcs_by_name, detect_rcs, installed_rcs, PathNotInRoot
 
 class NoBugDir(Exception):
     def __init__(self, path):
@@ -33,48 +34,6 @@ class NoBugDir(Exception):
         Exception.__init__(self, msg)
         self.path = path
 
-def iter_parent_dirs(cur_dir):
-    cur_dir = os.path.realpath(cur_dir)
-    old_dir = None
-    while True:
-        yield cur_dir
-        old_dir = cur_dir
-        cur_dir = os.path.normpath(os.path.join(cur_dir, '..'))
-        if old_dir == cur_dir:
-            break;
-
-
-def tree_root(dir, old_version=False):
-    for rootdir in iter_parent_dirs(dir):
-        versionfile=os.path.join(rootdir, ".be", "version")
-        if os.path.exists(versionfile):
-            if not old_version:
-                test_version(versionfile)
-            return BugDir(os.path.join(rootdir, ".be"))
-        elif not os.path.exists(rootdir):
-            raise NoRootEntry(rootdir)
-        old_rootdir = rootdir
-        rootdir=os.path.join('..', rootdir)
-    
-    raise NoBugDir(dir)
-
-class BadTreeVersion(Exception):
-    def __init__(self, version):
-        Exception.__init__(self, "Unsupported tree version: %s" % version)
-        self.version = version
-
-def test_version(path):
-    tree_version = file(path, "rb").read()
-    if tree_version != TREE_VERSION_STRING:
-        raise BadTreeVersion(tree_version)
-
-def set_version(path, rcs):
-    rcs.set_file_contents(os.path.join(path, "version"), TREE_VERSION_STRING)
-    
-
-TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n"
-
 class NoRootEntry(Exception):
     def __init__(self, path):
         self.path = path
@@ -86,32 +45,15 @@ class AlreadyInitialized(Exception):
         Exception.__init__(self, 
                            "Specified root is already initialized: %s" % path)
 
-def bugdir_root(versioning_root):
-    return os.path.join(versioning_root, ".be")
+class InvalidValue(ValueError):
+    def __init__(self, name, value):
+        msg = "Cannot assign value %s to %s" % (value, name)
+        Exception.__init__(self, msg)
+        self.name = name
+        self.value = value
+
 
-def create_bug_dir(path, rcs):
-    """
-    >>> import tests
-    >>> rcs = rcs_by_name("None")
-    >>> create_bug_dir('/highly-unlikely-to-exist', rcs)
-    Traceback (most recent call last):
-    NoRootEntry: Specified root does not exist: /highly-unlikely-to-exist
-    """
-    root = os.path.join(path, ".be")
-    try:
-        rcs.mkdir(root)
-    except OSError, e:
-        if e.errno == errno.ENOENT:
-            raise NoRootEntry(path)
-        elif e.errno == errno.EEXIST:
-            raise AlreadyInitialized(path)
-        else:
-            raise
-    rcs.mkdir(os.path.join(root, "bugs"))
-    set_version(root, rcs)
-    mapfile.map_save(rcs,
-                     os.path.join(root, "settings"), {"rcs_name": rcs.name})
-    return BugDir(bugdir_root(path))
+TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n"
 
 
 def setting_property(name, valid=None):
@@ -130,83 +72,232 @@ def setting_property(name, valid=None):
             del self.settings[name]
         else:
             self.settings[name] = value
-        self.save_settings()
+        self.save()
     return property(getter, setter)
 
 
-class BugDir:
-    def __init__(self, dir):
-        self.dir = dir
-        self.bugs_path = os.path.join(self.dir, "bugs")
+class BugDir (list):
+    def __init__(self, root=None, sink_to_existing_root=True,
+                 assert_new_BugDir=False, allow_rcs_init=False,
+                 loadNow=False, rcs=None):
+        list.__init__(self)
+        if root == None:
+            root = os.getcwd()
+        if sink_to_existing_root == True:
+            self.root = self.find_root(root)
+        else:
+            if not os.path.exists(root):
+                raise NoRootEntry(root)
+            self.root = root
+        if loadNow == True:
+            self.load()
+        else:
+            if assert_new_BugDir:
+                if os.path.exists(self.get_path()):
+                    raise AlreadyInitialized, self.get_path()
+            if rcs == None:
+                rcs = self.guess_rcs(allow_rcs_init)
+            self.settings = {"rcs_name": self.rcs_name}
+            self.rcs_name = rcs.name
+
+    def find_root(self, path):
+        """
+        Search for an existing bug database dir and it's ancestors and
+        return a BugDir rooted there.
+        """
+        if not os.path.exists(path):
+            raise NoRootEntry(path)
+        versionfile = utility.search_parent_directories(path, os.path.join(".be", "version"))
+        if versionfile != None:
+            beroot = os.path.dirname(versionfile)
+            root = os.path.dirname(beroot)
+            return root
+        else:
+            beroot = utility.search_parent_directories(path, ".be")
+            if beroot == None:
+                raise NoBugDir(path)
+            return beroot
+        
+    def get_version(self, path=None):
+        if path == None:
+            path = self.get_path("version")
         try:
-            self.settings = mapfile.map_load(os.path.join(self.dir,"settings"))
-        except mapfile.NoSuchFile:
-            self.settings = {"rcs_name": "None"}
+            tree_version = self.rcs.get_file_contents(path)
+        except AttributeError, e:
+            # haven't initialized rcs yet
+            tree_version = file(path, "rb").read().decode("utf-8")
+        return tree_version
+
+    def set_version(self):
+        self.rcs.set_file_contents(self.get_path("version"), TREE_VERSION_STRING)
 
     rcs_name = setting_property("rcs_name",
                                 ("None", "bzr", "git", "Arch", "hg"))
-    _rcs = None
 
-    target = setting_property("target")
-    
-    def save_settings(self):
-        mapfile.map_save(self.rcs,
-                         os.path.join(self.dir, "settings"), self.settings)
+    _rcs = None
 
     def _get_rcs(self):
         if self._rcs is not None:
             if self.rcs_name == self._rcs.name:
                 return self._rcs
         self._rcs = rcs_by_name(self.rcs_name)
-        self._rcs.root(self.dir)
+        self._rcs.root(self.root)
         return self._rcs
 
     rcs = property(_get_rcs)
 
+    target = setting_property("target")
+
+    def get_path(self, *args):
+        my_dir = os.path.join(self.root, ".be")
+        if len(args) == 0:
+            return my_dir
+        assert args[0] in ["version", "settings", "bugs"], str(args)
+        return os.path.join(my_dir, *args)
+
+    def guess_rcs(self, allow_rcs_init=False):
+        deepdir = self.get_path()
+        if not os.path.exists(deepdir):
+            deepdir = os.path.dirname(deepdir)
+        rcs = detect_rcs(deepdir)
+        if rcs.name == "None":
+            if allow_rcs_init == True:
+                rcs = installed_rcs()
+                rcs.init(self.root)
+        self.settings = {"rcs_name": rcs.name}
+        self.rcs_name = rcs.name
+        return rcs
+
+    def load(self):
+        version = self.get_version()
+        if version != TREE_VERSION_STRING:
+            raise NotImplementedError, "BugDir cannot handle version '%s' yet." % version
+        else:
+            if not os.path.exists(self.get_path()):
+                raise NoBugDir(self.get_path())
+            self.settings = self._get_settings(self.get_path("settings"))
+            self._clear_bugs()
+            for uuid in self.list_uuids():
+                self._load_bug(uuid)
+            
+        self._bug_map_gen()
+
+    def save(self):
+        self.rcs.mkdir(self.get_path())
+        self.set_version()
+        self._save_settings(self.get_path("settings"), self.settings)
+        self.rcs.mkdir(self.get_path("bugs"))
+        for bug in self:
+            bug.save()
+
+    def _get_settings(self, settings_path):
+        try:
+            settings = mapfile.map_load(settings_path)
+        except mapfile.NoSuchFile:
+            settings = {"rcs_name": "None"}
+        return settings
+
+    def _save_settings(self, settings_path, settings):
+        try:
+            mapfile.map_save(self.rcs, settings_path, settings)
+        except PathNotInRoot, e:
+            # Handling duplicate bugdir settings, special case
+            none_rcs = rcs_by_name("None")
+            none_rcs.root(settings_path)
+            mapfile.map_save(none_rcs, settings_path, settings)
+
     def duplicate_bugdir(self, revision):
-        return BugDir(bugdir_root(self.rcs.duplicate_repo(revision)))
+        duplicate_path = self.rcs.duplicate_repo(revision)
 
-    def remove_duplicate_bugdir(self):
-        self.rcs.remove_duplicate_repo()
+        # setup revision RCS as None, since the duplicate may not be versioned
+        duplicate_settings_path = os.path.join(duplicate_path, ".be", "settings")
+        duplicate_settings = self._get_settings(duplicate_settings_path)
+        if "rcs_name" in duplicate_settings:
+            duplicate_settings["rcs_name"] = "None"
+            self._save_settings(duplicate_settings_path, duplicate_settings)
 
-    def list(self):
-        for uuid in self.list_uuids():
-            yield self.get_bug(uuid)
+        return BugDir(duplicate_path, loadNow=True)
 
-    def bug_map(self):
-        bugs = {}
-        for bug in self.list():
-            bugs[bug.uuid] = bug
-        return bugs
+    def remove_duplicate_bugdir(self):
+        self.rcs.remove_duplicate_repo()
 
-    def get_bug(self, uuid):
-        return Bug(self.bugs_path, uuid, self.rcs, self)
+    def _bug_map_gen(self):
+        map = {}
+        for bug in self:
+            map[bug.uuid] = bug
+        self.bug_map = map
 
     def list_uuids(self):
-        for uuid in os.listdir(self.bugs_path):
+        for uuid in os.listdir(self.get_path("bugs")):
             if (uuid.startswith('.')):
                 continue
             yield uuid
 
-    def new_bug(self, uuid=None):
-        if uuid is None:
-            uuid = names.uuid()
-        path = os.path.join(self.bugs_path, uuid)
-        self.rcs.mkdir(path)
-        bug = Bug(self.bugs_path, None, self.rcs, self)
-        bug.uuid = uuid
-        bug.creator = self.rcs.get_user_id()
-        bug.severity = "minor"
-        bug.status = "open"
-        bug.time = time.time()
-        return bug
+    def _clear_bugs(self):
+        while len(self) > 0:
+            self.pop()
+
+    def _load_bug(self, uuid):
+        bg = bug.Bug(bugdir=self, uuid=uuid, loadNow=True)
+        self.append(bg)
+        self._bug_map_gen()
+        return bg
+
+    def new_bug(self, uuid=None, summary=None):
+        bg = bug.Bug(bugdir=self, uuid=uuid, summary=summary)
+        self.append(bg)
+        self._bug_map_gen()
+        return bg
+
+    def remove_bug(self, bug):
+        self.remove(bug)
+        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 = simple_bug_dir()
+        >>> bug_a = bd.bug_from_shortname('a')
+        >>> print type(bug_a)
+        <class 'libbe.bug.Bug'>
+        >>> print bug_a
+        a:om: Bug A
+        """
+        matches = []
+        for bug in self:
+            if bug.uuid.startswith(shortname):
+                matches.append(bug)
+        if len(matches) > 1:
+            raise cmdutil.UserError("More than one bug matches %s.  Please be more"
+                                    " specific." % shortname)
+        if len(matches) == 1:
+            return matches[0]
+        raise KeyError("No bug matches %s" % shortname)
+
+    def bug_from_uuid(self, uuid):
+        if uuid not in self.bug_map:
+            self._bug_map_gen()
+            if uuid not in self.bug_map:
+                raise KeyError("No bug matches %s" % uuid +str(self.bug_map)+str(self))
+        return self.bug_map[uuid]
 
-class InvalidValue(ValueError):
-    def __init__(self, name, value):
-        msg = "Cannot assign value %s to %s" % (value, name)
-        Exception.__init__(self, msg)
-        self.name = name
-        self.value = value
 
 def simple_bug_dir():
     """
@@ -218,18 +309,17 @@ def simple_bug_dir():
     ['a', 'b']
     """
     dir = utility.Dir()
-    rcs = installed_rcs()
-    rcs.init(dir.path)
     assert os.path.exists(dir.path)
-    bugdir = create_bug_dir(dir.path, rcs)
+    bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True)
     bugdir._dir_ref = dir # postpone cleanup since dir.__del__() removes dir.
-    bug_a = bugdir.new_bug("a")
-    bug_a.summary = "Bug A"
-    bug_a.save()
-    bug_b = bugdir.new_bug("b")
+    bug_a = bugdir.new_bug("a", summary="Bug A")
+    bug_a.creator = "John Doe <jdoe@example.com>"
+    bug_a.time = 0
+    bug_b = bugdir.new_bug("b", summary="Bug B")
+    bug_b.creator = "Jane Doe <jdoe@example.com>"
+    bug_b.time = 0
     bug_b.status = "closed"
-    bug_b.summary = "Bug B"
-    bug_b.save()
+    bugdir.save()
     return bugdir
 
 
@@ -238,9 +328,8 @@ class BugDirTestCase(unittest.TestCase):
         unittest.TestCase.__init__(self, *args, **kwargs)
     def setUp(self):
         self.dir = utility.Dir()
-        self.rcs = installed_rcs()
-        self.rcs.init(self.dir.path)
-        self.bugdir = create_bug_dir(self.dir.path, self.rcs)
+        self.bugdir = BugDir(self.dir.path, sink_to_existing_root=False, allow_rcs_init=True)
+        self.rcs = self.bugdir.rcs
     def tearDown(self):
         del(self.rcs)
         del(self.dir)
@@ -250,9 +339,8 @@ class BugDirTestCase(unittest.TestCase):
         fullpath = self.fullPath(path)
         self.failUnless(os.path.exists(fullpath)==True,
                         "path %s does not exist" % fullpath)
-    def testBugDirDuplicate(self):
-        self.assertRaises(AlreadyInitialized, create_bug_dir,
-                          self.dir.path, self.rcs)
+        self.assertRaises(AlreadyInitialized, BugDir,
+                          self.dir.path, assertNewBugDir=True)
 
 unitsuite = unittest.TestLoader().loadTestsFromTestCase(BugDirTestCase)
 suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])
index 62a0c7cb7a1bdc486dd0ff36bc4988319c66e5f3..55a7a721908d3a911ee0374c637cec527a615070 100644 (file)
 #    You should have received a copy of the GNU General Public License
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-import bugdir
-import plugin
-import locale
-import os
 import optparse
+import os
+import locale
 from textwrap import TextWrapper
 from StringIO import StringIO
-import utility
 import doctest
 
+import bugdir
+import plugin
+import utility
+
 class UserError(Exception):
     def __init__(self, msg):
         Exception.__init__(self, msg)
@@ -33,40 +34,6 @@ class UserErrorWrap(UserError):
         UserError.__init__(self, str(exception))
         self.exception = exception
 
-def get_bug(spec, bug_dir=None):
-    """
-    >>> bd = bugdir.simple_bug_dir()
-    >>> bug_a = get_bug('a', bd)
-    >>> print type(bug_a)
-    <class 'libbe.bug.Bug'>
-    >>> print bug_a
-    a:om: Bug A
-    >>> print bd.get_bug('a')
-    a:om: Bug A
-    >>> bug_a == bd.get_bug('a')
-    True
-    """
-    matches = []
-    try:
-        if bug_dir is None:
-            bug_dir = bugdir.tree_root('.')
-    except bugdir.NoBugDir, e:
-        raise UserErrorWrap(e)
-    bugs = list(bug_dir.list())
-    for bug in bugs:
-        if bug.uuid.startswith(spec):
-            matches.append(bug)
-    if len(matches) > 1:
-        raise UserError("More than one bug matches %s.  Please be more"
-                        " specific." % spec)
-    if len(matches) == 1:
-        return matches[0]
-        
-    matches = []
-    if len(matches) == 0:
-        raise UserError("No bug matches %s" % spec)
-    return matches[0]
-
 def iter_commands():
     for name, module in plugin.iter_plugins("becommands"):
         yield name.replace("_", "-"), module
@@ -115,34 +82,6 @@ class UsageError(Exception):
 def raise_get_help(option, opt, value, parser):
     raise GetHelp
 
-
-def iter_comment_name(bug, unique_name):
-    """Iterate through id, comment pairs, in date order.
-    (This is a user-friendly id, not the comment uuid)
-    """
-    def key(comment):
-        return comment.time
-    for num, comment in enumerate(sorted(bug.list_comments(), key=key)):
-        yield ("%s:%d" % (unique_name, num+1), comment)
-
-
-def comment_from_name(bug, unique_name, name):
-    """Use a comment name to look up a comment"""
-    for cur_name, comment in iter_comment_name(bug, unique_name):
-        if name == cur_name:
-            return comment
-    raise KeyError(name)
-
-
-def get_bug_and_comment(identifier, bug_dir=None):
-    ids = identifier.split(':')
-    bug = get_bug(ids[0], bug_dir)
-    if len(ids) == 2:
-        comment = comment_from_name(bug, ids[0], identifier)
-    else:
-        comment = None
-    return bug, comment
-
         
 class CmdOptionParser(optparse.OptionParser):
     def __init__(self, usage):
@@ -174,44 +113,6 @@ def underlined(instring):
     return "%s\n%s" % (instring, "="*len(instring))
 
 
-def print_threaded_comments(comments, name_map, indent=""):
-    """Print a threaded display of comments"""
-    tw = TextWrapper(initial_indent = indent, subsequent_indent = indent, 
-                     width=80)
-    for comment, children in comments:
-        s = StringIO()
-        print >> s, "--------- Comment ---------"
-        print >> s, "Name: %s" % name_map[comment.uuid]
-        print >> s, "From: %s" % comment.From
-        print >> s, "Date: %s\n" % utility.time_to_str(comment.time)
-        print >> s, comment.body.rstrip('\n')
-
-        s.seek(0)
-        for line in s:
-            print tw.fill(line).rstrip('\n')
-        print_threaded_comments(children, name_map, indent=indent+"    ")
-
-
-def bug_tree(dir=None):
-    """Retrieve the bug tree specified by the user.  If no directory is
-    specified, the current working directory is used.
-
-    :param dir: The directory to search for the bug tree in.
-
-    >>> bug_tree() is not None
-    True
-    >>> bug_tree("/")
-    Traceback (most recent call last):
-    UserErrorWrap: The directory "/" has no bug directory.
-    """
-    if dir is None:
-        dir = os.getcwd()
-    try:
-        return bugdir.tree_root(dir)
-    except bugdir.NoBugDir, e:
-        raise UserErrorWrap(e)
-
-
 def _test():
     import doctest
     import sys
index 9fa3816072514cfcad6df54928f81e4b1d463ea3..95d5607e91cc133945566c107b8b8176f382dbea 100644 (file)
@@ -20,33 +20,24 @@ from libbe.utility import time_to_str
 from libbe.bug import cmp_severity
 import doctest
 
-def diff(old_tree, new_tree):
-    old_bug_map = old_tree.bug_map()
-    new_bug_map = new_tree.bug_map()
+def diff(old_bugdir, new_bugdir):
     added = []
     removed = []
     modified = []
-    for old_bug in old_bug_map.itervalues():
-        new_bug = new_bug_map.get(old_bug.uuid)
+    for old_bug in old_bugdir:
+        new_bug = new_bugdir.bug_map.get(old_bug.uuid)
         if new_bug is None :
             removed.append(old_bug)
         else:
             if old_bug != new_bug:
                 modified.append((old_bug, new_bug))
-    for new_bug in new_bug_map.itervalues():
-        if not old_bug_map.has_key(new_bug.uuid):
+    for new_bug in new_bugdir:
+        if not old_bugdir.bug_map.has_key(new_bug.uuid):
             added.append(new_bug)
     return (removed, modified, added)
 
-
-def reference_diff(bugdir, revision=None):
-    d = diff(bugdir.duplicate_bugdir(revision), bugdir)
-    bugdir.remove_duplicate_bugdir()
-    return d
-
 def diff_report(diff_data, bug_dir):
     (removed, modified, added) = diff_data
-    bugs = list(bug_dir.list())
     def modified_cmp(left, right):
         return cmp_severity(left[1], right[1])
 
@@ -54,7 +45,7 @@ def diff_report(diff_data, bug_dir):
     removed.sort(cmp_severity)
     modified.sort(modified_cmp)
 
-    if len(added) > 0: 
+    if len(added) > 0:
         print "New bug reports:"
         for bug in added:
             print bug.string(shortlist=True)
@@ -62,7 +53,7 @@ def diff_report(diff_data, bug_dir):
     if len(modified) > 0:
         printed = False
         for old_bug, new_bug in modified:
-            change_str = bug_changes(old_bug, new_bug, bugs)
+            change_str = bug_changes(old_bug, new_bug, bug_dir)
             if change_str is None:
                 continue
             if not printed:
@@ -73,7 +64,7 @@ def diff_report(diff_data, bug_dir):
     if len(removed) > 0: 
         print "Removed bug reports:"
         for bug in removed:
-            print bug.string(bugs, shortlist=True)
+            print bug.string(shortlist=True)
    
 def change_lines(old, new, attributes):
     change_list = []    
@@ -91,8 +82,8 @@ def bug_changes(old, new, bugs):
     change_list = change_lines(old, new, ("time", "creator", "severity",
     "target", "summary", "status", "assigned"))
 
-    old_comment_ids = list(old.iter_comment_ids())
-    new_comment_ids = list(new.iter_comment_ids())
+    old_comment_ids = [c.uuid for c in old.comment_root.traverse()]
+    new_comment_ids = [c.uuid for c in new.comment_root.traverse()]
     change_strings = ["%s: %s -> %s" % f for f in change_list]
     for comment_id in new_comment_ids:
         if comment_id not in old_comment_ids:
@@ -105,8 +96,8 @@ def bug_changes(old, new, bugs):
 
     if len(change_strings) == 0:
         return None
-    return "%s%s\n" % (new.string(bugs, shortlist=True), 
-                       "\n".join(change_strings))
+    return "%s\n  %s" % (new.string(shortlist=True),
+                         "  \n".join(change_strings))
 
 
 def comment_summary(comment, status):
index 8f69554af898cd5b4a1c305ee8fab08c34613a57..9a7fa8b85a15f1e3126700611f0e5e03f3f1fb49 100644 (file)
@@ -95,11 +95,11 @@ def parse(f):
     f = utility.get_file(f)
     result = {}
     for line in f:
-        line = line.rstrip('\n')
+        line = line.decode("utf-8").rstrip('\n')
         if len(line) == 0:
             continue
-        name,value = [f.decode('utf-8') for f in line.split('=', 1)]
-        assert not result.has_key('name')
+        name,value = [f for f in line.split('=', 1)]
+        assert not result.has_key(name)
         result[name] = value
     return result
 
diff --git a/libbe/names.py b/libbe/names.py
deleted file mode 100644 (file)
index 6e0378e..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2005 Aaron Bentley and Panometrics, Inc.
-# <abentley@panoramicfeedback.com>
-#
-#    This program is free software; you can redistribute it and/or modify
-#    it under the terms of the GNU General Public License as published by
-#    the Free Software Foundation; either version 2 of the License, or
-#    (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#
-#    You should have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-import os
-import sys
-import doctest
-
-def uuid():
-    # this code borrowed from standard commands module
-    # but adapted to win32
-    pipe = os.popen('uuidgen', 'r')
-    text = pipe.read()
-    sts = pipe.close()
-    if sts not in (0, None):
-        raise "Failed to run uuidgen"
-    if text[-1:] == '\n': text = text[:-1]
-    return text
-
-def unique_name(bug, bugs):
-    """
-    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 some_bug in bugs:
-        if bug.uuid == some_bug.uuid:
-            continue
-        while (bug.uuid[:chars] == some_bug.uuid[:chars]):
-            chars+=1
-    return bug.uuid[:chars]
-
-suite = doctest.DocTestSuite()
index 05a43982759f2fb909bdf5120b8951fe517e990d..0964fba01a4aa3aaca41e2d547f2ccbd2cf77fd8 100644 (file)
@@ -36,6 +36,8 @@ def iter_plugins(prefix):
     modfiles = os.listdir(os.path.join(plugin_path, prefix))
     modfiles.sort()
     for modfile in modfiles:
+        if modfile.startswith('.'):
+            continue # the occasional emacs temporary file
         if modfile.endswith(".py") and modfile != "__init__.py":
             yield modfile[:-3], my_import(prefix+"."+modfile[:-3])
 
index 2993a808ddb2ff675698858ddba5a0a312961559..abd92cbfb1e7d5297783541637307fbdc28da459 100644 (file)
@@ -24,7 +24,7 @@ import tempfile
 import shutil
 import unittest
 import doctest
-from utility import Dir
+from utility import Dir, search_parent_directories
 
 def _get_matching_rcs(matchfn):
     """Return the first module for which matchfn(RCS_instance) is true"""
@@ -32,9 +32,9 @@ def _get_matching_rcs(matchfn):
     import bzr
     import hg
     import git
-    for module in [arch, bzr, hg, git]:
+    for module in [git, arch, bzr, hg, git]:
         rcs = module.new()
-        if matchfn(rcs):
+        if matchfn(rcs) == True:
             return rcs
         else:
             del(rcs)
@@ -62,6 +62,9 @@ class CommandError(Exception):
 class SettingIDnotSupported(NotImplementedError):
     pass
 
+class PathNotInRoot(Exception):
+    pass
+
 def new():
     return RCS()
 
@@ -152,7 +155,10 @@ class RCS(object):
         pass
     def _rcs_get_file_contents(self, path, revision=None):
         """
-        Get the file as it was in a given revision.
+        Get the file contents as they were in a given revision.  Don't
+        worry about decoding the contents, the RCS.get_file_contents()
+        method will handle that.
+        
         Revision==None specifies the current revision.
         """
         assert revision == None, \
@@ -180,7 +186,7 @@ class RCS(object):
             if e.errno == errno.ENOENT:
                 return False
             raise e
-    def detect(self, path=None):
+    def detect(self, path="."):
         """
         Detect whether a directory is revision controlled with this RCS.
         """
@@ -264,23 +270,28 @@ class RCS(object):
         Revision==None specifies the current revision.
         """
         relpath = self._u_rel_path(path)
-        return self._rcs_get_file_contents(relpath, revision)
+        return self._rcs_get_file_contents(relpath, revision).decode("utf-8")
     def set_file_contents(self, path, contents):
         """
         Set the file contents under version control.
         """
         add = not os.path.exists(path)
-        file(path, "wb").write(contents)
+        file(path, "wb").write(contents.encode("utf-8"))
         if add:
             self.add(path)
         else:
             self.update(path)
     def mkdir(self, path):
         """
-        Created directory at path under version control.
+        Create (if neccessary) a directory at path under version
+        control.
         """
-        os.mkdir(path)
-        self.add(path)
+        if not os.path.exists(path):
+            os.mkdir(path)
+            self.add(path)
+        else:
+            assert os.path.isdir(path)
+            self.update(path)
     def duplicate_repo(self, revision=None):
         """
         Get the repository as it was in a given revision.
@@ -366,16 +377,7 @@ class RCS(object):
           /.be
         or None if none of those files exist.
         """
-        path = os.path.realpath(path)
-        assert os.path.exists(path)
-        old_path = None
-        while True:
-            if os.path.exists(os.path.join(path, filename)):
-                return os.path.join(path, filename)
-            if path == old_path:
-                return None
-            old_path = path
-            path = os.path.dirname(path)
+        return search_parent_directories(path, filename)
     def _u_rel_path(self, path, root=None):
         """
         Return the relative path to path from root.
@@ -389,8 +391,9 @@ class RCS(object):
         if os.path.isabs(path):
             absRoot = os.path.abspath(root)
             absRootSlashedDir = os.path.join(absRoot,"")
-            assert path.startswith(absRootSlashedDir), \
-                "file %s not in root %s" % (path, absRootSlashedDir)
+            if not path.startswith(absRootSlashedDir):
+                raise PathNotInRoot, \
+                    "file %s not in root %s" % (path, absRootSlashedDir)
             assert path != absRootSlashedDir, \
                 "file %s == root directory %s" % (path, absRootSlashedDir)
             path = path[len(absRootSlashedDir):]
index f595bdbb4844792c348ea806b036c0506e1df99d..81023cd8ba64b5a38c9eb821ab8565039cf85db6 100644 (file)
@@ -71,6 +71,32 @@ def get_file(f):
     else:
         return f
 
+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
+    /a/b/c/.be
+    /a/b/.be
+    /a/.be
+    /.be
+    or None if none of those files exist.
+    """
+    path = os.path.realpath(path)
+    assert os.path.exists(path)
+    old_path = None
+    while True:
+        check_path = os.path.join(path, filename)
+        if os.path.exists(check_path):
+            return check_path
+        if path == old_path:
+            return None
+        old_path = path
+        path = os.path.dirname(path)
+
 class Dir:
     "A temporary directory for testing use"
     def __init__(self):
diff --git a/test.py b/test.py
index 9af153b7ffc7a5a444a8291a6b50d7768be5c93f..bf57d1ec0d015083586432d3b89e133d8e7560e6 100644 (file)
--- a/test.py
+++ b/test.py
@@ -1,11 +1,11 @@
-"""Usage: python test.py [module]
+"""Usage: python test.py [module(s) ...]
 
-When called without an optional module name, run the doctests from
-*all* modules.  This may raise lots of errors if you haven't installed
-one of the versioning control systems.
+When called without optional module names, run the doctests from *all*
+modules.  This may raise lots of errors if you haven't installed one
+of the versioning control systems.
 
-When called with an optional module name, only run the doctests from
-that module.
+When called with module name arguments, only run the doctests from
+those modules.
 """
 
 from libbe import plugin
@@ -16,19 +16,19 @@ import sys
 suite = unittest.TestSuite()
 
 if len(sys.argv) > 1:
-    submodname = sys.argv[1]
-    match = False
-    mod = plugin.get_plugin("libbe", submodname)
-    if mod is not None and hasattr(mod, "suite"):
-        suite.addTest(mod.suite)
-        match = True
-    mod = plugin.get_plugin("becommands", submodname)
-    if mod is not None:
-        suite.addTest(doctest.DocTestSuite(mod))
-        match = True
-    if not match:
-        print "No modules match \"%s\"" % submodname
-        sys.exit(1)
+    for submodname in sys.argv[1:]:
+        match = False
+        mod = plugin.get_plugin("libbe", submodname)
+        if mod is not None and hasattr(mod, "suite"):
+            suite.addTest(mod.suite)
+            match = True
+        mod = plugin.get_plugin("becommands", submodname)
+        if mod is not None:
+            suite.addTest(doctest.DocTestSuite(mod))
+            match = True
+        if not match:
+            print "No modules match \"%s\"" % submodname
+            sys.exit(1)
 else:
     failed = False
     for modname,module in plugin.iter_plugins("libbe"):