from twisted.internet import defer
from cStringIO import StringIO
from allmydata.filetree.interfaces import (
- INode, IDirectoryNode, ISubTree,
+ INode, INodeMaker, IDirectoryNode, ISubTree,
ICHKDirectoryNode, ISSKDirectoryNode,
NoSuchChildError,
)
# each time the vdrive changes, update the local drive to match, and
# vice versa.
-# from the itertools 'recipes' page
-from itertools import izip, tee
-def pairwise(iterable):
- "s -> (s0,s1), (s1,s2), (s2, s3), ..."
- a, b = tee(iterable)
- try:
- b.next()
- except StopIteration:
- pass
+from itertools import islice, izip
+def in_pairs(iterable):
+ "s -> (s0,s1), (s2,s3), (s4,s5), ..."
+ a = islice(iterable, 0, None, 2)
+ b = islice(iterable, 1, None, 2)
return izip(a, b)
+
class SubTreeNode:
implements(INode, IDirectoryNode)
data.append(self.children[name].serialize_node())
return data
- def populate_node(self, data, node_maker):
+ def populate_dirnode(self, data, node_maker):
+ assert INodeMaker(node_maker)
assert len(data) % 2 == 0
- for (name, child_data) in pairwise(data):
+ for (name, child_data) in in_pairs(data):
if isinstance(child_data, (list, tuple)):
child = SubTreeNode(self.enclosing_tree)
- child.populate_node(child_data)
+ child.populate_dirnode(child_data, node_maker)
else:
assert isinstance(child_data, str)
- child = node_maker(child_data)
+ child = node_maker.make_node_from_serialized(child_data)
self.children[name] = child
def _populate_from_data(self, data, node_maker):
self.root = SubTreeNode(self)
- self.root.populate_node(bencode.bdecode(data), node_maker)
+ self.root.populate_dirnode(bencode.bdecode(data), node_maker)
return self
def serialize_subtree_to_file(self, f):
f = open(self.filename, "rb")
data = f.read()
f.close()
- return defer.succeed(self._populate_from_data(node_maker))
+ d = defer.succeed(data)
+ d.addCallback(self._populate_from_data, node_maker)
+ return d
def create_node_now(self):
return LocalFileSubTreeNode().new(self.filename)
def _update(self):
f = open(self.filename, "wb")
- self.serialize_to_file(f)
+ self.serialize_subtree_to_file(f)
f.close()
def update_now(self, uploader):
def update_now(self, uploader):
f = StringIO()
- self.serialize_to_file(f)
+ self.serialize_subtree_to_file(f)
data = f.getvalue()
d = uploader.upload_data(data)
def _uploaded(uri):
# this is the CHK form
old_uri = self.uri
f, filename = workqueue.create_tempfile(".chkdir")
- self.serialize_to_file(f)
+ self.serialize_subtree_to_file(f)
f.close()
boxname = workqueue.create_boxname()
workqueue.add_upload_chk(filename, boxname)
raise RuntimeError("This SSKDirectorySubTree is not mutable")
f = StringIO()
- self.serialize_to_file(f)
+ self.serialize_subtree_to_file(f)
data = f.getvalue()
self.version += 1
def update(self, workqueue):
# this is the SSK form
f, filename = workqueue.create_tempfile(".sskdir")
- self.serialize_to_file(f)
+ self.serialize_subtree_to_file(f)
f.close()
oldversion = self.version
.prefix ."""
def populate_node(body, node_maker):
"""vdrive.make_node_from_serialized() will first use the prefix from
- the .prefix attribute to decide what kind of Node to create. It will
- then call this function with the body to populate the new Node."""
+ the .prefix attribute to decide what kind of Node to create. They
+ will then call this populate_node() method with the body to populate
+ the new Node. 'node_maker' provides INodeMaker, which provides that
+ same make_node_from_serialized function to create any internal child
+ nodes that might be necessary."""
class IFileNode(Interface):
"""This is a file which can be retrieved."""
(SSKDirectorySubTrees and redirections).
"""
+class INodeMaker(Interface):
+ def make_node_from_serialized(serialized):
+ """Turn a string into an INode, which contains information about
+ the file or directory (like a URI), but does not contain the actual
+ contents. An IOpener can be used later to retrieve the contents
+ (which means downloading the file if this is an IFileNode, or
+ perhaps creating a new subtree from the contents)."""
+
+class ISubTreeMaker(Interface):
+ def make_subtree_from_node(node, parent_is_mutable):
+ """Turn an INode into an ISubTree (using an internal opener to
+ download the data, if necessary).
+ This returns a Deferred that fires with the ISubTree instance.
+ """
+
#class IMutableSubTree(Interface):
# def mutation_affects_parent():
# """This returns True for CHK nodes where you must inform the parent
from twisted.internet import defer
from allmydata.filetree import interfaces, directory, redirect
#from allmydata.filetree.file import CHKFile, MutableSSKFile, ImmutableSSKFile
-from allmydata.filetree.interfaces import INode, IDirectoryNode
+from allmydata.filetree.interfaces import INode, IDirectoryNode, INodeMaker
all_openable_subtree_types = [
directory.LocalFileSubTree,
def _create(self, node, parent_is_mutable, node_maker):
assert INode(node)
+ assert INodeMaker(node_maker)
for subtree_class in all_openable_subtree_types:
if isinstance(node, subtree_class.node_class):
subtree = subtree_class()
def open(self, node, parent_is_mutable, node_maker):
assert INode(node)
assert not isinstance(node, IDirectoryNode)
+ assert INodeMaker(node_maker)
# is it in cache? To check this we need to use the node's serialized
# form, since nodes are instances and don't compare by value
from zope.interface import implements
from twisted.internet import defer
-from allmydata.filetree.interfaces import ISubTree
+from allmydata.filetree.interfaces import ISubTree, INodeMaker
from allmydata.filetree.basenode import BaseDataNode
from allmydata.util import bencode
return self.child_node.serialize_node()
def _populate_from_data(self, data, node_maker):
- self.child_node = node_maker(data)
+ assert INodeMaker(node_maker)
+ self.child_node = node_maker.make_node_from_serialized(data)
return self
# note: we don't cache the contents of the file. TODO: consider
# doing this based upon mtime. It is important that we be able to
# notice if the file has been changed.
- return defer.succeed(self._populate_from_data(data, node_maker))
+ d = defer.succeed(data)
+ d.addCallback(self._populate_from_data, node_maker)
+ return d
def is_mutable(self):
return True
# TODO: queen?
# TODO: pubsub so we can cache the queen's results
d = self._queen.callRemote("lookup_handle", self.handle)
- d.addCallback(self._choose_winner, local_version_and_data, node_maker)
+ d.addCallback(self._choose_winner, local_version_and_data)
+ d.addCallback(self._populate_from_data, node_maker)
return d
- def _choose_winner(self, queen_version_and_data, local_version_and_data, node_maker):
+ def _choose_winner(self, queen_version_and_data, local_version_and_data):
queen_version, queen_data = bencode.bdecode(queen_version_and_data)
local_version, local_data = bencode.bdecode(local_version_and_data)
if queen_version > local_version:
data = local_data
self.version = local_version
# NOTE: two layers of bencoding here, TODO
- return self._populate_from_data(data, node_maker)
+ return data
def is_mutable(self):
return True
from zope.interface import implements
-from allmydata.filetree import opener, directory, redirect
+from allmydata.filetree import opener, directory, file, redirect
from allmydata.filetree.interfaces import (
- IVirtualDrive, INode, ISubTree, IFileNode, IDirectoryNode,
+ IVirtualDrive, INodeMaker, INode, ISubTree, IFileNode, IDirectoryNode,
NoSuchDirectoryError, NoSuchChildError, PathAlreadyExistsError,
PathDoesNotExistError,
)
# node specification strings (found inside the serialized form of subtrees)
# into Nodes (which live in the in-RAM form of subtrees).
all_node_types = [
+ directory.LocalFileSubTreeNode,
directory.CHKDirectorySubTreeNode,
directory.SSKDirectorySubTreeNode,
+ file.CHKFileNode,
+ file.SSKFileNode,
redirect.LocalFileRedirectionNode,
redirect.QueenRedirectionNode,
redirect.HTTPRedirectionNode,
]
class VirtualDrive(object):
- implements(IVirtualDrive)
+ implements(IVirtualDrive, INodeMaker)
def __init__(self, workqueue, downloader, root_node):
assert INode(root_node)
self.root_node = root_node
# these are called when loading and creating nodes
+
+ # INodeMaker
def make_node_from_serialized(self, serialized):
# this turns a string into an INode, which contains information about
# the file or directory (like a URI), but does not contain the actual
for node_class in all_node_types:
if prefix == node_class.prefix:
node = node_class()
- node.populate_node(body, self.make_node_from_serialized)
+ node.populate_node(body, self)
return node
- raise RuntimeError("unable to handle subtree type '%s'" % prefix)
+ raise RuntimeError("unable to handle node type '%s'" % prefix)
+ # ISubTreeMaker
def make_subtree_from_node(self, node, parent_is_mutable):
assert INode(node)
- return self.opener.open(node, parent_is_mutable,
- self.make_subtree_from_node)
+ return self.opener.open(node, parent_is_mutable, self)
# these methods are used to walk through our subtrees
#from zope.interface import implements
from twisted.trial import unittest
-#from twisted.internet import defer
+from twisted.internet import defer
#from allmydata.filetree.interfaces import IOpener, IDirectoryNode
#from allmydata.filetree.directory import (ImmutableDirectorySubTree,
# SubTreeNode,
import os.path
from allmydata.filetree import directory, redirect, vdrive
-from allmydata.filetree.interfaces import (ISubTree, INode, IDirectoryNode)
+from allmydata.filetree.interfaces import (ISubTree, INode, IDirectoryNode, IFileNode)
from allmydata.filetree.file import CHKFileNode
+from allmydata.util import bencode
+
+class InPairs(unittest.TestCase):
+ def test_in_pairs(self):
+ l = range(8)
+ pairs = list(directory.in_pairs(l))
+ self.failUnlessEqual(pairs, [(0,1), (2,3), (4,5), (6,7)])
class Stuff(unittest.TestCase):
del root, subdir1, subdir2, subdir3, subdir4
# leaving file1 for later use
- # now serialize it and reconstruct it
+ # now serialize it and examine the results
f = StringIO()
subtree.serialize_subtree_to_file(f)
data = f.getvalue()
#print data
+ unpacked = bencode.bdecode(data)
+ #print unpacked
+ del f, data, unpacked
node = subtree.create_node_now()
self.failUnless(isinstance(node, directory.LocalFileSubTreeNode))
self.failUnless(isinstance(node_s, str))
self.failUnless(node_s.startswith("LocalFileDirectory:"))
self.failUnless("dirtree.save" in node_s)
+ del node, node_s
+
+ d = defer.maybeDeferred(subtree.update_now, None)
+ def _updated(node):
+ # now reconstruct it
+ return v.make_subtree_from_node(node, False)
+ d.addCallback(_updated)
- # now reconstruct it
- d = v.make_subtree_from_node(node, False)
def _opened(new_subtree):
res = new_subtree.get_node_for_path([])
(found_path, root, remaining_path) = res
self.failUnless(IDirectoryNode.providedBy(root))
self.failUnlessEqual(root.list(), ["foo.txt", "subdir1"])
file1a = root.get("foo.txt")
- self.failUnless(isinstance(CHKFileNode, file1a))
+ self.failUnless(INode(file1a))
+ self.failUnless(isinstance(file1a, CHKFileNode))
+ self.failUnless(IFileNode(file1a))
self.failUnlessEqual(file1a.get_uri(), "uri1")
subdir1 = root.get("subdir1")
subdir2 = subdir1.get("subdir2")
self.failUnlessEqual(subdir2.list(), [])
d.addCallback(_opened)
return d
- testDirectory.todo = "not working yet"
def testVdrive(self):
# create some stuff, see if we can import everything