unicode handling: declare dirnodes to contain unicode child names, update webish to match

This commit is contained in:
Brian Warner 2008-02-14 15:45:56 -07:00
parent 61edecd137
commit 7927495cbe
7 changed files with 377 additions and 283 deletions

View File

@ -58,6 +58,18 @@ In addition, each directory has a corresponding URL. The Pictures URL is:
http://localhost:8123/uri/$PRIVATE_VDRIVE_URI/Pictures
Note that all filenames are assumed to be UTF-8 encoded, so "resume.doc"
(with an acute accent on both E's) would be accessed with:
http://localhost:8123/uri/$PRIVATE_VDRIVE_URI/r%C3%A9sum%C3%A9.doc
The filenames inside upload POST forms are interpreted using whatever
character set was provided in the conventional '_charset' field, and defaults
to UTF-8 if not otherwise specified. The JSON representation of each
directory contains native unicode strings. Tahoe directories are specified to
contain unicode filenames, and cannot contain binary strings that are not
representable as such.
c. URIs
From the "URIs" chapter in architecture.txt, recall that each file and

View File

@ -102,8 +102,8 @@ class NewDirectoryNode:
# the directory is serialized as a list of netstrings, one per child.
# Each child is serialized as a list of four netstrings: (name,
# rocap, rwcap, metadata), in which the name,rocap,metadata are in
# cleartext. The rwcap is formatted as:
# pack("16ss32s", iv, AES(H(writekey+iv), plaintextrwcap), mac)
# cleartext. The 'name' is UTF-8 encoded. The rwcap is formatted as:
# pack("16ss32s", iv, AES(H(writekey+iv), plaintextrwcap), mac)
assert isinstance(data, str)
# an empty directory is serialized as an empty string
if data == "":
@ -113,6 +113,7 @@ class NewDirectoryNode:
while len(data) > 0:
entry, data = split_netstring(data, 1, True)
name, rocap, rwcapdata, metadata_s = split_netstring(entry, 4)
name = name.decode("utf-8")
if writeable:
rwcap = self._decrypt_rwcapdata(rwcapdata)
child = self._create_node(rwcap)
@ -129,13 +130,14 @@ class NewDirectoryNode:
entries = []
for name in sorted(children.keys()):
child, metadata = children[name]
assert isinstance(name, unicode)
assert (IFileNode.providedBy(child)
or IMutableFileNode.providedBy(child)
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()
entry = "".join([netstring(name),
entry = "".join([netstring(name.encode("utf-8")),
netstring(rocap),
netstring(self._encrypt_rwcap(rwcap)),
netstring(simplejson.dumps(metadata))])
@ -168,6 +170,7 @@ class NewDirectoryNode:
def has_child(self, name):
"""I return a Deferred that fires with a boolean, True if there
exists a child of the given name, False if not."""
assert isinstance(name, unicode)
d = self._read()
d.addCallback(lambda children: children.has_key(name))
return d
@ -181,16 +184,19 @@ class NewDirectoryNode:
def get(self, name):
"""I return a Deferred that fires with the named child node,
which is either an IFileNode or an IDirectoryNode."""
assert isinstance(name, unicode)
d = self._read()
d.addCallback(self._get, name)
return d
def get_metadata_for(self, name):
assert isinstance(name, unicode)
d = self._read()
d.addCallback(lambda children: children[name][1])
return d
def set_metadata_for(self, name, metadata):
assert isinstance(name, unicode)
if self.is_readonly():
return defer.fail(NotMutableError())
assert isinstance(metadata, dict)
@ -216,8 +222,12 @@ class NewDirectoryNode:
if not path:
return defer.succeed(self)
if isinstance(path, (str, unicode)):
if isinstance(path, (list, tuple)):
pass
else:
path = path.split("/")
for p in path:
assert isinstance(p, unicode)
childname = path[0]
remaining_path = path[1:]
d = self.get(childname)
@ -237,6 +247,7 @@ class NewDirectoryNode:
If this directory node is read-only, the Deferred will errback with a
NotMutableError."""
assert isinstance(name, unicode)
return self.set_node(name, self._create_node(child_uri), metadata)
def set_uris(self, entries):
@ -248,6 +259,7 @@ class NewDirectoryNode:
else:
assert len(e) == 3
name, child_uri, metadata = e
assert isinstance(name, unicode)
node_entries.append( (name,self._create_node(child_uri),metadata) )
return self.set_nodes(node_entries)
@ -259,6 +271,7 @@ class NewDirectoryNode:
If this directory node is read-only, the Deferred will errback with a
NotMutableError."""
assert isinstance(name, unicode)
assert IFilesystemNode.providedBy(child), child
d = self.set_nodes( [(name, child, metadata)])
d.addCallback(lambda res: child)
@ -277,6 +290,7 @@ class NewDirectoryNode:
else:
assert len(e) == 3
name, child, new_metadata = e
assert isinstance(name, unicode)
if name in children:
metadata = children[name][1].copy()
else:
@ -303,6 +317,7 @@ class NewDirectoryNode:
resulting FileNode to the directory at the given name. I return a
Deferred that fires (with the IFileNode of the uploaded file) when
the operation completes."""
assert isinstance(name, unicode)
if self.is_readonly():
return defer.fail(NotMutableError())
d = self._client.upload(uploadable)
@ -314,6 +329,7 @@ class NewDirectoryNode:
def delete(self, name):
"""I remove the child at the specific name. I return a Deferred that
fires (with the node just removed) when the operation finishes."""
assert isinstance(name, unicode)
if self.is_readonly():
return defer.fail(NotMutableError())
d = self._read()
@ -333,6 +349,7 @@ class NewDirectoryNode:
"""I create and attach an empty directory at the given name. I return
a Deferred that fires (with the new directory node) when the
operation finishes."""
assert isinstance(name, unicode)
if self.is_readonly():
return defer.fail(NotMutableError())
d = self._client.create_empty_dirnode()
@ -349,10 +366,12 @@ class NewDirectoryNode:
is referenced by name. On the new parent, the child will live under
'new_child_name', which defaults to 'current_child_name'. I return a
Deferred that fires when the operation finishes."""
assert isinstance(current_child_name, unicode)
if self.is_readonly() or new_parent.is_readonly():
return defer.fail(NotMutableError())
if new_child_name is None:
new_child_name = current_child_name
assert isinstance(new_child_name, unicode)
d = self.get(current_child_name)
def sn(child):
return new_parent.set_node(new_child_name, child)

View File

@ -587,6 +587,11 @@ class IMutableFileNode(IFileNode, IMutableFilesystemNode):
"""
class IDirectoryNode(IMutableFilesystemNode):
"""I represent a name-to-child mapping, holding the tahoe equivalent of a
directory. All child names are unicode strings, and all children are some
sort of IFilesystemNode (either files or subdirectories).
"""
def get_uri():
"""
The dirnode ('1') URI returned by this method can be used in
@ -607,30 +612,33 @@ class IDirectoryNode(IMutableFilesystemNode):
def list():
"""I return a Deferred that fires with a dictionary mapping child
name to (node, metadata_dict) tuples, in which 'node' is either an
IFileNode or IDirectoryNode, and 'metadata_dict' is a dictionary of
metadata."""
name (a unicode string) to (node, metadata_dict) tuples, in which
'node' is either an IFileNode or IDirectoryNode, and 'metadata_dict'
is a dictionary of metadata."""
def has_child(name):
"""I return a Deferred that fires with a boolean, True if there
exists a child of the given name, False if not."""
exists a child of the given name, False if not. The child name must
be a unicode string."""
def get(name):
"""I return a Deferred that fires with a specific named child node,
either an IFileNode or an IDirectoryNode."""
either an IFileNode or an IDirectoryNode. The child name must be a
unicode string."""
def get_metadata_for(name):
"""I return a Deferred that fires with the metadata dictionary for a
specific named child node. This metadata is stored in the *edge*, not
in the child, so it is attached to the parent dirnode rather than the
child dir-or-file-node."""
child dir-or-file-node. The child name must be a unicode string."""
def set_metadata_for(name, metadata):
"""I replace any existing metadata for the named child with the new
metadata. This metadata is stored in the *edge*, not in the child, so
it is attached to the parent dirnode rather than the child
dir-or-file-node. I return a Deferred (that fires with this dirnode)
when the operation is complete."""
metadata. The child name must be a unicode string. This metadata is
stored in the *edge*, not in the child, so it is attached to the
parent dirnode rather than the child dir-or-file-node. I return a
Deferred (that fires with this dirnode) when the operation is
complete."""
def get_child_at_path(path):
"""Transform a child path into an IDirectoryNode or IFileNode.
@ -640,13 +648,13 @@ class IDirectoryNode(IMutableFilesystemNode):
errbacks with IndexError if the node could not be found.
The path can be either a single string (slash-separated) or a list of
path-name elements.
path-name elements. All elements must be unicode strings.
"""
def set_uri(name, child_uri, metadata=None):
"""I add a child (by URI) at the specific name. I return a Deferred
that fires when the operation finishes. I will replace any existing
child of the same name.
child of the same name. The child name must be a unicode string.
The child_uri could be for a file, or for a directory (either
read-write or read-only, using a URI that came from get_uri() ).
@ -665,14 +673,15 @@ class IDirectoryNode(IMutableFilesystemNode):
"""Add multiple (name, child_uri) pairs (or (name, child_uri,
metadata) triples) 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.
calling set_uri() multiple times, but is much more efficient. All
child names must be unicode strings.
"""
def set_node(name, child, metadata=None):
"""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
node that was just added. I will replace any existing child of the
same name.
same name. The child name must be a unicode string.
If metadata= is provided, I will use it as the metadata for the named
edge. This will replace any existing metadata. If metadata= is left
@ -688,31 +697,35 @@ class IDirectoryNode(IMutableFilesystemNode):
"""Add multiple (name, child_node) pairs (or (name, child_node,
metadata) triples) 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."""
calling set_node() multiple times, but is much more efficient. All
child names must be unicode strings."""
def add_file(name, uploadable, metadata=None):
"""I upload a file (using the given IUploadable), then attach the
resulting FileNode to the directory at the given name. I set metadata
the same way as set_uri and set_node.
the same way as set_uri and set_node. The child name must be a
unicode string.
I return a Deferred that fires (with the IFileNode of the uploaded
file) when the operation completes."""
def delete(name):
"""I remove the child at the specific name. I return a Deferred that
fires when the operation finishes."""
fires when the operation finishes. The child name must be a unicode
string."""
def create_empty_directory(name):
"""I create and attach an empty directory at the given name. I return
a Deferred that fires when the operation finishes."""
"""I create and attach an empty directory at the given name. The
child name must be a unicode string. I return a Deferred that fires
when the operation finishes."""
def move_child_to(current_child_name, new_parent, new_child_name=None):
"""I take one of my children and move them to a new parent. The child
is referenced by name. On the new parent, the child will live under
'new_child_name', which defaults to 'current_child_name'. TODO: what
should we do about metadata? I return a Deferred that fires when the
operation finishes."""
operation finishes. The child name must be a unicode string."""
def build_manifest():
"""Return a frozenset of verifier-capability strings for all nodes

View File

@ -83,14 +83,14 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d = self.client.create_empty_dirnode()
def _created(dn):
u = make_mutable_file_uri()
d = dn.set_uri("child", u, {})
d = dn.set_uri(u"child", u, {})
d.addCallback(lambda res: dn.list())
def _check1(children):
self.failUnless("child" in children)
self.failUnless(u"child" in children)
d.addCallback(_check1)
d.addCallback(lambda res:
self.shouldFail(KeyError, "get bogus", None,
dn.get, "bogus"))
dn.get, u"bogus"))
def _corrupt(res):
filenode = dn._node
si = IURI(filenode.get_uri()).storage_index
@ -126,7 +126,7 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d = self.client.create_empty_dirnode()
def _created(rw_dn):
d2 = rw_dn.set_uri("child", fileuri)
d2 = rw_dn.set_uri(u"child", fileuri)
d2.addCallback(lambda res: rw_dn)
return d2
d.addCallback(_created)
@ -138,23 +138,23 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
self.failUnless(ro_dn.is_mutable())
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.set_uri, "newchild", fileuri)
ro_dn.set_uri, u"newchild", fileuri)
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.set_node, "newchild", filenode)
ro_dn.set_node, u"newchild", filenode)
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.add_file, "newchild", uploadable)
ro_dn.add_file, u"newchild", uploadable)
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.delete, "child")
ro_dn.delete, u"child")
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.create_empty_directory, "newchild")
ro_dn.create_empty_directory, u"newchild")
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
ro_dn.move_child_to, "child", rw_dn)
ro_dn.move_child_to, u"child", rw_dn)
self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
rw_dn.move_child_to, "child", ro_dn)
rw_dn.move_child_to, u"child", ro_dn)
return ro_dn.list()
d.addCallback(_ready)
def _listed(children):
self.failUnless("child" in children)
self.failUnless(u"child" in children)
d.addCallback(_listed)
return d
@ -186,16 +186,16 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d = n.list()
d.addCallback(lambda res: self.failUnlessEqual(res, {}))
d.addCallback(lambda res: n.has_child("missing"))
d.addCallback(lambda res: n.has_child(u"missing"))
d.addCallback(lambda res: self.failIf(res))
fake_file_uri = make_mutable_file_uri()
m = Marker(fake_file_uri)
ffu_v = m.get_verifier()
assert isinstance(ffu_v, str)
self.expected_manifest.append(ffu_v)
d.addCallback(lambda res: n.set_uri("child", fake_file_uri))
d.addCallback(lambda res: n.set_uri(u"child", fake_file_uri))
d.addCallback(lambda res: n.create_empty_directory("subdir"))
d.addCallback(lambda res: n.create_empty_directory(u"subdir"))
def _created(subdir):
self.failUnless(isinstance(subdir, FakeDirectoryNode))
self.subdir = subdir
@ -207,7 +207,7 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d.addCallback(lambda res: n.list())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["child", "subdir"])))
sorted([u"child", u"subdir"])))
d.addCallback(lambda res: n.build_manifest())
def _check_manifest(manifest):
@ -216,116 +216,116 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d.addCallback(_check_manifest)
def _add_subsubdir(res):
return self.subdir.create_empty_directory("subsubdir")
return self.subdir.create_empty_directory(u"subsubdir")
d.addCallback(_add_subsubdir)
d.addCallback(lambda res: n.get_child_at_path("subdir/subsubdir"))
d.addCallback(lambda res: n.get_child_at_path(u"subdir/subsubdir"))
d.addCallback(lambda subsubdir:
self.failUnless(isinstance(subsubdir,
FakeDirectoryNode)))
d.addCallback(lambda res: n.get_child_at_path(""))
d.addCallback(lambda res: n.get_child_at_path(u""))
d.addCallback(lambda res: self.failUnlessEqual(res.get_uri(),
n.get_uri()))
d.addCallback(lambda res: n.get_metadata_for("child"))
d.addCallback(lambda res: n.get_metadata_for(u"child"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
# set_uri + metadata
# it should be possible to add a child without any metadata
d.addCallback(lambda res: n.set_uri("c2", fake_file_uri, {}))
d.addCallback(lambda res: n.get_metadata_for("c2"))
d.addCallback(lambda res: n.set_uri(u"c2", fake_file_uri, {}))
d.addCallback(lambda res: n.get_metadata_for(u"c2"))
d.addCallback(lambda metadata: self.failUnlessEqual(metadata, {}))
# if we don't set any defaults, the child should get timestamps
d.addCallback(lambda res: n.set_uri("c3", fake_file_uri))
d.addCallback(lambda res: n.get_metadata_for("c3"))
d.addCallback(lambda res: n.set_uri(u"c3", fake_file_uri))
d.addCallback(lambda res: n.get_metadata_for(u"c3"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
# or we can add specific metadata at set_uri() time, which
# overrides the timestamps
d.addCallback(lambda res: n.set_uri("c4", fake_file_uri,
d.addCallback(lambda res: n.set_uri(u"c4", fake_file_uri,
{"key": "value"}))
d.addCallback(lambda res: n.get_metadata_for("c4"))
d.addCallback(lambda res: n.get_metadata_for(u"c4"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata, {"key": "value"}))
d.addCallback(lambda res: n.delete("c2"))
d.addCallback(lambda res: n.delete("c3"))
d.addCallback(lambda res: n.delete("c4"))
d.addCallback(lambda res: n.delete(u"c2"))
d.addCallback(lambda res: n.delete(u"c3"))
d.addCallback(lambda res: n.delete(u"c4"))
# set_node + metadata
# it should be possible to add a child without any metadata
d.addCallback(lambda res: n.set_node("d2", n, {}))
d.addCallback(lambda res: n.get_metadata_for("d2"))
d.addCallback(lambda res: n.set_node(u"d2", n, {}))
d.addCallback(lambda res: n.get_metadata_for(u"d2"))
d.addCallback(lambda metadata: self.failUnlessEqual(metadata, {}))
# if we don't set any defaults, the child should get timestamps
d.addCallback(lambda res: n.set_node("d3", n))
d.addCallback(lambda res: n.get_metadata_for("d3"))
d.addCallback(lambda res: n.set_node(u"d3", n))
d.addCallback(lambda res: n.get_metadata_for(u"d3"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
# or we can add specific metadata at set_node() time, which
# overrides the timestamps
d.addCallback(lambda res: n.set_node("d4", n,
d.addCallback(lambda res: n.set_node(u"d4", n,
{"key": "value"}))
d.addCallback(lambda res: n.get_metadata_for("d4"))
d.addCallback(lambda res: n.get_metadata_for(u"d4"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata, {"key": "value"}))
d.addCallback(lambda res: n.delete("d2"))
d.addCallback(lambda res: n.delete("d3"))
d.addCallback(lambda res: n.delete("d4"))
d.addCallback(lambda res: n.delete(u"d2"))
d.addCallback(lambda res: n.delete(u"d3"))
d.addCallback(lambda res: n.delete(u"d4"))
# metadata through set_uris()
d.addCallback(lambda res: n.set_uris([ ("e1", fake_file_uri),
("e2", fake_file_uri, {}),
("e3", fake_file_uri,
d.addCallback(lambda res: n.set_uris([ (u"e1", fake_file_uri),
(u"e2", fake_file_uri, {}),
(u"e3", fake_file_uri,
{"key": "value"}),
]))
d.addCallback(lambda res: n.get_metadata_for("e1"))
d.addCallback(lambda res: n.get_metadata_for(u"e1"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
d.addCallback(lambda res: n.get_metadata_for("e2"))
d.addCallback(lambda res: n.get_metadata_for(u"e2"))
d.addCallback(lambda metadata: self.failUnlessEqual(metadata, {}))
d.addCallback(lambda res: n.get_metadata_for("e3"))
d.addCallback(lambda res: n.get_metadata_for(u"e3"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata, {"key": "value"}))
d.addCallback(lambda res: n.delete("e1"))
d.addCallback(lambda res: n.delete("e2"))
d.addCallback(lambda res: n.delete("e3"))
d.addCallback(lambda res: n.delete(u"e1"))
d.addCallback(lambda res: n.delete(u"e2"))
d.addCallback(lambda res: n.delete(u"e3"))
# metadata through set_nodes()
d.addCallback(lambda res: n.set_nodes([ ("f1", n),
("f2", n, {}),
("f3", n,
d.addCallback(lambda res: n.set_nodes([ (u"f1", n),
(u"f2", n, {}),
(u"f3", n,
{"key": "value"}),
]))
d.addCallback(lambda res: n.get_metadata_for("f1"))
d.addCallback(lambda res: n.get_metadata_for(u"f1"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
d.addCallback(lambda res: n.get_metadata_for("f2"))
d.addCallback(lambda res: n.get_metadata_for(u"f2"))
d.addCallback(lambda metadata: self.failUnlessEqual(metadata, {}))
d.addCallback(lambda res: n.get_metadata_for("f3"))
d.addCallback(lambda res: n.get_metadata_for(u"f3"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata, {"key": "value"}))
d.addCallback(lambda res: n.delete("f1"))
d.addCallback(lambda res: n.delete("f2"))
d.addCallback(lambda res: n.delete("f3"))
d.addCallback(lambda res: n.delete(u"f1"))
d.addCallback(lambda res: n.delete(u"f2"))
d.addCallback(lambda res: n.delete(u"f3"))
d.addCallback(lambda res:
n.set_metadata_for("child",
n.set_metadata_for(u"child",
{"tags": ["web2.0-compatible"]}))
d.addCallback(lambda n1: n1.get_metadata_for("child"))
d.addCallback(lambda n1: n1.get_metadata_for(u"child"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata,
{"tags": ["web2.0-compatible"]}))
@ -339,14 +339,14 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
# from causing the test to fail, stall for more than a few
# hundrededths of a second.
d.addCallback(self.stall, 0.1)
d.addCallback(lambda res: n.add_file("timestamps",
d.addCallback(lambda res: n.add_file(u"timestamps",
upload.Data("stamp me")))
d.addCallback(self.stall, 0.1)
def _stop(res):
self._stop_timestamp = time.time()
d.addCallback(_stop)
d.addCallback(lambda res: n.get_metadata_for("timestamps"))
d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
def _check_timestamp1(metadata):
self.failUnless("ctime" in metadata)
self.failUnless("mtime" in metadata)
@ -364,28 +364,28 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
self._old_mtime = metadata["mtime"]
d.addCallback(_check_timestamp1)
d.addCallback(self.stall, 2.0) # accomodate low-res timestamps
d.addCallback(lambda res: n.set_node("timestamps", n))
d.addCallback(lambda res: n.get_metadata_for("timestamps"))
d.addCallback(lambda res: n.set_node(u"timestamps", n))
d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
def _check_timestamp2(metadata):
self.failUnlessEqual(metadata["ctime"], self._old_ctime,
"%s != %s" % (metadata["ctime"],
self._old_ctime))
self.failUnlessGreaterThan(metadata["mtime"], self._old_mtime)
return n.delete("timestamps")
return n.delete(u"timestamps")
d.addCallback(_check_timestamp2)
# also make sure we can add/update timestamps on a
# previously-existing child that didn't have any, since there are
# a lot of 0.7.0-generated edges around out there
d.addCallback(lambda res: n.set_node("no_timestamps", n, {}))
d.addCallback(lambda res: n.set_node("no_timestamps", n))
d.addCallback(lambda res: n.get_metadata_for("no_timestamps"))
d.addCallback(lambda res: n.set_node(u"no_timestamps", n, {}))
d.addCallback(lambda res: n.set_node(u"no_timestamps", n))
d.addCallback(lambda res: n.get_metadata_for(u"no_timestamps"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
d.addCallback(lambda res: n.delete("no_timestamps"))
d.addCallback(lambda res: n.delete(u"no_timestamps"))
d.addCallback(lambda res: n.delete("subdir"))
d.addCallback(lambda res: n.delete(u"subdir"))
d.addCallback(lambda old_child:
self.failUnlessEqual(old_child.get_uri(),
self.subdir.get_uri()))
@ -393,47 +393,47 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin):
d.addCallback(lambda res: n.list())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["child"])))
sorted([u"child"])))
uploadable = upload.Data("some data")
d.addCallback(lambda res: n.add_file("newfile", uploadable))
d.addCallback(lambda res: n.add_file(u"newfile", uploadable))
d.addCallback(lambda newnode:
self.failUnless(IFileNode.providedBy(newnode)))
d.addCallback(lambda res: n.list())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["child", "newfile"])))
d.addCallback(lambda res: n.get_metadata_for("newfile"))
sorted([u"child", u"newfile"])))
d.addCallback(lambda res: n.get_metadata_for(u"newfile"))
d.addCallback(lambda metadata:
self.failUnlessEqual(sorted(metadata.keys()),
["ctime", "mtime"]))
uploadable = upload.Data("some data")
d.addCallback(lambda res: n.add_file("newfile-metadata",
d.addCallback(lambda res: n.add_file(u"newfile-metadata",
uploadable,
{"key": "value"}))
d.addCallback(lambda newnode:
self.failUnless(IFileNode.providedBy(newnode)))
d.addCallback(lambda res: n.get_metadata_for("newfile-metadata"))
d.addCallback(lambda res: n.get_metadata_for(u"newfile-metadata"))
d.addCallback(lambda metadata:
self.failUnlessEqual(metadata, {"key": "value"}))
d.addCallback(lambda res: n.delete("newfile-metadata"))
d.addCallback(lambda res: n.delete(u"newfile-metadata"))
d.addCallback(lambda res: n.create_empty_directory("subdir2"))
d.addCallback(lambda res: n.create_empty_directory(u"subdir2"))
def _created2(subdir2):
self.subdir2 = subdir2
d.addCallback(_created2)
d.addCallback(lambda res:
n.move_child_to("child", self.subdir2))
n.move_child_to(u"child", self.subdir2))
d.addCallback(lambda res: n.list())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["newfile", "subdir2"])))
sorted([u"newfile", u"subdir2"])))
d.addCallback(lambda res: self.subdir2.list())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["child"])))
sorted([u"child"])))
return d

View File

@ -750,10 +750,10 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
log.msg("_created_dirnode(%s)" % (dnode,))
d1 = dnode.list()
d1.addCallback(lambda children: self.failUnlessEqual(children, {}))
d1.addCallback(lambda res: dnode.has_child("edgar"))
d1.addCallback(lambda res: dnode.has_child(u"edgar"))
d1.addCallback(lambda answer: self.failUnlessEqual(answer, False))
d1.addCallback(lambda res: dnode.set_node("see recursive", dnode))
d1.addCallback(lambda res: dnode.has_child("see recursive"))
d1.addCallback(lambda res: dnode.set_node(u"see recursive", dnode))
d1.addCallback(lambda res: dnode.has_child(u"see recursive"))
d1.addCallback(lambda answer: self.failUnlessEqual(answer, True))
d1.addCallback(lambda res: dnode.build_manifest())
d1.addCallback(lambda manifest:
@ -840,10 +840,10 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
self._root_directory_uri = new_dirnode.get_uri()
return c0.create_node_from_uri(self._root_directory_uri)
d.addCallback(_made_root)
d.addCallback(lambda root: root.create_empty_directory("subdir1"))
d.addCallback(lambda root: root.create_empty_directory(u"subdir1"))
def _made_subdir1(subdir1_node):
self._subdir1_node = subdir1_node
d1 = subdir1_node.add_file("mydata567", ut)
d1 = subdir1_node.add_file(u"mydata567", ut)
d1.addCallback(self.log, "publish finished")
def _stash_uri(filenode):
self.uri = filenode.get_uri()
@ -854,8 +854,8 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _do_publish2(self, res):
ut = upload.Data(self.data)
d = self._subdir1_node.create_empty_directory("subdir2")
d.addCallback(lambda subdir2: subdir2.add_file("mydata992", ut))
d = self._subdir1_node.create_empty_directory(u"subdir2")
d.addCallback(lambda subdir2: subdir2.add_file(u"mydata992", ut))
return d
def log(self, res, msg, **kwargs):
@ -875,14 +875,14 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
d.addCallback(self.log, "GOT private directory")
def _got_new_dir(privnode):
rootnode = self.clients[0].create_node_from_uri(self._root_directory_uri)
d1 = privnode.create_empty_directory("personal")
d1 = privnode.create_empty_directory(u"personal")
d1.addCallback(self.log, "made P/personal")
d1.addCallback(lambda node: node.add_file("sekrit data", ut))
d1.addCallback(lambda node: node.add_file(u"sekrit data", ut))
d1.addCallback(self.log, "made P/personal/sekrit data")
d1.addCallback(lambda res: rootnode.get_child_at_path(["subdir1", "subdir2"]))
d1.addCallback(lambda res: rootnode.get_child_at_path([u"subdir1", u"subdir2"]))
def _got_s2(s2node):
d2 = privnode.set_uri("s2-rw", s2node.get_uri())
d2.addCallback(lambda node: privnode.set_uri("s2-ro", s2node.get_readonly_uri()))
d2 = privnode.set_uri(u"s2-rw", s2node.get_uri())
d2.addCallback(lambda node: privnode.set_uri(u"s2-ro", s2node.get_readonly_uri()))
return d2
d1.addCallback(_got_s2)
d1.addCallback(lambda res: privnode)
@ -895,8 +895,8 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
c1 = self.clients[1]
d = defer.succeed(c1.create_node_from_uri(self._root_directory_uri))
d.addCallback(self.log, "check_publish1 got /")
d.addCallback(lambda root: root.get("subdir1"))
d.addCallback(lambda subdir1: subdir1.get("mydata567"))
d.addCallback(lambda root: root.get(u"subdir1"))
d.addCallback(lambda subdir1: subdir1.get(u"mydata567"))
d.addCallback(lambda filenode: filenode.download_to_data())
d.addCallback(self.log, "get finished")
def _get_done(data):
@ -907,14 +907,14 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _check_publish2(self, res):
# this one uses the path-based API
rootnode = self.clients[1].create_node_from_uri(self._root_directory_uri)
d = rootnode.get_child_at_path("subdir1")
d = rootnode.get_child_at_path(u"subdir1")
d.addCallback(lambda dirnode:
self.failUnless(IDirectoryNode.providedBy(dirnode)))
d.addCallback(lambda res: rootnode.get_child_at_path("subdir1/mydata567"))
d.addCallback(lambda res: rootnode.get_child_at_path(u"subdir1/mydata567"))
d.addCallback(lambda filenode: filenode.download_to_data())
d.addCallback(lambda data: self.failUnlessEqual(data, self.data))
d.addCallback(lambda res: rootnode.get_child_at_path("subdir1/mydata567"))
d.addCallback(lambda res: rootnode.get_child_at_path(u"subdir1/mydata567"))
def _got_filenode(filenode):
fnode = self.clients[1].create_node_from_uri(filenode.get_uri())
assert fnode == filenode
@ -925,7 +925,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
# this one uses the path-based API
self._private_node = resnode
d = self._private_node.get_child_at_path("personal")
d = self._private_node.get_child_at_path(u"personal")
def _got_personal(personal):
self._personal_node = personal
return personal
@ -936,12 +936,12 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def get_path(path):
return self._private_node.get_child_at_path(path)
d.addCallback(lambda res: get_path("personal/sekrit data"))
d.addCallback(lambda res: get_path(u"personal/sekrit data"))
d.addCallback(lambda filenode: filenode.download_to_data())
d.addCallback(lambda data: self.failUnlessEqual(data, self.smalldata))
d.addCallback(lambda res: get_path("s2-rw"))
d.addCallback(lambda res: get_path(u"s2-rw"))
d.addCallback(lambda dirnode: self.failUnless(dirnode.is_mutable()))
d.addCallback(lambda res: get_path("s2-ro"))
d.addCallback(lambda res: get_path(u"s2-ro"))
def _got_s2ro(dirnode):
self.failUnless(dirnode.is_mutable(), dirnode)
self.failUnless(dirnode.is_readonly(), dirnode)
@ -949,29 +949,29 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
d1.addCallback(lambda res: dirnode.list())
d1.addCallback(self.log, "dirnode.list")
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mkdir(nope)", None, dirnode.create_empty_directory, "nope"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mkdir(nope)", None, dirnode.create_empty_directory, u"nope"))
d1.addCallback(self.log, "doing add_file(ro)")
ut = upload.Data("I will disappear, unrecorded and unobserved. The tragedy of my demise is made more poignant by its silence, but this beauty is not for you to ever know.")
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "add_file(nope)", None, dirnode.add_file, "hope", ut))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "add_file(nope)", None, dirnode.add_file, u"hope", ut))
d1.addCallback(self.log, "doing get(ro)")
d1.addCallback(lambda res: dirnode.get("mydata992"))
d1.addCallback(lambda res: dirnode.get(u"mydata992"))
d1.addCallback(lambda filenode:
self.failUnless(IFileNode.providedBy(filenode)))
d1.addCallback(self.log, "doing delete(ro)")
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, "mydata992"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, u"mydata992"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, "hopeless", self.uri))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri))
d1.addCallback(lambda res: self.shouldFail2(KeyError, "get(missing)", "'missing'", dirnode.get, "missing"))
d1.addCallback(lambda res: self.shouldFail2(KeyError, "get(missing)", "'missing'", dirnode.get, u"missing"))
personal = self._personal_node
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, "mydata992", personal, "nope"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope"))
d1.addCallback(self.log, "doing move_child_to(ro)2")
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv to readonly", None, personal.move_child_to, "sekrit data", dirnode, "nope"))
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv to readonly", None, personal.move_child_to, u"sekrit data", dirnode, u"nope"))
d1.addCallback(self.log, "finished with _got_s2ro")
return d1
@ -982,15 +982,15 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
d1 = defer.succeed(None)
d1.addCallback(self.log, "mv 'P/personal/sekrit data' to P/sekrit")
d1.addCallback(lambda res:
personal.move_child_to("sekrit data",home,"sekrit"))
personal.move_child_to(u"sekrit data",home,u"sekrit"))
d1.addCallback(self.log, "mv P/sekrit 'P/sekrit data'")
d1.addCallback(lambda res:
home.move_child_to("sekrit", home, "sekrit data"))
home.move_child_to(u"sekrit", home, u"sekrit data"))
d1.addCallback(self.log, "mv 'P/sekret data' P/personal/")
d1.addCallback(lambda res:
home.move_child_to("sekrit data", personal))
home.move_child_to(u"sekrit data", personal))
d1.addCallback(lambda res: home.build_manifest())
d1.addCallback(self.log, "manifest")
@ -1319,7 +1319,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _check_put((out,err)):
self.failUnless("200 OK" in out)
self.failUnlessEqual(err, "")
d = self._private_node.get_child_at_path("test_put/upload.txt")
d = self._private_node.get_child_at_path(u"test_put/upload.txt")
d.addCallback(lambda filenode: filenode.download_to_data())
def _check_put2(res):
self.failUnlessEqual(res, TESTDATA)
@ -1358,10 +1358,10 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _check_mv((out,err)):
self.failUnless("OK" in out)
self.failUnlessEqual(err, "")
d = self.shouldFail2(KeyError, "test_cli._check_rm", "'upload.txt'", self._private_node.get_child_at_path, "test_put/upload.txt")
d = self.shouldFail2(KeyError, "test_cli._check_rm", "'upload.txt'", self._private_node.get_child_at_path, u"test_put/upload.txt")
d.addCallback(lambda res:
self._private_node.get_child_at_path("test_put/moved.txt"))
self._private_node.get_child_at_path(u"test_put/moved.txt"))
d.addCallback(lambda filenode: filenode.download_to_data())
def _check_mv2(res):
self.failUnlessEqual(res, TESTDATA)
@ -1376,7 +1376,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _check_rm((out,err)):
self.failUnless("200 OK" in out)
self.failUnlessEqual(err, "")
d = self.shouldFail2(KeyError, "test_cli._check_rm", "'moved.txt'", self._private_node.get_child_at_path, "test_put/moved.txt")
d = self.shouldFail2(KeyError, "test_cli._check_rm", "'moved.txt'", self._private_node.get_child_at_path, u"test_put/moved.txt")
return d
d.addCallback(_check_rm)
return d
@ -1443,7 +1443,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
def _test_checker_3(self, res):
# check one file, through FileNode.check()
d = self._private_node.get_child_at_path("personal/sekrit data")
d = self._private_node.get_child_at_path(u"personal/sekrit data")
d.addCallback(lambda n: n.check())
def _checked(results):
# 'sekrit data' is small, and fits in a LiteralFileNode, so
@ -1453,7 +1453,7 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
c0 = self.clients[1]
n = c0.create_node_from_uri(self._root_directory_uri)
d.addCallback(lambda res: n.get_child_at_path("subdir1/mydata567"))
d.addCallback(lambda res: n.get_child_at_path(u"subdir1/mydata567"))
d.addCallback(lambda n: n.check())
def _checked2(results):
# mydata567 is large and lives in a CHK

View File

@ -97,29 +97,34 @@ class WebMixin(object):
self._foo_readonly_uri = foo.get_readonly_uri()
# NOTE: we ignore the deferred on all set_uri() calls, because we
# know the fake nodes do these synchronously
self.public_root.set_uri("foo", foo.get_uri())
self.public_root.set_uri(u"foo", foo.get_uri())
self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
foo.set_uri("bar.txt", self._bar_txt_uri)
foo.set_uri(u"bar.txt", self._bar_txt_uri)
foo.set_uri("empty", res[3][1].get_uri())
foo.set_uri(u"empty", res[3][1].get_uri())
sub_uri = res[4][1].get_uri()
foo.set_uri("sub", sub_uri)
foo.set_uri(u"sub", sub_uri)
sub = self.s.create_node_from_uri(sub_uri)
_ign, n, blocking_uri = self.makefile(1)
foo.set_uri("blockingfile", blocking_uri)
foo.set_uri(u"blockingfile", blocking_uri)
unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
# ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
# still think of it as an umlaut
foo.set_uri(unicode_filename, self._bar_txt_uri)
_ign, n, baz_file = self.makefile(2)
sub.set_uri("baz.txt", baz_file)
sub.set_uri(u"baz.txt", baz_file)
_ign, n, self._bad_file_uri = self.makefile(3)
# this uri should not be downloadable
del FakeCHKFileNode.all_contents[self._bad_file_uri]
rodir = res[5][1]
self.public_root.set_uri("reedownlee", rodir.get_readonly_uri())
rodir.set_uri("nor", baz_file)
self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri())
rodir.set_uri(u"nor", baz_file)
# public/
# public/foo/
@ -132,7 +137,7 @@ class WebMixin(object):
# public/reedownlee/nor
self.NEWFILE_CONTENTS = "newfile contents\n"
return foo.get_metadata_for("bar.txt")
return foo.get_metadata_for(u"bar.txt")
d.addCallback(_then)
def _got_metadata(metadata):
self._bar_txt_metadata = metadata
@ -148,7 +153,7 @@ class WebMixin(object):
return self.s.stopService()
def failUnlessIsBarDotTxt(self, res):
self.failUnlessEqual(res, self.BAR_CONTENTS)
self.failUnlessEqual(res, self.BAR_CONTENTS, res)
def failUnlessIsBarJSON(self, res):
data = simplejson.loads(res)
@ -170,17 +175,20 @@ class WebMixin(object):
kidnames = sorted(data[1]["children"])
self.failUnlessEqual(kidnames,
["bar.txt", "blockingfile", "empty", "sub"])
[u"bar.txt", u"blockingfile", u"empty",
u"n\u00fc.txt", u"sub"])
kids = data[1]["children"]
self.failUnlessEqual(kids["sub"][0], "dirnode")
self.failUnless("metadata" in kids["sub"][1])
self.failUnless("ctime" in kids["sub"][1]["metadata"])
self.failUnless("mtime" in kids["sub"][1]["metadata"])
self.failUnlessEqual(kids["bar.txt"][0], "filenode")
self.failUnlessEqual(kids["bar.txt"][1]["size"], len(self.BAR_CONTENTS))
self.failUnlessEqual(kids["bar.txt"][1]["ro_uri"], self._bar_txt_uri)
self.failUnlessEqual(kids["bar.txt"][1]["metadata"]["ctime"],
self.failUnlessEqual(kids[u"sub"][0], "dirnode")
self.failUnless("metadata" in kids[u"sub"][1])
self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
self._bar_txt_metadata["ctime"])
self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
self._bar_txt_uri)
def GET(self, urlpath, followRedirect=False):
url = self.webish_url + urlpath
@ -208,7 +216,7 @@ class WebMixin(object):
if isinstance(value, tuple):
filename, value = value
form.append('Content-Disposition: form-data; name="%s"; '
'filename="%s"' % (name, filename))
'filename="%s"' % (name, filename.encode("utf-8")))
else:
form.append('Content-Disposition: form-data; name="%s"' % name)
form.append('')
@ -399,9 +407,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
# TODO: we lose the response code, so we can't check this
#self.failUnlessEqual(responsecode, 201)
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "new.txt",
self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
self.NEWFILE_CONTENTS))
return d
@ -409,9 +417,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
# TODO: we lose the response code, so we can't check this
#self.failUnlessEqual(responsecode, 200)
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "bar.txt")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
self.NEWFILE_CONTENTS))
return d
@ -427,11 +435,11 @@ class Web(WebMixin, unittest.TestCase):
def test_PUT_NEWFILEURL_mkdirs(self):
d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
fn = self._foo_node
d.addCallback(self.failUnlessURIMatchesChild, fn, "newdir/new.txt")
d.addCallback(lambda res: self.failIfNodeHasChild(fn, "new.txt"))
d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, "newdir"))
d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "newdir/new.txt",
self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
self.NEWFILE_CONTENTS))
return d
@ -446,7 +454,7 @@ class Web(WebMixin, unittest.TestCase):
def test_DELETE_FILEURL(self):
d = self.DELETE(self.public_url + "/foo/bar.txt")
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, "bar.txt"))
self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
return d
def test_DELETE_FILEURL_missing(self):
@ -546,9 +554,9 @@ class Web(WebMixin, unittest.TestCase):
f.write(self.NEWFILE_CONTENTS)
f.close()
d = self.PUT(url, "")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "new.txt",
self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
self.NEWFILE_CONTENTS))
return d
@ -584,14 +592,14 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/foo/newdir/new.txt?t=upload&localfile=%s"
% urllib.quote(localfile), "")
d.addCallback(self.failUnlessURIMatchesChild,
self._foo_node, "newdir/new.txt")
self._foo_node, u"newdir/new.txt")
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, "new.txt"))
self.failIfNodeHasChild(self._foo_node, u"new.txt"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "newdir"))
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node,
"newdir/new.txt",
u"newdir/new.txt",
self.NEWFILE_CONTENTS))
return d
@ -691,16 +699,16 @@ class Web(WebMixin, unittest.TestCase):
def test_PUT_NEWDIRURL(self):
d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "newdir"))
d.addCallback(lambda res: self._foo_node.get("newdir"))
self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
def test_PUT_NEWDIRURL_replace(self):
d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "sub"))
d.addCallback(lambda res: self._foo_node.get("sub"))
self.failUnlessNodeHasChild(self._foo_node, u"sub"))
d.addCallback(lambda res: self._foo_node.get(u"sub"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
@ -711,33 +719,33 @@ class Web(WebMixin, unittest.TestCase):
"There was already a child by that name, and you asked me "
"to not replace it")
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "sub"))
d.addCallback(lambda res: self._foo_node.get("sub"))
d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
self.failUnlessNodeHasChild(self._foo_node, u"sub"))
d.addCallback(lambda res: self._foo_node.get(u"sub"))
d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
return d
def test_PUT_NEWDIRURL_mkdirs(self):
d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, "newdir"))
self.failIfNodeHasChild(self._foo_node, u"newdir"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "subdir"))
self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
d.addCallback(lambda res:
self._foo_node.get_child_at_path("subdir/newdir"))
self._foo_node.get_child_at_path(u"subdir/newdir"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
def test_DELETE_DIRURL(self):
d = self.DELETE(self.public_url + "/foo")
d.addCallback(lambda res:
self.failIfNodeHasChild(self.public_root, "foo"))
self.failIfNodeHasChild(self.public_root, u"foo"))
return d
def test_DELETE_DIRURL_missing(self):
d = self.DELETE(self.public_url + "/foo/missing")
d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self.public_root, "foo"))
self.failUnlessNodeHasChild(self.public_root, u"foo"))
return d
def test_DELETE_DIRURL_missing2(self):
@ -755,20 +763,22 @@ class Web(WebMixin, unittest.TestCase):
def _check(res):
names = [path for (path,node) in out]
self.failUnlessEqual(sorted(names),
[('foo',),
('foo','bar.txt'),
('foo','blockingfile'),
('foo', 'empty'),
('foo', 'sub'),
('foo','sub','baz.txt'),
('reedownlee',),
('reedownlee', 'nor'),
[(u'foo',),
(u'foo',u'bar.txt'),
(u'foo',u'blockingfile'),
(u'foo', u'empty'),
(u'foo', u"n\u00fc.txt"),
(u'foo', u'sub'),
(u'foo',u'sub',u'baz.txt'),
(u'reedownlee',),
(u'reedownlee', u'nor'),
])
subindex = names.index( ('foo', 'sub') )
bazindex = names.index( ('foo', 'sub', 'baz.txt') )
subindex = names.index( (u'foo', u'sub') )
bazindex = names.index( (u'foo', u'sub', u'baz.txt') )
self.failUnless(subindex < bazindex)
for path,node in out:
if path[-1] in ('bar.txt', 'blockingfile', 'baz.txt', 'nor'):
if path[-1] in (u'bar.txt', u"n\u00fc.txt", u'blockingfile',
u'baz.txt', u'nor'):
self.failUnless(interfaces.IFileNode.providedBy(node))
else:
self.failUnless(interfaces.IDirectoryNode.providedBy(node))
@ -839,18 +849,22 @@ class Web(WebMixin, unittest.TestCase):
return d
def failUnlessNodeKeysAre(self, node, expected_keys):
for k in expected_keys:
assert isinstance(k, unicode)
d = node.list()
def _check(children):
self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
d.addCallback(_check)
return d
def failUnlessNodeHasChild(self, node, name):
assert isinstance(name, unicode)
d = node.list()
def _check(children):
self.failUnless(name in children)
d.addCallback(_check)
return d
def failIfNodeHasChild(self, node, name):
assert isinstance(name, unicode)
d = node.list()
def _check(children):
self.failIf(name in children)
@ -858,6 +872,7 @@ class Web(WebMixin, unittest.TestCase):
return d
def failUnlessChildContentsAre(self, node, name, expected_contents):
assert isinstance(name, unicode)
d = node.get_child_at_path(name)
d.addCallback(lambda node: node.download_to_data())
def _check(contents):
@ -866,6 +881,7 @@ class Web(WebMixin, unittest.TestCase):
return d
def failUnlessChildURIIs(self, node, name, expected_uri):
assert isinstance(name, unicode)
d = node.get_child_at_path(name)
def _check(child):
self.failUnlessEqual(child.get_uri(), expected_uri.strip())
@ -873,6 +889,7 @@ class Web(WebMixin, unittest.TestCase):
return d
def failUnlessURIMatchesChild(self, got_uri, node, name):
assert isinstance(name, unicode)
d = node.get_child_at_path(name)
def _check(child):
self.failUnlessEqual(got_uri.strip(), child.get_uri())
@ -896,15 +913,15 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/newdir?t=upload&localdir=%s"
% urllib.quote(localdir), "")
pr = self.public_root
d.addCallback(lambda res: self.failUnlessNodeHasChild(pr, "newdir"))
d.addCallback(lambda res: pr.get("newdir"))
d.addCallback(lambda res: self.failUnlessNodeHasChild(pr, u"newdir"))
d.addCallback(lambda res: pr.get(u"newdir"))
d.addCallback(self.failUnlessNodeKeysAre,
["one", "two", "three", "zap.zip"])
d.addCallback(lambda res: pr.get_child_at_path("newdir/one"))
d.addCallback(self.failUnlessNodeKeysAre, ["sub"])
d.addCallback(lambda res: pr.get_child_at_path("newdir/three"))
d.addCallback(self.failUnlessNodeKeysAre, ["foo.txt", "bar.txt"])
d.addCallback(lambda res: pr.get_child_at_path("newdir/three/bar.txt"))
[u"one", u"two", u"three", u"zap.zip"])
d.addCallback(lambda res: pr.get_child_at_path(u"newdir/one"))
d.addCallback(self.failUnlessNodeKeysAre, [u"sub"])
d.addCallback(lambda res: pr.get_child_at_path(u"newdir/three"))
d.addCallback(self.failUnlessNodeKeysAre, [u"foo.txt", u"bar.txt"])
d.addCallback(lambda res: pr.get_child_at_path(u"newdir/three/bar.txt"))
d.addCallback(lambda barnode: barnode.download_to_data())
d.addCallback(lambda contents:
self.failUnlessEqual(contents,
@ -945,16 +962,16 @@ class Web(WebMixin, unittest.TestCase):
% urllib.quote(localdir),
"")
fn = self._foo_node
d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, "subdir"))
d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir"))
d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"subdir"))
d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir"))
d.addCallback(self.failUnlessNodeKeysAre,
["one", "two", "three", "zap.zip"])
d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir/one"))
d.addCallback(self.failUnlessNodeKeysAre, ["sub"])
d.addCallback(lambda res: fn.get_child_at_path("subdir/newdir/three"))
d.addCallback(self.failUnlessNodeKeysAre, ["foo.txt", "bar.txt"])
[u"one", u"two", u"three", u"zap.zip"])
d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir/one"))
d.addCallback(self.failUnlessNodeKeysAre, [u"sub"])
d.addCallback(lambda res: fn.get_child_at_path(u"subdir/newdir/three"))
d.addCallback(self.failUnlessNodeKeysAre, [u"foo.txt", u"bar.txt"])
d.addCallback(lambda res:
fn.get_child_at_path("subdir/newdir/three/bar.txt"))
fn.get_child_at_path(u"subdir/newdir/three/bar.txt"))
d.addCallback(lambda barnode: barnode.download_to_data())
d.addCallback(lambda contents:
self.failUnlessEqual(contents,
@ -976,12 +993,28 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="upload",
file=("new.txt", self.NEWFILE_CONTENTS))
fn = self._foo_node
d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
self.NEWFILE_CONTENTS))
return d
def test_POST_upload_unicode(self):
filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
target_url = self.public_url + "/foo/" + filename.encode("utf-8")
d = self.POST(self.public_url + "/foo", t="upload",
file=(filename, self.NEWFILE_CONTENTS))
fn = self._foo_node
d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, filename,
self.NEWFILE_CONTENTS))
d.addCallback(lambda res: self.GET(target_url))
d.addCallback(lambda contents: self.failUnlessEqual(contents,
self.NEWFILE_CONTENTS,
contents))
return d
def test_POST_upload_no_link(self):
d = self.POST("/uri", t="upload",
file=("new.txt", self.NEWFILE_CONTENTS))
@ -1026,11 +1059,11 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
file=("new.txt", self.NEWFILE_CONTENTS))
fn = self._foo_node
d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
self.NEWFILE_CONTENTS))
d.addCallback(lambda res: self._foo_node.get("new.txt"))
d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
def _got(newnode):
self.failUnless(IMutableFileNode.providedBy(newnode))
self.failUnless(newnode.is_mutable())
@ -1044,11 +1077,11 @@ class Web(WebMixin, unittest.TestCase):
self.POST(self.public_url + "/foo", t="upload",
mutable="true",
file=("new.txt", NEWER_CONTENTS)))
d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
NEWER_CONTENTS))
d.addCallback(lambda res: self._foo_node.get("new.txt"))
d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
def _got2(newnode):
self.failUnless(IMutableFileNode.providedBy(newnode))
self.failUnless(newnode.is_mutable())
@ -1085,9 +1118,9 @@ class Web(WebMixin, unittest.TestCase):
d.addCallback(_parse_overwrite_form_and_submit)
d.addBoth(self.shouldRedirect, urllib.quote(self.public_url + "/foo/"))
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
EVEN_NEWER_CONTENTS))
d.addCallback(lambda res: self._foo_node.get("new.txt"))
d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
def _got3(newnode):
self.failUnless(IMutableFileNode.providedBy(newnode))
self.failUnless(newnode.is_mutable())
@ -1101,9 +1134,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="upload",
file=("bar.txt", self.NEWFILE_CONTENTS))
fn = self._foo_node
d.addCallback(self.failUnlessURIMatchesChild, fn, "bar.txt")
d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "bar.txt",
self.failUnlessChildContentsAre(fn, u"bar.txt",
self.NEWFILE_CONTENTS))
return d
@ -1144,7 +1177,7 @@ class Web(WebMixin, unittest.TestCase):
d.addBoth(self.shouldRedirect, "/THERE")
fn = self._foo_node
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
self.NEWFILE_CONTENTS))
return d
@ -1152,9 +1185,9 @@ class Web(WebMixin, unittest.TestCase):
fn = self._foo_node
d = self.POST(self.public_url + "/foo", t="upload",
name="new.txt", file=self.NEWFILE_CONTENTS)
d.addCallback(self.failUnlessURIMatchesChild, fn, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(fn, "new.txt",
self.failUnlessChildContentsAre(fn, u"new.txt",
self.NEWFILE_CONTENTS))
return d
@ -1169,13 +1202,14 @@ class Web(WebMixin, unittest.TestCase):
# make sure that nothing was added
d.addCallback(lambda res:
self.failUnlessNodeKeysAre(self._foo_node,
["bar.txt", "blockingfile",
"empty", "sub"]))
[u"bar.txt", u"blockingfile",
u"empty", u"n\u00fc.txt",
u"sub"]))
return d
def test_POST_mkdir(self): # return value?
d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
d.addCallback(lambda res: self._foo_node.get("newdir"))
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
@ -1223,7 +1257,7 @@ class Web(WebMixin, unittest.TestCase):
def test_POST_mkdir_replace(self): # return value?
d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
d.addCallback(lambda res: self._foo_node.get("sub"))
d.addCallback(lambda res: self._foo_node.get(u"sub"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
@ -1234,8 +1268,8 @@ class Web(WebMixin, unittest.TestCase):
"409 Conflict",
"There was already a child by that name, and you asked me "
"to not replace it")
d.addCallback(lambda res: self._foo_node.get("sub"))
d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
d.addCallback(lambda res: self._foo_node.get(u"sub"))
d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
return d
def test_POST_mkdir_no_replace_field(self): # return value?
@ -1245,15 +1279,15 @@ class Web(WebMixin, unittest.TestCase):
"409 Conflict",
"There was already a child by that name, and you asked me "
"to not replace it")
d.addCallback(lambda res: self._foo_node.get("sub"))
d.addCallback(self.failUnlessNodeKeysAre, ["baz.txt"])
d.addCallback(lambda res: self._foo_node.get(u"sub"))
d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
return d
def test_POST_mkdir_whendone_field(self):
d = self.POST(self.public_url + "/foo",
t="mkdir", name="newdir", when_done="/THERE")
d.addBoth(self.shouldRedirect, "/THERE")
d.addCallback(lambda res: self._foo_node.get("newdir"))
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
@ -1261,25 +1295,25 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo?when_done=/THERE",
t="mkdir", name="newdir")
d.addBoth(self.shouldRedirect, "/THERE")
d.addCallback(lambda res: self._foo_node.get("newdir"))
d.addCallback(lambda res: self._foo_node.get(u"newdir"))
d.addCallback(self.failUnlessNodeKeysAre, [])
return d
def test_POST_put_uri(self):
contents, n, newuri = self.makefile(8)
d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "new.txt")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "new.txt",
self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
contents))
return d
def test_POST_put_uri_replace(self):
contents, n, newuri = self.makefile(8)
d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, "bar.txt")
d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
contents))
return d
@ -1313,7 +1347,7 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
d.addCallback(lambda res: self._foo_node.list())
def _check(children):
self.failIf("bar.txt" in children)
self.failIf(u"bar.txt" in children)
d.addCallback(_check)
return d
@ -1321,9 +1355,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="rename",
from_name="bar.txt", to_name='wibble.txt')
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, "bar.txt"))
self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "wibble.txt"))
self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
d.addCallback(self.failUnlessIsBarDotTxt)
d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
@ -1335,9 +1369,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url + "/foo", t="rename",
from_name="bar.txt", to_name='empty')
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, "bar.txt"))
self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "empty"))
self.failUnlessNodeHasChild(self._foo_node, u"empty"))
d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
d.addCallback(self.failUnlessIsBarDotTxt)
d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
@ -1384,7 +1418,7 @@ class Web(WebMixin, unittest.TestCase):
"to_name= may not contain a slash",
)
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "bar.txt"))
self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
d.addCallback(lambda res: self.POST(self.public_url, t="rename",
from_name="foo/bar.txt", to_name='george.txt'))
d.addBoth(self.shouldFail, error.Error,
@ -1393,11 +1427,11 @@ class Web(WebMixin, unittest.TestCase):
"from_name= may not contain a slash",
)
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self.public_root, "foo"))
self.failUnlessNodeHasChild(self.public_root, u"foo"))
d.addCallback(lambda res:
self.failIfNodeHasChild(self.public_root, "george.txt"))
self.failIfNodeHasChild(self.public_root, u"george.txt"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self._foo_node, "bar.txt"))
self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
d.addCallback(lambda res: self.GET(self.public_url + "/foo?t=json"))
d.addCallback(self.failUnlessIsFooJSON)
return d
@ -1406,9 +1440,9 @@ class Web(WebMixin, unittest.TestCase):
d = self.POST(self.public_url, t="rename",
from_name="foo", to_name='plunk')
d.addCallback(lambda res:
self.failIfNodeHasChild(self.public_root, "foo"))
self.failIfNodeHasChild(self.public_root, u"foo"))
d.addCallback(lambda res:
self.failUnlessNodeHasChild(self.public_root, "plunk"))
self.failUnlessNodeHasChild(self.public_root, u"plunk"))
d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
d.addCallback(self.failUnlessIsFooJSON)
return d
@ -1497,7 +1531,7 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "new.txt",
self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
contents))
return d
@ -1506,7 +1540,7 @@ class Web(WebMixin, unittest.TestCase):
d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
d.addCallback(lambda res:
self.failUnlessChildContentsAre(self._foo_node, "bar.txt",
self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
contents))
return d

View File

@ -233,6 +233,8 @@ class Directory(rend.Page):
def render_row(self, ctx, data):
name, (target, metadata) = data
name = name.encode("utf-8")
assert not isinstance(name, unicode)
if self._dirnode.is_readonly():
delete = "-"
@ -480,9 +482,10 @@ class WebDownloadTarget:
if self._save_to_file is not None:
# tell the browser to save the file rather display it
# TODO: quote save_to_file properly
filename = self._save_to_file.encode("utf-8")
self._req.setHeader("content-disposition",
'attachment; filename="%s"'
% self._save_to_file)
% filename)
def write(self, data):
self._req.write(data)
@ -526,16 +529,16 @@ class FileDownloader(resource.Resource):
def render(self, req):
gte = static.getTypeAndEncoding
type, encoding = gte(self._name,
static.File.contentTypes,
static.File.contentEncodings,
defaultType="text/plain")
ctype, encoding = gte(self._name,
static.File.contentTypes,
static.File.contentEncodings,
defaultType="text/plain")
save_to_file = None
if get_arg(req, "save", False):
# TODO: make the API specification clear: should "save=" or
# "save=false" count?
save_to_file = self._name
wdt = WebDownloadTarget(req, type, encoding, save_to_file)
wdt = WebDownloadTarget(req, ctype, encoding, save_to_file)
d = self._filenode.download(wdt)
# exceptions during download are handled by the WebDownloadTarget
d.addErrback(lambda why: None)
@ -683,6 +686,7 @@ class LocalDirectoryDownloader(resource.Resource, DirnodeWalkerMixin):
self._localdir = localdir
def _handle(self, path, node, metadata):
path = tuple([p.encode("utf-8") for p in path])
localfile = os.path.join(self._localdir, os.sep.join(path))
if IDirectoryNode.providedBy(node):
fileutil.make_dirs(localfile)
@ -807,6 +811,8 @@ class POSTHandler(rend.Page):
t = get_arg(req, "t")
assert t is not None
charset = get_arg(req, "_charset", "utf-8")
name = get_arg(req, "name", None)
if name and "/" in name:
req.setResponseCode(http.BAD_REQUEST)
@ -814,6 +820,8 @@ class POSTHandler(rend.Page):
return "name= may not contain a slash"
if name is not None:
name = name.strip()
name = name.decode(charset)
assert isinstance(name, unicode)
# we allow the user to delete an empty-named file, but not to create
# them, since that's an easy and confusing mistake to make
@ -849,12 +857,16 @@ class POSTHandler(rend.Page):
d = self._node.delete(name)
d.addCallback(lambda res: "thing deleted")
elif t == "rename":
from_name = 'from_name' in req.fields and req.fields["from_name"].value
from_name = get_arg(req, "from_name")
if from_name is not None:
from_name = from_name.strip()
to_name = 'to_name' in req.fields and req.fields["to_name"].value
from_name = from_name.decode(charset)
assert isinstance(from_name, unicode)
to_name = get_arg(req, "to_name")
if to_name is not None:
to_name = to_name.strip()
to_name = to_name.decode(charset)
assert isinstance(to_name, unicode)
if not from_name or not to_name:
raise RuntimeError("rename requires from_name and to_name")
if not IDirectoryNode.providedBy(self._node):
@ -877,13 +889,17 @@ class POSTHandler(rend.Page):
d.addCallback(lambda res: "thing renamed")
elif t == "upload":
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
# this prohibts empty, missing, and all-whitespace filenames
raise RuntimeError("upload requires a name")
name = name.decode(charset)
assert isinstance(name, unicode)
if "mutable" in req.fields:
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
raise RuntimeError("upload-mutable requires a name")
# SDMF: files are small, and we can only upload data.
contents.file.seek(0)
data = contents.file.read()
@ -910,12 +926,6 @@ class POSTHandler(rend.Page):
return d2
d.addCallback(_checked)
else:
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
raise RuntimeError("upload requires a name")
uploadable = FileHandle(contents.file)
d = self._check_replacement(name)
d.addCallback(lambda res: self._node.add_file(name, uploadable))
@ -974,13 +984,13 @@ class DELETEHandler(rend.Page):
d = self._node.delete(self._name)
def _done(res):
# what should this return??
return "%s deleted" % self._name
return "%s deleted" % self._name.encode("utf-8")
d.addCallback(_done)
def _trap_missing(f):
f.trap(KeyError)
req.setResponseCode(http.NOT_FOUND)
req.setHeader("content-type", "text/plain")
return "no such child %s" % self._name
return "no such child %s" % self._name.encode("utf-8")
d.addErrback(_trap_missing)
return d
@ -1098,7 +1108,9 @@ class PUTHandler(rend.Page):
return d
def _upload_localdir(self, node, localdir):
# build up a list of files to upload
# build up a list of files to upload. TODO: for now, these files and
# directories must have UTF-8 encoded filenames: anything else will
# cause the upload to break.
all_files = []
all_dirs = []
msg = "No files to upload! %s is empty" % localdir
@ -1112,9 +1124,13 @@ class PUTHandler(rend.Page):
relative_root = root[len(localdir)+1:]
path = tuple(relative_root.split(os.sep))
for d in dirs:
all_dirs.append(path + (d,))
this_dir = path + (d,)
this_dir = tuple([p.decode("utf-8") for p in this_dir])
all_dirs.append(this_dir)
for f in files:
all_files.append(path + (f,))
this_file = path + (f,)
this_file = tuple([p.decode("utf-8") for p in this_file])
all_files.append(this_file)
d = defer.succeed(msg)
for dir in all_dirs:
if dir:
@ -1190,7 +1206,7 @@ class VDrive(rend.Page):
def locateChild(self, ctx, segments):
req = inevow.IRequest(ctx)
method = req.method
path = segments
path = tuple([seg.decode("utf-8") for seg in segments])
t = get_arg(req, "t", "")
localfile = get_arg(req, "localfile", None)