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.

This commit is contained in:
Brian Warner 2007-12-18 23:30:02 -07:00
parent 2308420fc6
commit 9a8f68c41f
3 changed files with 75 additions and 5 deletions

View File

@ -6,7 +6,7 @@ from twisted.internet import defer
import simplejson import simplejson
from allmydata.mutable import NotMutableError from allmydata.mutable import NotMutableError
from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\ from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\
IURI, IFileNode, IMutableFileURI, IVerifierURI IURI, IFileNode, IMutableFileURI, IVerifierURI, IFilesystemNode
from allmydata.util import hashutil from allmydata.util import hashutil
from allmydata.util.hashutil import netstring from allmydata.util.hashutil import netstring
from allmydata.uri import NewDirectoryURI from allmydata.uri import NewDirectoryURI
@ -131,7 +131,7 @@ class NewDirectoryNode:
child, metadata = children[name] child, metadata = children[name]
assert (IFileNode.providedBy(child) assert (IFileNode.providedBy(child)
or IMutableFileNode.providedBy(child) or IMutableFileNode.providedBy(child)
or IDirectoryNode.providedBy(child)), children or IDirectoryNode.providedBy(child)), (name,child)
assert isinstance(metadata, dict) assert isinstance(metadata, dict)
rwcap = child.get_uri() # might be RO if the child is not writeable rwcap = child.get_uri() # might be RO if the child is not writeable
rocap = child.get_readonly_uri() rocap = child.get_readonly_uri()
@ -224,7 +224,20 @@ class NewDirectoryNode:
If this directory node is read-only, the Deferred will errback with a If this directory node is read-only, the Deferred will errback with a
NotMutableError.""" 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): 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 """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 If this directory node is read-only, the Deferred will errback with a
NotMutableError.""" 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(): if self.is_readonly():
return defer.fail(NotMutableError()) return defer.fail(NotMutableError())
d = self._read() d = self._read()
def _add(children): def _add(children):
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) children[name] = (child, metadata)
new_contents = self._pack_contents(children) new_contents = self._pack_contents(children)
return self._node.replace(new_contents, wait_for_numpeers=wait_for_numpeers) return self._node.replace(new_contents, wait_for_numpeers=wait_for_numpeers)
d.addCallback(_add) d.addCallback(_add)
d.addCallback(lambda res: child) d.addCallback(lambda res: None)
return d return d
def add_file(self, name, uploadable, wait_for_numpeers=None): def add_file(self, name, uploadable, wait_for_numpeers=None):
"""I upload a file (using the given IUploadable), then attach the """I upload a file (using the given IUploadable), then attach the
resulting FileNode to the directory at the given name. I return a resulting FileNode to the directory at the given name. I return a

View File

@ -530,6 +530,13 @@ class IDirectoryNode(IMutableFilesystemNode):
If this directory node is read-only, the Deferred will errback with a If this directory node is read-only, the Deferred will errback with a
NotMutableError.""" 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): def set_node(name, child):
"""I add a child at the specific name. I return a Deferred that fires """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 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 If this directory node is read-only, the Deferred will errback with a
NotMutableError.""" 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): def add_file(name, uploadable):
"""I upload a file (using the given IUploadable), then attach the """I upload a file (using the given IUploadable), then attach the
resulting FileNode to the directory at the given name. I return a resulting FileNode to the directory at the given name. I return a

View File

@ -606,6 +606,35 @@ class Web(WebMixin, unittest.TestCase):
return d 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('<a href="%d">%d</a>' % (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): def test_GET_DIRURL_json(self):
d = self.GET(self.public_url + "/foo?t=json") d = self.GET(self.public_url + "/foo?t=json")
d.addCallback(self.failUnlessIsFooJSON) d.addCallback(self.failUnlessIsFooJSON)