Rev 36: We can take a lock, and figure out the state change. in lp:~jameinel/+junk/file_locking

John Arbash Meinel john at arbash-meinel.com
Wed Sep 23 05:20:25 BST 2009


At lp:~jameinel/+junk/file_locking

------------------------------------------------------------
revno: 36
revision-id: john at arbash-meinel.com-20090923042011-2yf08hrrtsown8dk
parent: john at arbash-meinel.com-20090923031025-29l2sznhmmp0akvb
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: file_locking
timestamp: Tue 2009-09-22 23:20:11 -0500
message:
  We can take a lock, and figure out the state change.
  
  Note that locking isn't saved yet, and the serializer doesn't support
  *reading* active locks... :)
-------------- next part --------------
=== modified file 'file_lock.py'
--- a/file_lock.py	2009-09-23 03:07:03 +0000
+++ b/file_lock.py	2009-09-23 04:20:11 +0000
@@ -151,7 +151,7 @@
             tracked_ids = []
         else:
             tracked_ids = stanza.get_all('t')
-        return FileLockingInfo(tracked_ids, [], logger)
+        return FileLockingInfo(tracked_ids, {}, logger)
 
     def to_bytes(self, locking_info):
         content = [self._HEADER, '\n']
@@ -356,7 +356,7 @@
                 'This is a Bazaar File Locking Lock Store\n'
                 'You should not need to modify any files in this directory.\n')
             logger = FileLockLogger.initialize(transport, 'log')
-            locking_info = FileLockingInfo([], [], logger)
+            locking_info = FileLockingInfo([], {}, logger)
             # Instead of having a separate 'format' file, we put the data into
             # the 'info' file. Which means that we have 1 less object to read
             # from remote.
@@ -500,6 +500,34 @@
                 trackers[path] = None
         return trackers
 
+    def get_path_status(self, path):
+        """Figure out what the status is for a given path.
+
+        :param path: A path to analyze
+        :return: Several possibilities
+            'untracked' This path nor its parents are not tracked
+            'tracked' (at)  This path or its parent is tracked, but not
+                            currently locked
+            'remote locked' Someone else holds a lock that applies to this path
+            'local locked'  We hold a lock that applies to this path
+        """
+        # TODO: this function should really support multiple paths at a time
+        # TODO: We probably want to return more info that just the basic status
+        #       for example, returning an identifier as to what places it is
+        #       tracked, or what lock is active for the path
+        tracked_at_path = self.tracked_at([path])[path]
+        if tracked_at_path is None:
+            return 'untracked'
+        ls = self._get_lock_store()
+        assert ls is not None
+        # TODO: This is double handling. tracked_at() already had to do path =>
+        #       file_id conversions to figure out what is where, we shouldn't
+        #       have to do it again here
+        file_id = self._wt.path2id(tracked_at_path)
+        if file_id in ls._locking_info._active_locks:
+            return 'locked'
+        return 'tracked'
+
     def is_locked(self, path):
         """Is the given path already locked?
 
@@ -513,6 +541,19 @@
     def take_lock(self, path):
         """Take the lock for the given path."""
         file_id = self._wt.path2id(path)
+        if file_id is None:
+            raise ValueError('Cannot take a lock on an unversioned path.')
+        ls = self._get_lock_store()
+        if ls is None:
+            raise ValueError('Cannot take a lock when no lock store has been'
+                             ' configured')
+        if file_id not in ls._locking_info._tracked_ids:
+            # TODO: Consider giving a hint exception in the case that the user
+            #       gives 'foo/bar' and 'foo' is tracked but 'foo/bar' is not
+            #       We could go through the tracked_at functionality to check
+            #       this
+            raise ValueError('path %s is not one of the tracked locations.')
+        ls._locking_info.create_lock(file_id)
 
     def steal_lock(self, path):
         """There is a lock from someone else on the given path, take it."""

=== modified file 'tests/test_file_lock.py'
--- a/tests/test_file_lock.py	2009-09-23 03:10:25 +0000
+++ b/tests/test_file_lock.py	2009-09-23 04:20:11 +0000
@@ -213,7 +213,7 @@
 
 # TODO: Currently MemoryTree doesn't provide a .bzr/checkout for us to put our
 #       held-lock info... :( As such, we can't use TestCaseWithMemoryTransport
-class TestFileLockManager(tests.TestCaseWithMemoryTransport):
+class TestCaseWithManager(tests.TestCaseWithMemoryTransport):
 
     def make_tree_and_manager(self, path):
         tree = self.make_branch_and_memory_tree(path)
@@ -224,7 +224,7 @@
         return tree, manager
 
     def make_simple_tree_and_manager(self, path):
-        tree, manager = self.make_tree_and_manager('tree')
+        tree, manager = self.make_tree_and_manager(path)
         tree.mkdir('adir', 'dir-id')
         tree.add(['afile', 'adir/subfile'],
                  ['file-id', 'subfile-id'],
@@ -233,6 +233,16 @@
         tree.put_file_bytes_non_atomic('subfile-id', 'content')
         return tree, manager
 
+    def make_dir_tracked(self, path):
+        tree, manager = self.make_simple_tree_and_manager(path)
+        manager.initialize_lock_store(self.get_transport('lock-store'))
+        manager.track_paths(['adir'])
+        return tree, manager
+
+
+
+class TestFileLockManager(TestCaseWithManager):
+
     def test_already_initialized(self):
         tree, manager = self.make_tree_and_manager('.')
         manager.initialize_lock_store(self.get_transport('lock-store'))
@@ -363,6 +373,47 @@
                             ['afile', 'adir', 'adir/subfile',
                             'adir/not-versioned', 'not-tracked']))
 
+    def test_take_lock_no_store(self):
+        tree, manager = self.make_simple_tree_and_manager('tree')
+        self.assertRaises(ValueError, manager.take_lock, 'adir')
+
+    def test_take_lock_not_versioned(self):
+        tree, manager = self.make_dir_tracked('tree')
+        self.assertRaises(ValueError, manager.take_lock, 'unknown')
+
+    def test_take_lock_not_tracked(self):
+        tree, manager = self.make_dir_tracked('tree')
+        self.assertRaises(ValueError, manager.take_lock, 'afile')
+
+
+class TestGetPathStatus(TestCaseWithManager):
+
+    def make_dir_locked(self):
+        tree, manager = self.make_dir_tracked('tree')
+        manager.take_lock('adir')
+        return tree, manager
+
+    def test_untracked_no_lock_store(self):
+        tree, manager = self.make_simple_tree_and_manager('tree')
+        self.assertEqual('untracked', manager.get_path_status('afile'))
+        self.assertEqual('untracked', manager.get_path_status('unknown'))
+
+    def test_untracked_outside_set(self):
+        tree, manager = self.make_dir_tracked('tree')
+        self.assertEqual('untracked', manager.get_path_status('afile'))
+        self.assertEqual('untracked', manager.get_path_status('unknown'))
+
+    def test_tracked_not_locked(self):
+        tree, manager = self.make_dir_tracked('tree')
+        self.assertEqual('tracked', manager.get_path_status('adir'))
+        self.assertEqual('tracked', manager.get_path_status('adir/subfile'))
+        self.assertEqual('tracked', manager.get_path_status('adir/unknown'))
+
+    def test_locked(self):
+        tree, manager = self.make_dir_locked()
+        self.assertEqual('locked', manager.get_path_status('adir'))
+        self.assertEqual('locked', manager.get_path_status('adir/subfile'))
+
 
 class TestFileLockStore(tests.TestCaseWithMemoryTransport):
 



More information about the bazaar-commits mailing list