From d0bdf9a611cf0e6442ed01d246c10308d6da0b6d Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Thu, 2 Oct 2008 17:52:03 -0700 Subject: [PATCH] dirnode: add get_child_and_metadata_at_path --- src/allmydata/dirnode.py | 34 +++++++++++++++++++++++---- src/allmydata/interfaces.py | 9 ++++++++ src/allmydata/test/test_dirnode.py | 37 ++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py index 58245100..c1250479 100644 --- a/src/allmydata/dirnode.py +++ b/src/allmydata/dirnode.py @@ -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): diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py index f2da66f8..ca89d4d1 100644 --- a/src/allmydata/interfaces.py +++ b/src/allmydata/interfaces.py @@ -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 diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py index 90f26bfd..19667228 100644 --- a/src/allmydata/test/test_dirnode.py +++ b/src/allmydata/test/test_dirnode.py @@ -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, {})) -- 2.45.2