filetree: refactor INode serialization, start on tests

This commit is contained in:
Brian Warner 2007-01-20 04:52:53 -07:00
parent e843abe542
commit c808d5a5ef
7 changed files with 104 additions and 39 deletions

View File

@ -12,7 +12,6 @@ class BaseDataNode(object):
raise NotImplementedError # must be provided by subclass
def serialize_node(self):
return "%s:%s" % (self.prefix, self.get_base_data())
def populate_node(self, data, node_maker):
assert data.startswith(self.prefix + ":")
self.set_base_data(data[len(self.prefix)+1:])
def populate_node(self, body, node_maker):
self.set_base_data(body)

View File

@ -124,6 +124,7 @@ class _DirectorySubTree(object):
def new(self):
# create a new, empty directory
self.root = SubTreeNode(self)
self.mutable = True # sure, why not
@ -135,7 +136,7 @@ class _DirectorySubTree(object):
# to populate_from_data()
raise NotImplementedError
def populate_from_data(self, data, node_maker):
def _populate_from_data(self, data, node_maker):
self.root = SubTreeNode(self)
self.root.populate_node(bencode.bdecode(data), node_maker)
return self
@ -176,6 +177,35 @@ class _DirectorySubTree(object):
break
return (found_path, node, remaining_path)
class LocalFileSubTreeNode(BaseDataNode):
prefix = "LocalFileDirectory"
def new(self, filename):
self.filename = filename
def get_base_data(self):
return self.filename
def set_base_data(self, data):
self.filename = data
class LocalFileSubTree(_DirectorySubTree):
def new(self, filename):
self.filename = filename
_DirectorySubTree.new(self)
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
self.mutable = True # probably
self.filename = node.filename
f = open(self.filename, "rb")
data = f.read()
f.close()
return defer.succeed(self._populate_from_data(node_maker))
def update(self, prepath, work_queue):
f = open(self.filename, "wb")
self.serialize_to_file(f)
f.close()
class CHKDirectorySubTreeNode(BaseDataNode):
implements(ICHKDirectoryNode)
prefix = "CHKDirectory"
@ -199,7 +229,7 @@ class CHKDirectorySubTree(_DirectorySubTree):
assert ICHKDirectoryNode(node)
self.mutable = parent_is_mutable
d = downloader.download(node.get_uri(), download.Data())
d.addCallback(self.populate_from_data, node_maker)
d.addCallback(self._populate_from_data, node_maker)
return d
def update(self, prepath, work_queue):
@ -228,10 +258,8 @@ class SSKDirectorySubTreeNode(object):
def serialize_node(self):
data = (self.read_cap, self.write_cap)
return "%s:%s" % (self.prefix, bencode.bencode(data))
def populate_node(self, data, node_maker):
assert data.startswith(self.prefix + ":")
capdata = data[len(self.prefix)+1:]
self.read_cap, self.write_cap = bencode.bdecode(capdata)
def populate_node(self, body, node_maker):
self.read_cap, self.write_cap = bencode.bdecode(body)
def get_read_capability(self):
return self.read_cap
@ -252,7 +280,7 @@ class SSKDirectorySubTree(_DirectorySubTree):
self.write_capability = node.get_write_capability()
self.mutable = bool(self.write_capability)
d = downloader.download_ssk(self.read_capability, download.Data())
d.addCallback(self.populate_from_data, node_maker)
d.addCallback(self._populate_from_data, node_maker)
return d
def set_version(self, version):

View File

