From: Brian Warner Date: Wed, 19 Dec 2007 06:30:02 +0000 (-0700) Subject: dirnode: add set_uris() and set_nodes() (plural), to set multiple children at once... X-Git-Url: https://git.rkrishnan.org/FOOURL?a=commitdiff_plain;h=9a8f68c41ff88e752283c6c9cd14beb2db226d49;p=tahoe-lafs%2Ftahoe-lafs.git dirnode: add set_uris() and set_nodes() (plural), to set multiple children at once. Use it to set up a new webapi test for issue #237. --- diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py index 0659d687..6efdc1d3 100644 --- a/src/allmydata/dirnode.py +++ b/src/allmydata/dirnode.py @@ -6,7 +6,7 @@ from twisted.internet import defer import simplejson from allmydata.mutable import NotMutableError from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\ - IURI, IFileNode, IMutableFileURI, IVerifierURI + IURI, IFileNode, IMutableFileURI, IVerifierURI, IFilesystemNode from allmydata.util import hashutil from allmydata.util.hashutil import netstring from allmydata.uri import NewDirectoryURI @@ -131,7 +131,7 @@ class NewDirectoryNode: child, metadata = children[name] assert (IFileNode.providedBy(child) or IMutableFileNode.providedBy(child) - or IDirectoryNode.providedBy(child)), children + or IDirectoryNode.providedBy(child)), (name,child) assert isinstance(metadata, dict) rwcap = child.get_uri() # might be RO if the child is not writeable rocap = child.get_readonly_uri() @@ -224,7 +224,20 @@ class NewDirectoryNode: If this directory node is read-only, the Deferred will errback with a NotMutableError.""" - return self.set_node(name, self._create_node(child_uri), metadata, wait_for_numpeers=wait_for_numpeers) + return self.set_node(name, self._create_node(child_uri), metadata, + wait_for_numpeers) + + def set_uris(self, entries, wait_for_numpeers=None): + node_entries = [] + for e in entries: + if len(e) == 2: + name, child_uri = e + metadata = {} + else: + assert len(e) == 3 + name, child_uri, metadata = e + node_entries.append( (name,self._create_node(child_uri),metadata) ) + return self.set_nodes(node_entries, wait_for_numpeers) def set_node(self, name, child, metadata={}, wait_for_numpeers=None): """I add a child at the specific name. I return a Deferred that fires @@ -234,17 +247,31 @@ class NewDirectoryNode: If this directory node is read-only, the Deferred will errback with a NotMutableError.""" + assert IFilesystemNode.providedBy(child), child + d = self.set_nodes( [(name, child, metadata)], wait_for_numpeers) + d.addCallback(lambda res: child) + return d + + def set_nodes(self, entries, wait_for_numpeers=None): if self.is_readonly(): return defer.fail(NotMutableError()) d = self._read() def _add(children): - children[name] = (child, metadata) + for e in entries: + if len(e) == 2: + name, child = e + metadata = {} + else: + assert len(e) == 3 + name, child, metadata = e + children[name] = (child, metadata) new_contents = self._pack_contents(children) return self._node.replace(new_contents, wait_for_numpeers=wait_for_numpeers) d.addCallback(_add) - d.addCallback(lambda res: child) + d.addCallback(lambda res: None) return d + def add_file(self, name, uploadable, wait_for_numpeers=None): """I upload a file (using the given IUploadable), then attach the resulting FileNode to the directory at the given name. I return a diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py index f8ef5acd..48de0abe 100644 --- a/src/allmydata/interfaces.py +++ b/src/allmydata/interfaces.py @@ -530,6 +530,13 @@ class IDirectoryNode(IMutableFilesystemNode): If this directory node is read-only, the Deferred will errback with a NotMutableError.""" + def set_uris(entries): + """Add multiple (name, child_uri) pairs to a directory node. Returns + a Deferred that fires (with None) when the operation finishes. This + is equivalent to calling set_uri() multiple times, but is much more + efficient. + """ + def set_node(name, child): """I add a child at the specific name. I return a Deferred that fires when the operation finishes. This Deferred will fire with the child @@ -539,6 +546,13 @@ class IDirectoryNode(IMutableFilesystemNode): If this directory node is read-only, the Deferred will errback with a NotMutableError.""" + def set_nodes(entries): + """Add multiple (name, child_node) pairs to a directory node. Returns + a Deferred that fires (with None) when the operation finishes. This + is equivalent to calling set_node() multiple times, but is much more + efficient.""" + + def add_file(name, uploadable): """I upload a file (using the given IUploadable), then attach the resulting FileNode to the directory at the given name. I return a diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index fd270ce2..fb0b3b10 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -606,6 +606,35 @@ class Web(WebMixin, unittest.TestCase): return d + def test_GET_DIRURL_large(self): + # Nevow has a problem showing more than about 192 children of a + # directory: it uses defer.success() and d.addCallback in a way that + # can make the stack grow very quickly. See ticket #237 for details. + # To work around this, I think we'll need to put a 'return + # defer.fireEventually' in our render_ method. This test is intended + # to trigger the bug (and eventually verify that our workaround + # actually works), but it isn't yet failing for me. + + d = self.s.create_empty_dirnode() + COUNT = 400 + def _created(dirnode): + entries = [ (str(i), self._foo_uri) for i in range(COUNT) ] + d2 = dirnode.set_uris(entries) + d2.addCallback(lambda res: dirnode) + return d2 + d.addCallback(_created) + + def _check(dirnode): + large_url = "/uri/" + dirnode.get_uri() + "/" + return self.GET(large_url) + d.addCallback(_check) + + def _done(res): + self.failUnless('%d' % (COUNT-1, COUNT-1) in res) + self.failIf("maximum recursion depth exceeded" in res) + d.addCallback(_done) + return d + def test_GET_DIRURL_json(self): d = self.GET(self.public_url + "/foo?t=json") d.addCallback(self.failUnlessIsFooJSON)