aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/body29
-rw-r--r--.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/values21
-rw-r--r--.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/values35
-rw-r--r--libbe/arch.py29
-rw-r--r--libbe/bug.py2
-rw-r--r--libbe/bugdir.py113
-rw-r--r--libbe/rcs.py16
7 files changed, 211 insertions, 34 deletions
diff --git a/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/body b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/body
new file mode 100644
index 0000000..dfcf82c
--- /dev/null
+++ b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/body
@@ -0,0 +1,29 @@
+I was having problems with `python test.py bugdir` with the Arch
+backend. Commits were failing with `archive not registered'.
+
+Adding some trace information to arch.Arch._rcs_init() and
+._rcs_cleanup() (the traceback module is great :p), I found
+that the problem was coming from bugdir.BugDir.guess_rcs().
+
+The Arch backend deletes any auto-created archives when it is cleaned
+up (RCS.__del__ -> RCS.cleanup -> Arch._rcs_cleanup). This means that
+whatever instance is used to init the archive in guess_rcs() must be
+kept around. I had been doing:
+ * installed_rcs() -> Arch-instance-A
+ * Arch-instance-A.init()
+ * store Arch-instnance-A.name as bugdir.rcs_name
+ * future calls to bugdir.rcs get new instance Arch-instance-B
+ * eventually Arch-instance-A cleaned up
+ * archive dissapears & tests crash
+
+I switched things around so .rcs is the `master attribute' and
+.rcs_name follows it. Now just save whichever rcs you used to init
+your archive as .rcs.
+
+In order to implement the fix, I had to tweak the memory/file-system
+interaction a bit. Instead of saving the settings *every*time* a
+setting_property changed, we now save only if the .be file exists.
+This file serves as a 'file-system-bugdir-active' flag. Before it is
+created (e.g., by a .save()), the BugDir lives purely in memory, and
+can freely go about configuring .rcs, .rcs_name, etc until it get's
+to the point where it's ready to go to disk.
diff --git a/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/values b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/values
new file mode 100644
index 0000000..b19c065
--- /dev/null
+++ b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/comments/8d927822-eff9-42c4-9541-8b784b3f7db2/values
@@ -0,0 +1,21 @@
+
+
+
+Content-type=text/plain
+
+
+
+
+
+
+Date=Sat, 22 Nov 2008 18:53:20 +0000
+
+
+
+
+
+
+From=W. Trevor King <wking@drexel.edu>
+
+
+
diff --git a/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/values b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/values
new file mode 100644
index 0000000..96c0708
--- /dev/null
+++ b/.be/bugs/496edad5-1484-413a-bc68-4b01274a65eb/values
@@ -0,0 +1,35 @@
+
+
+
+creator=W. Trevor King <wking@drexel.edu>
+
+
+
+
+
+
+severity=minor
+
+
+
+
+
+
+status=fixed
+
+
+
+
+
+
+summary=Early del-cleanup with Arch backend
+
+
+
+
+
+
+time=Sat, 22 Nov 2008 18:38:32 +0000
+
+
+
diff --git a/libbe/arch.py b/libbe/arch.py
index b35a897..6415cef 100644
--- a/libbe/arch.py
+++ b/libbe/arch.py
@@ -21,7 +21,11 @@ import re
import unittest
import doctest
+import traceback
+import sys
+
import config
+from beuuid import uuid_gen
from rcs import RCS, RCStestCase, CommandError
client = config.get_val("arch_client")
@@ -54,6 +58,10 @@ class Arch(RCS):
self._create_archive(path)
self._create_project(path)
self._add_project_code(path)
+ #print "RCSid:", id(self), "init", self._archive_project_name()
+ #print "BEGIN_TRACE"
+ #traceback.print_stack(file=sys.stdout)
+ #print "END_TRACE"
def _create_archive(self, path):
# Create a new archive
# http://regexps.srparish.net/tutorial-tla/new-archive.html#Creating_a_New_Archive
@@ -62,13 +70,13 @@ class Arch(RCS):
name, email = self._u_parse_id(id)
if email == None:
email = "%s@example.com" % name
- trailer = "%s-%s" % ("bugs-everywhere-auto",
- time.strftime("%Y.%H.%M.%S"))
+ trailer = "%s-%s" % ("bugs-everywhere-auto", uuid_gen()[0:8])
self._archive_name = "%s--%s" % (email, trailer)
self._archive_dir = "/tmp/%s" % trailer
self._tmp_archive = True
self._u_invoke_client("make-archive", self._archive_name,
self._archive_dir, directory=path)
+ self._u_invoke_client("archives")
def _invoke_client(self, *args, **kwargs):
"""
Invoke the client on our archive.
@@ -145,11 +153,16 @@ class Arch(RCS):
self._invoke_client("import", "--summary", "Began versioning",
directory=path)
def _rcs_cleanup(self):
+ #print "RCSid:", id(self), "cleaned", self._archive_project_name()
+ #print "BEGIN_TRACE"
+ #traceback.print_stack(file=sys.stdout)
+ #print "END_TRACE"
if self._tmp_project == True:
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)
@@ -185,6 +198,7 @@ class Arch(RCS):
def _rcs_get_user_id(self):
try:
+ self._u_invoke_client("archives")
status,output,error = self._u_invoke_client('my-id')
return output.rstrip('\n')
except Exception, e:
@@ -195,11 +209,13 @@ class Arch(RCS):
def _rcs_set_user_id(self, value):
self._u_invoke_client('my-id', value)
def _rcs_add(self, path):
+ self._u_invoke_client("archives")
self._u_invoke_client("add-id", path)
realpath = os.path.realpath(self._u_abspath(path))
pathAdded = realpath in self._list_added(self.rootdir)
if self.paranoid and not pathAdded:
self._force_source(path)
+ self._u_invoke_client("archives")
def _list_added(self, root):
assert os.path.exists(root)
assert os.access(root, os.X_OK)
@@ -243,13 +259,16 @@ class Arch(RCS):
def _rcs_commit(self, commitfile):
summary,body = self._u_parse_commitfile(commitfile)
#status,output,error = self._invoke_client("make-log")
+ self._u_invoke_client("tree-root")
+ self._u_invoke_client("tree-version")
+ self._u_invoke_client("archives")
if body == None:
status,output,error \
- = self._invoke_client("commit","--summary",summary)
+ = self._u_invoke_client("commit","--summary",summary)
else:
status,output,error \
- = self._invoke_client("commit","--summary",summary,
- "--log-message",body)
+ = self._u_invoke_client("commit","--summary",summary,
+ "--log-message",body)
revision = None
revline = re.compile("[*] committed (.*)")
match = revline.search(output)
diff --git a/libbe/bug.py b/libbe/bug.py
index b1e8d26..6a9a589 100644
--- a/libbe/bug.py
+++ b/libbe/bug.py
@@ -119,7 +119,7 @@ class Bug(object):
self.status = "open"
self.severity = "minor"
self.assigned = None
- self.time = time.time()
+ self.time = int(time.time()) # only save to second precision
self.comment_root = comment.Comment(self, uuid=comment.INVALID_UUID)
def __repr__(self):
diff --git a/libbe/bugdir.py b/libbe/bugdir.py
index 6152e3f..a552b0f 100644
--- a/libbe/bugdir.py
+++ b/libbe/bugdir.py
@@ -56,14 +56,14 @@ class InvalidValue(ValueError):
TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n"
-def setting_property(name, valid=None):
+def setting_property(name, valid=None, doc=None):
def getter(self):
value = self.settings.get(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:
@@ -72,15 +72,29 @@ def setting_property(name, valid=None):
del self.settings[name]
else:
self.settings[name] = value
- self.save()
- return property(getter, setter)
+ self._save_settings(self.get_path("settings"), self.settings)
+
+ return property(getter, setter, doc=doc)
class BugDir (list):
+ """
+ File-system access:
+ When rooted in non-bugdir directory, BugDirs live completely in
+ memory until the first call to .save(). This creates a '.be'
+ sub-directory containing configurations options, bugs, comments,
+ etc. Once this sub-directory has been created (possibly by
+ another BugDir instance) any changes to the BugDir in memory will
+ be flushed to the file system automatically. However, the BugDir
+ will only load information from the file system when it loads new
+ bugs/comments that it doesn't already have in memory, or when it
+ explicitly asked to do so (e.g. .load() or __init__(loadNow=True)).
+ """
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)
+ self.settings = {}
if root == None:
root = os.getcwd()
if sink_to_existing_root == True:
@@ -92,13 +106,12 @@ class BugDir (list):
if loadNow == True:
self.load()
else:
- if assert_new_BugDir:
+ if assert_new_BugDir == True:
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
+ self.rcs = rcs
def find_root(self, path):
"""
@@ -132,21 +145,24 @@ class BugDir (list):
self.rcs.set_file_contents(self.get_path("version"), TREE_VERSION_STRING)
rcs_name = setting_property("rcs_name",
- ("None", "bzr", "git", "Arch", "hg"))
+ ("None", "bzr", "git", "Arch", "hg"),
+ doc="The name of the current RCS. Kept seperate to make saving/loading settings easy. Don't set this attribute. Set .rcs instead, and .rcs_name will be automatically adjusted.")
_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.root)
return self._rcs
- rcs = property(_get_rcs)
+ def _set_rcs(self, rcs):
+ if rcs == None:
+ rcs = rcs_by_name("None")
+ self._rcs = rcs
+ rcs.root(self.root)
+ self.rcs_name = rcs.name
- target = setting_property("target")
+ rcs = property(_get_rcs, _set_rcs, doc="A revision control system (RCS) instance")
+
+ target = setting_property("target", doc="The current project development target")
def get_path(self, *args):
my_dir = os.path.join(self.root, ".be")
@@ -160,12 +176,12 @@ class BugDir (list):
if not os.path.exists(deepdir):
deepdir = os.path.dirname(deepdir)
rcs = detect_rcs(deepdir)
+ install = False
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
+ self.rcs = rcs
return rcs
def load(self):
@@ -176,6 +192,7 @@ class BugDir (list):
if not os.path.exists(self.get_path()):
raise NoBugDir(self.get_path())
self.settings = self._get_settings(self.get_path("settings"))
+ self.rcs = rcs_by_name(self.rcs_name)
self._clear_bugs()
for uuid in self.list_uuids():
self._load_bug(uuid)
@@ -198,10 +215,17 @@ class BugDir (list):
return settings
def _save_settings(self, settings_path, settings):
+ if not os.path.exists(self.get_path()):
+ # don't save settings until the bug directory has been
+ # initialized. this initialization happens the first time
+ # a bug directory is saved (BugDir.save()). If the user
+ # is just working with a BugDir in memory, we don't want
+ # to go cluttering up his file system with settings files.
+ return
try:
mapfile.map_save(self.rcs, settings_path, settings)
except PathNotInRoot, e:
- # Handling duplicate bugdir settings, special case
+ # Special case for configuring duplicate bugdir settings
none_rcs = rcs_by_name("None")
none_rcs.root(settings_path)
mapfile.map_save(none_rcs, settings_path, settings)
@@ -209,7 +233,7 @@ class BugDir (list):
def duplicate_bugdir(self, revision):
duplicate_path = self.rcs.duplicate_repo(revision)
- # setup revision RCS as None, since the duplicate may not be versioned
+ # setup revision RCS as None, since the duplicate may not be initialized for versioning
duplicate_settings_path = os.path.join(duplicate_path, ".be", "settings")
duplicate_settings = self._get_settings(duplicate_settings_path)
if "rcs_name" in duplicate_settings:
@@ -228,10 +252,18 @@ class BugDir (list):
self.bug_map = map
def list_uuids(self):
- for uuid in os.listdir(self.get_path("bugs")):
- if (uuid.startswith('.')):
- continue
- yield uuid
+ uuids = []
+ if os.path.exists(self.get_path()):
+ # list the uuids on disk
+ for uuid in os.listdir(self.get_path("bugs")):
+ if not (uuid.startswith('.')):
+ uuids.append(uuid)
+ yield uuid
+ # and the ones that are still just in memory
+ for bug in self:
+ if bug.uuid not in uuids:
+ uuids.append(bug.uuid)
+ yield bug.uuid
def _clear_bugs(self):
while len(self) > 0:
@@ -341,6 +373,41 @@ class BugDirTestCase(unittest.TestCase):
"path %s does not exist" % fullpath)
self.assertRaises(AlreadyInitialized, BugDir,
self.dir.path, assertNewBugDir=True)
+ def versionTest(self):
+ if self.rcs.versioned == False:
+ return
+ original = self.bugdir.rcs.commit("Began versioning")
+ bugA = self.bugdir.bug_from_uuid("a")
+ bugA.status = "fixed"
+ self.bugdir.save()
+ new = self.rcs.commit("Fixed bug a")
+ dupdir = self.bugdir.duplicate_bugdir(original)
+ self.failUnless(dupdir.root != self.bugdir.root, "%s, %s" % (dupdir.root, self.bugdir.root))
+ bugAorig = dupdir.bug_from_uuid("a")
+ self.failUnless(bugA != bugAorig, "\n%s\n%s" % (bugA.string(), bugAorig.string()))
+ bugAorig.status = "fixed"
+ self.failUnless(bug.cmp_status(bugA, bugAorig)==0, "%s, %s" % (bugA.status, bugAorig.status))
+ self.failUnless(bug.cmp_severity(bugA, bugAorig)==0, "%s, %s" % (bugA.severity, bugAorig.severity))
+ self.failUnless(bug.cmp_assigned(bugA, bugAorig)==0, "%s, %s" % (bugA.assigned, bugAorig.assigned))
+ self.failUnless(bug.cmp_time(bugA, bugAorig)==0, "%s, %s" % (bugA.time, bugAorig.time))
+ self.failUnless(bug.cmp_creator(bugA, bugAorig)==0, "%s, %s" % (bugA.creator, bugAorig.creator))
+ self.failUnless(bugA == bugAorig, "\n%s\n%s" % (bugA.string(), bugAorig.string()))
+ self.bugdir.remove_duplicate_bugdir()
+ self.failUnless(os.path.exists(dupdir.root)==False, str(dupdir.root))
+ def testRun(self):
+ self.bugdir.new_bug(uuid="a", summary="Ant")
+ self.bugdir.new_bug(uuid="b", summary="Cockroach")
+ self.bugdir.new_bug(uuid="c", summary="Praying mantis")
+ length = len(self.bugdir)
+ self.failUnless(length == 3, "%d != 3 bugs" % length)
+ uuids = list(self.bugdir.list_uuids())
+ self.failUnless(len(uuids) == 3, "%d != 3 uuids" % len(uuids))
+ self.failUnless(uuids == ["a","b","c"], str(uuids))
+ bugA = self.bugdir.bug_from_uuid("a")
+ bugAprime = self.bugdir.bug_from_shortname("a")
+ self.failUnless(bugA == bugAprime, "%s != %s" % (bugA, bugAprime))
+ self.bugdir.save()
+ self.versionTest()
unitsuite = unittest.TestLoader().loadTestsFromTestCase(BugDirTestCase)
suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()])
diff --git a/libbe/rcs.py b/libbe/rcs.py
index abd92cb..10338b9 100644
--- a/libbe/rcs.py
+++ b/libbe/rcs.py
@@ -24,15 +24,18 @@ import tempfile
import shutil
import unittest
import doctest
+
+
from utility import Dir, search_parent_directories
+
def _get_matching_rcs(matchfn):
"""Return the first module for which matchfn(RCS_instance) is true"""
import arch
import bzr
import hg
import git
- for module in [git, arch, bzr, hg, git]:
+ for module in [arch, bzr, hg, git]:
rcs = module.new()
if matchfn(rcs) == True:
return rcs
@@ -70,7 +73,7 @@ def new():
class RCS(object):
"""
- Implement the 'no-rcs' interface.
+ This class implements a 'no-rcs' interface.
Support for other RCSs can be added by subclassing this class, and
overriding methods _rcs_*() with code appropriate for your RCS.
@@ -340,9 +343,9 @@ class RCS(object):
def _u_invoke(self, args, expect=(0,), cwd=None):
if cwd == None:
cwd = self.rootdir
+ if self.verboseInvoke == True:
+ print >> sys.stderr, "%s$ %s" % (cwd, " ".join(args))
try :
- if self.verboseInvoke == True:
- print "%s$ %s" % (cwd, " ".join(args))
if sys.platform != "win32":
q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd)
else:
@@ -354,8 +357,11 @@ class RCS(object):
raise CommandError(strerror, e.args[0])
output, error = q.communicate()
status = q.wait()
+ if self.verboseInvoke == True:
+ print >> sys.stderr, "%d\n%s%s" % (status, output, error)
if status not in expect:
- raise CommandError(error, status)
+ strerror = "%s\nwhile executing %s\n%s" % (args[1], args, error)
+ raise CommandError(strerror, status)
return status, output, error
def _u_invoke_client(self, *args, **kwargs):
directory = kwargs.get('directory',None)