@ -4,16 +4,21 @@ from zope.interface import Interface
class INode(Interface):
"""This is some sort of retrievable node. All objects which implement
other I*Node interfaces also implement this one."""
# the INode-implementing class must have an attribute named .prefix which
# contains a string.
def serialize_node():
"""Return a data structure which contains enough information to build
this node again in the future (by calling
vdrive.make_node_from_serialized(). For IDirectoryNodes, this will be
a list. For all other nodes this will be a string."""
def populate_node(data, node_maker):
"""vdrive.make_node_from_serialized() will first use the prefix
inside 'data' to decide what kind of Node to create. It will then
call this function to populate the new Node from the data returned by
serialize_node."""
a list. For all other nodes this will be a string of the form
'prefix:body', where 'prefix' must be the same as the class attribute
.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."""
class IFileNode(Interface):
"""This is a file which can be retrieved."""

View File

@ -14,6 +14,9 @@ all_openable_subtree_types = [
redirect.QueenOrLocalFileRedirection,
]
# the Opener can turn an INode (which describes a subtree, like a directory
# or a redirection) into the fully-populated subtree.
class Opener(object):
implements(interfaces.IOpener)
def __init__(self, queen, downloader):

View File

@ -37,6 +37,10 @@ class _BaseRedirection(object):
class LocalFileRedirection(_BaseRedirection):
stype = "LocalFileRedirection"
def new(self, handle, child_node):
self.filename = handle
_BaseRedirection.new(self, child_node)
def populate_from_node(self, node, parent_is_mutable, node_maker, downloader):
# return a Deferred that fires (with self) when this node is ready
# for use

View File

@ -28,9 +28,29 @@ class VirtualDrive(object):
self.workqueue = workqueue
workqueue.set_vdrive(self)
# TODO: queen?
self.queen = None
self.opener = opener.Opener(self.queen, downloader)
self.root_node = root_node
# these are called when loading and creating nodes
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
# 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)
# maybe include parent_is_mutable?
assert isinstance(serialized, str)
prefix, body = serialized.split(":", 2)
for node_class in all_node_types:
if prefix == node_class.prefix:
node = node_class()
node.populate_node(body, self.make_node_from_serialized)
return node
raise RuntimeError("unable to handle subtree type '%s'" % prefix)
# these methods are used to walk through our subtrees
def _get_root(self):
@ -127,25 +147,6 @@ class VirtualDrive(object):
d.addCallback(_got_closest)
return d
# these are called when loading and creating nodes
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
# 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)
# maybe include parent_is_mutable?
assert isinstance(serialized, str)
colon = serialized.index(":")
prefix = serialized[:colon]
for node_class in all_node_types:
if prefix == node_class.prefix:
node = node_class()
node.populate_node(serialized, self.make_node_from_serialized)
return node
raise RuntimeError("unable to handle subtree type '%s'" % prefix)
# these are called by the workqueue
def add(self, path, new_node):

View File

@ -1,17 +1,17 @@
"""
from zope.interface import implements
from twisted.trial import unittest
from twisted.internet import defer
from allmydata.filetree.interfaces import IOpener, IDirectoryNode
from allmydata.filetree.directory import (ImmutableDirectorySubTree,
from allmydata.filetree.directory import (#ImmutableDirectorySubTree,
SubTreeNode,
CHKDirectorySubTree)
from allmydata.filetree.specification import (CHKFileSpecification,
CHKDirectorySpecification)
#from allmydata.filetree.specification import (CHKFileSpecification,
# CHKDirectorySpecification)
from allmydata import workqueue
from cStringIO import StringIO
"""
class FakeOpener(object):
implements(IOpener)
def __init__(self, objects={}):
@ -311,3 +311,28 @@ del MultipleSubTrees
class Redirect(unittest.TestCase):
pass
"""
from allmydata.filetree import directory, redirect, vdrive
class Load(unittest.TestCase):
def testCreate(self):
# create some stuff, see if we can import everything
wq = workqueue.WorkQueue("test_filetree_new/Load/1.workqueue")
dl = None
# create an empty directory (stored locally) as our root
root = directory.LocalFileSubTree()
root.new("dirtree.save")
# and a node to point to it
root_node = directory.LocalFileSubTreeNode()
root_node.new("dirtree.save")
v = vdrive.VirtualDrive(wq, dl, root_node)
def start():
root_node = redirect.LocalFileRedirectionNode()
root_node.new("handle", dirtree)
root = redirect.LocalFileRedirection()
# wow, bootstrapping is hard