From 430b3a03fccf310a220816e1145d7750f6e4ccd9 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Sun, 21 Jan 2007 15:01:34 -0700 Subject: [PATCH] move upload/download interfaces to allmydata.interfaces, let SubTreeMaker assert IDownloader-ness of its 'downloader' argument --- src/allmydata/download.py | 27 ++-------------- src/allmydata/filetree/vdrive.py | 2 ++ src/allmydata/interfaces.py | 43 +++++++++++++++++++++++++ src/allmydata/test/test_filetree_new.py | 13 +++++--- src/allmydata/upload.py | 18 ++--------- src/allmydata/webish.py | 2 +- 6 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/allmydata/download.py b/src/allmydata/download.py index 10c77895..5051a1e4 100644 --- a/src/allmydata/download.py +++ b/src/allmydata/download.py @@ -1,6 +1,6 @@ import os, sha -from zope.interface import Interface, implements +from zope.interface import implements from twisted.python import failure, log from twisted.internet import defer from twisted.application import service @@ -9,6 +9,7 @@ from allmydata.util import idlib, bencode from allmydata.util.deferredutil import DeferredListShouldSucceed from allmydata import codec from allmydata.uri import unpack_uri +from allmydata.interfaces import IDownloadTarget, IDownloader class NotEnoughPeersError(Exception): pass @@ -147,26 +148,6 @@ class FileDownloader: def netstring(s): return "%d:%s," % (len(s), s) -class IDownloadTarget(Interface): - def open(): - """Called before any calls to write() or close().""" - def write(data): - pass - def close(): - pass - def fail(): - """fail() is called to indicate that the download has failed. No - further methods will be invoked on the IDownloadTarget after fail().""" - def register_canceller(cb): - """The FileDownloader uses this to register a no-argument function - that the target can call to cancel the download. Once this canceller - is invoked, no further calls to write() or close() will be made.""" - def finish(self): - """When the FileDownloader is done, this finish() function will be - called. Whatever it returns will be returned to the invoker of - Downloader.download. - """ - class FileName: implements(IDownloadTarget) def __init__(self, filename): @@ -222,10 +203,6 @@ class FileHandle: def finish(self): pass -class IDownloader(Interface): - def download(uri, target): - pass - class Downloader(service.MultiService): """I am a service that allows file downloading. """ diff --git a/src/allmydata/filetree/vdrive.py b/src/allmydata/filetree/vdrive.py index 473257ca..e2c1f15e 100644 --- a/src/allmydata/filetree/vdrive.py +++ b/src/allmydata/filetree/vdrive.py @@ -9,6 +9,7 @@ from allmydata.filetree.interfaces import ( PathDoesNotExistError, ) from allmydata.upload import IUploadable +from allmydata.interfaces import IDownloader from allmydata.filetree.nodemaker import NodeMaker @@ -30,6 +31,7 @@ class SubTreeMaker(object): # create subtrees. That means a Downloader and a reference to the # queen. self._queen = queen + assert IDownloader(downloader) self._downloader = downloader self._node_maker = NodeMaker() self._cache = {} diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py index 893d2a13..0e1a8456 100644 --- a/src/allmydata/interfaces.py +++ b/src/allmydata/interfaces.py @@ -180,3 +180,46 @@ class ICodecDecoder(Interface): of 'required_shares' passed into the original ICodecEncode.set_params() call. """ + +class IDownloadTarget(Interface): + def open(): + """Called before any calls to write() or close().""" + def write(data): + """Output some data to the target.""" + def close(): + """Inform the target that there is no more data to be written.""" + def fail(): + """fail() is called to indicate that the download has failed. No + further methods will be invoked on the IDownloadTarget after fail().""" + def register_canceller(cb): + """The FileDownloader uses this to register a no-argument function + that the target can call to cancel the download. Once this canceller + is invoked, no further calls to write() or close() will be made.""" + def finish(self): + """When the FileDownloader is done, this finish() function will be + called. Whatever it returns will be returned to the invoker of + Downloader.download. + """ + +class IDownloader(Interface): + def download(uri, target): + """Perform a CHK download, sending the data to the given target. + 'target' must provide IDownloadTarget.""" + +class IUploadable(Interface): + def get_filehandle(): + """Return a filehandle from which the data to be uploaded can be + read. It must implement .read, .seek, and .tell (since the latter two + are used to determine the length of the data).""" + def close_filehandle(f): + """The upload is finished. This provides the same filehandle as was + returned by get_filehandle. This is an appropriate place to close the + filehandle.""" + +class IUploader(Interface): + def upload(uploadable): + """Upload the file. 'uploadable' must impement IUploadable. This + returns a Deferred which fires with the URI of the file.""" + + def upload_ssk(write_capability, new_version, uploadable): + pass # TODO diff --git a/src/allmydata/test/test_filetree_new.py b/src/allmydata/test/test_filetree_new.py index 8b61abef..be01cd05 100644 --- a/src/allmydata/test/test_filetree_new.py +++ b/src/allmydata/test/test_filetree_new.py @@ -1,8 +1,8 @@ -#from zope.interface import implements +from zope.interface import implements from twisted.trial import unittest from twisted.internet import defer -#from allmydata.filetree.interfaces import IOpener, IDirectoryNode +from allmydata.interfaces import IDownloader, IUploader #from allmydata.filetree.directory import (ImmutableDirectorySubTree, # SubTreeNode, # CHKDirectorySubTree) @@ -20,6 +20,7 @@ class FakeOpener(object): #print "open", subtree_specification, subtree_specification.serialize(), parent_is_mutable return defer.succeed(self.objects[subtree_specification.serialize()]) + class FakeWorkQueue(object): implements(workqueue.IWorkQueue) def __init__(self): @@ -319,6 +320,7 @@ from allmydata.filetree.interfaces import (ISubTree, INode, IDirectoryNode, IFileNode, NoSuchDirectoryError, NoSuchChildError) from allmydata.filetree.file import CHKFileNode +from allmydata.interfaces import IDownloader from allmydata.util import bencode class InPairs(unittest.TestCase): @@ -327,11 +329,14 @@ class InPairs(unittest.TestCase): pairs = list(directory.in_pairs(l)) self.failUnlessEqual(pairs, [(0,1), (2,3), (4,5), (6,7)]) +class StubDownloader(object): + implements(IDownloader) + class Stuff(unittest.TestCase): def makeVirtualDrive(self, basedir, root_node=None): wq = workqueue.WorkQueue(os.path.join(basedir, "1.workqueue")) - dl = None + dl = StubDownloader() if not root_node: root_node = directory.LocalFileSubTreeNode() root_node.new("rootdirtree.save") @@ -347,7 +352,7 @@ class Stuff(unittest.TestCase): self.failUnlessEqual(c1a, c2a) def testDirectory(self): - stm = vdrive.SubTreeMaker(None, None) + stm = vdrive.SubTreeMaker(None, StubDownloader()) # create an empty directory (stored locally) subtree = directory.LocalFileSubTree() diff --git a/src/allmydata/upload.py b/src/allmydata/upload.py index a226f0d6..7b39c1cc 100644 --- a/src/allmydata/upload.py +++ b/src/allmydata/upload.py @@ -1,5 +1,5 @@ -from zope.interface import Interface, implements +from zope.interface import implements from twisted.python import failure, log from twisted.internet import defer from twisted.application import service @@ -10,6 +10,7 @@ from allmydata.util.idlib import peerid_to_short_string as shortid from allmydata.util.deferredutil import DeferredListShouldSucceed from allmydata import codec from allmydata.uri import pack_uri +from allmydata.interfaces import IUploadable, IUploader from cStringIO import StringIO import sha @@ -245,12 +246,6 @@ class FileUploader: def netstring(s): return "%d:%s," % (len(s), s) -class IUploadable(Interface): - def get_filehandle(): - pass - def close_filehandle(f): - pass - class FileName: implements(IUploadable) def __init__(self, filename): @@ -279,17 +274,10 @@ class FileHandle: # the originator of the filehandle reserves the right to close it pass -class IUploader(Interface): - def upload(uploadable): - """Upload the file. 'uploadable' must impement IUploadable. This - returns a Deferred which fires with the URI of the file.""" - - def upload_ssk(write_capability, new_version, uploadable): - pass # TODO - class Uploader(service.MultiService): """I am a service that allows file uploading. """ + implements(IUploader) name = "uploader" uploader_class = FileUploader debug = False diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py index 45025288..90289c11 100644 --- a/src/allmydata/webish.py +++ b/src/allmydata/webish.py @@ -4,7 +4,7 @@ from twisted.web import static, resource, server, html from twisted.python import util, log from nevow import inevow, rend, loaders, appserver, url, tags as T from allmydata.util import idlib -from allmydata.download import IDownloadTarget#, IDownloader +from allmydata.interfaces import IDownloadTarget#, IDownloader from allmydata import upload from zope.interface import implements, Interface import urllib -- 2.45.2