dirnode: add get_child_and_metadata_at_path
authorBrian Warner <warner@allmydata.com>
Fri, 3 Oct 2008 00:52:03 +0000 (17:52 -0700)
committerBrian Warner <warner@allmydata.com>
Fri, 3 Oct 2008 00:52:03 +0000 (17:52 -0700)
src/allmydata/dirnode.py
src/allmydata/interfaces.py
src/allmydata/test/test_dirnode.py

index 58245100019d4d66c3fa168dfdaad12c531e3244..c1250479d2445f165629752db545f6f4607ebb77 100644 (file)
@@ -248,6 +248,12 @@ class NewDirectoryNode:
             raise KeyError(name)
         return child[0]
 
+    def _get_with_metadata(self, children, name):
+        child = children.get(name)
+        if child is None:
+            raise KeyError(name)
+        return child
+
     def get(self, name):
         """I return a Deferred that fires with the named child node,
         which is either an IFileNode or an IDirectoryNode."""
@@ -256,6 +262,15 @@ class NewDirectoryNode:
         d.addCallback(self._get, name)
         return d
 
+    def get_child_and_metadata(self, name):
+        """I return a Deferred that fires with the (node, metadata) pair for
+        the named child. The node is either an IFileNode or an
+        IDirectoryNode, and the metadata is a dictionary."""
+        assert isinstance(name, unicode)
+        d = self._read()
+        d.addCallback(self._get_with_metadata, name)
+        return d
+
     def get_metadata_for(self, name):
         assert isinstance(name, unicode)
         d = self._read()
@@ -282,9 +297,17 @@ class NewDirectoryNode:
         The path can be either a single string (slash-separated) or a list of
         path-name elements.
         """
+        d = self.get_child_and_metdadata_at_path(path)
+        d.addCallback(lambda (node, metadata): node)
+        return d
+
+    def get_child_and_metdadata_at_path(self, path):
+        """Transform a child path into an IDirectoryNode or IFileNode and
+        a metadata dictionary from the last edge that was traversed.
+        """
 
         if not path:
-            return defer.succeed(self)
+            return defer.succeed((self, {}))
         if isinstance(path, (list, tuple)):
             pass
         else:
@@ -293,11 +316,12 @@ class NewDirectoryNode:
             assert isinstance(p, unicode)
         childname = path[0]
         remaining_path = path[1:]
-        d = self.get(childname)
         if remaining_path:
-            def _got(node):
-                return node.get_child_at_path(remaining_path)
-            d.addCallback(_got)
+            d = self.get(childname)
+            d.addCallback(lambda node:
+                          node.get_child_and_metdadata_at_path(remaining_path))
+            return d
+        d = self.get_child_and_metadata(childname)
         return d
 
     def set_uri(self, name, child_uri, metadata=None, overwrite=True):
index f2da66f87c3fa8e78f19621fe45ff1326c474f3c..ca89d4d14056b016a5e811a186e9cebb11d3858b 100644 (file)
@@ -703,6 +703,15 @@ class IDirectoryNode(IMutableFilesystemNode):
         path-name elements. All elements must be unicode strings.
         """
 
+    def get_child_and_metadata_at_path(path):
+        """Transform a child path into an IDirectoryNode/IFileNode and
+        metadata.
+
+        I am like get_child_at_path(), but my Deferred fires with a tuple of
+        (node, metadata). The metadata comes from the last edge. If the path
+        is empty, the metadata will be an empty dictionary.
+        """
+
     def set_uri(name, child_uri, metadata=None, overwrite=True):
         """I add a child (by URI) at the specific name. I return a Deferred
         that fires when the operation finishes. If overwrite= is True, I will
index 90f26bfdb43d4b8fe4112384b22284f36b4cef49..1966722871bc462b6606ff5bb19615d7e7793c26 100644 (file)
@@ -361,6 +361,10 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin, testutil.StallMixin):
             def _add_subsubdir(res):
                 return self.subdir.create_empty_directory(u"subsubdir")
             d.addCallback(_add_subsubdir)
+            # /
+            # /child = mutable
+            # /subdir = directory
+            # /subdir/subsubdir = directory
             d.addCallback(lambda res: n.get_child_at_path(u"subdir/subsubdir"))
             d.addCallback(lambda subsubdir:
                           self.failUnless(isinstance(subsubdir,
@@ -374,6 +378,39 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin, testutil.StallMixin):
                           self.failUnlessEqual(sorted(metadata.keys()),
                                                ["ctime", "mtime"]))
 
+            d.addCallback(lambda res:
+                          self.shouldFail(KeyError, "gcamap-no",
+                                          "'nope'",
+                                          n.get_child_and_metdadata_at_path,
+                                          u"subdir/nope"))
+            d.addCallback(lambda res:
+                          n.get_child_and_metdadata_at_path(u""))
+            def _check_child_and_metadata1(res):
+                child, metadata = res
+                self.failUnless(isinstance(child, FakeDirectoryNode))
+                # edge-metadata needs at least one path segment
+                self.failUnlessEqual(sorted(metadata.keys()), [])
+            d.addCallback(_check_child_and_metadata1)
+            d.addCallback(lambda res:
+                          n.get_child_and_metdadata_at_path(u"child"))
+
+            def _check_child_and_metadata2(res):
+                child, metadata = res
+                self.failUnlessEqual(child.get_uri(),
+                                     fake_file_uri.to_string())
+                self.failUnlessEqual(sorted(metadata.keys()),
+                                     ["ctime", "mtime"])
+            d.addCallback(_check_child_and_metadata2)
+
+            d.addCallback(lambda res:
+                          n.get_child_and_metdadata_at_path(u"subdir/subsubdir"))
+            def _check_child_and_metadata3(res):
+                child, metadata = res
+                self.failUnless(isinstance(child, FakeDirectoryNode))
+                self.failUnlessEqual(sorted(metadata.keys()),
+                                     ["ctime", "mtime"])
+            d.addCallback(_check_child_and_metadata3)
+
             # set_uri + metadata
             # it should be possible to add a child without any metadata
             d.addCallback(lambda res: n.set_uri(u"c2", fake_file_uri, {}))