From 0cf320c2ab384bf2e491a780163e73e30b23432e Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Thu, 19 Nov 2009 23:52:55 -0800
Subject: [PATCH] interface name cleanups: IFileNode, IImmutableFileNode,
 IMutableFileNode

The proper hierarchy is:
 IFilesystemNode
 +IFileNode
 ++IMutableFileNode
 ++IImmutableFileNode
 +IDirectoryNode

Also expand test_client.py (NodeMaker) to hit all IFilesystemNode types.
---
 src/allmydata/dirnode.py            |  6 ++--
 src/allmydata/immutable/filenode.py |  8 ++---
 src/allmydata/interfaces.py         | 46 +++++++++++++++++------------
 src/allmydata/test/common.py        |  8 ++---
 src/allmydata/test/test_client.py   | 38 +++++++++++++++++++++++-
 src/allmydata/test/test_dirnode.py  |  6 ++--
 src/allmydata/web/directory.py      | 17 +++++------
 7 files changed, 85 insertions(+), 44 deletions(-)

diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py
index fb3cd2da..96c31ffb 100644
--- a/src/allmydata/dirnode.py
+++ b/src/allmydata/dirnode.py
@@ -8,8 +8,8 @@ import simplejson
 from allmydata.mutable.common import NotMutableError
 from allmydata.mutable.filenode import MutableFileNode
 from allmydata.unknown import UnknownNode
-from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\
-     IFileNode, IFilesystemNode, \
+from allmydata.interfaces import IFilesystemNode, IDirectoryNode, IFileNode, \
+     IImmutableFileNode, IMutableFileNode, \
      ExistingChildError, NoSuchChildError, ICheckable, IDeepCheckable, \
      CannotPackUnknownNodeError
 from allmydata.check_results import DeepCheckResults, \
@@ -687,7 +687,7 @@ class DeepStats:
             self.add("count-mutable-files")
             # TODO: update the servermap, compute a size, add it to
             # size-mutable-files, max it into "largest-mutable-file"
-        elif IFileNode.providedBy(node): # CHK and LIT
+        elif IImmutableFileNode.providedBy(node): # CHK and LIT
             self.add("count-files")
             size = node.get_size()
             self.histogram("size-files-histogram", size)
diff --git a/src/allmydata/immutable/filenode.py b/src/allmydata/immutable/filenode.py
index 3b239687..c57709b3 100644
--- a/src/allmydata/immutable/filenode.py
+++ b/src/allmydata/immutable/filenode.py
@@ -5,7 +5,7 @@ from twisted.internet import defer
 from twisted.internet.interfaces import IPushProducer, IConsumer
 from twisted.protocols import basic
 from foolscap.api import eventually
-from allmydata.interfaces import IFileNode, ICheckable, \
+from allmydata.interfaces import IImmutableFileNode, ICheckable, \
      IDownloadTarget, IUploadResults
 from allmydata.util import dictutil, log, base32
 from allmydata.uri import CHKFileURI, LiteralFileURI
@@ -15,7 +15,7 @@ from allmydata.immutable.repairer import Repairer
 from allmydata.immutable import download
 
 class _ImmutableFileNodeBase(object):
-    implements(IFileNode, ICheckable)
+    implements(IImmutableFileNode, ICheckable)
 
     def get_readonly_uri(self):
         return self.get_uri()
@@ -29,12 +29,12 @@ class _ImmutableFileNodeBase(object):
     def __hash__(self):
         return self.u.__hash__()
     def __eq__(self, other):
-        if IFileNode.providedBy(other):
+        if isinstance(other, _ImmutableFileNodeBase):
             return self.u.__eq__(other.u)
         else:
             return False
     def __ne__(self, other):
-        if IFileNode.providedBy(other):
+        if isinstance(other, _ImmutableFileNodeBase):
             return self.u.__eq__(other.u)
         else:
             return True
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index 38427bdf..c6897ef5 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -483,6 +483,13 @@ class UnhandledCapTypeError(Exception):
 class NotDeepImmutableError(Exception):
     """Deep-immutable directories can only contain deep-immutable children"""
 
+# The hierarchy looks like this:
+#  IFilesystemNode
+#   IFileNode
+#    IMutableFileNode
+#    IImmutableFileNode
+#   IDirectoryNode
+
 class IFilesystemNode(Interface):
     def get_cap():
         """Return the strongest 'cap instance' associated with this node.
@@ -563,10 +570,11 @@ class IFilesystemNode(Interface):
         data this node represents.
         """
 
-class IMutableFilesystemNode(IFilesystemNode):
-    pass
-
 class IFileNode(IFilesystemNode):
+    """I am a node which represents a file: a sequence of bytes. I am not a
+    container, like IDirectoryNode."""
+
+class IImmutableFileNode(IFileNode):
     def download(target):
         """Download the file's contents to a given IDownloadTarget"""
 
@@ -626,7 +634,7 @@ class IFileNode(IFilesystemNode):
 
         """
 
-class IMutableFileNode(IFileNode, IMutableFilesystemNode):
+class IMutableFileNode(IFileNode):
     """I provide access to a 'mutable file', which retains its identity
     regardless of what contents are put in it.
 
@@ -819,10 +827,11 @@ class ExistingChildError(Exception):
 class NoSuchChildError(Exception):
     """A directory node was asked to fetch a child which does not exist."""
 
-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).
+class IDirectoryNode(IFilesystemNode):
+    """I represent a filesystem node that is a container, with 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():
@@ -846,8 +855,8 @@ class IDirectoryNode(IMutableFilesystemNode):
     def list():
         """I return a Deferred that fires with a dictionary mapping child
         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."""
+        'node' is an IFilesystemNode (either 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
@@ -877,7 +886,7 @@ class IDirectoryNode(IMutableFilesystemNode):
         name."""
 
     def get_child_at_path(path):
-        """Transform a child path into an IDirectoryNode or IFileNode.
+        """Transform a child path into an IFilesystemNode.
 
         I perform a recursive series of 'get' operations to find the named
         descendant node. I return a Deferred that fires with the node, or
@@ -888,8 +897,7 @@ class IDirectoryNode(IMutableFilesystemNode):
         """
 
     def get_child_and_metadata_at_path(path):
-        """Transform a child path into an IDirectoryNode/IFileNode and
-        metadata.
+        """Transform a child path into an IFilesystemNode and metadata.
 
         I am like get_child_at_path(), but my Deferred fires with a tuple of
         (node, metadata). The metadata comes from the last edge. If the path
@@ -934,7 +942,7 @@ class IDirectoryNode(IMutableFilesystemNode):
         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. The child name must be a unicode string. The 'child'
-        instance must be an instance providing IDirectoryNode or IFileNode.
+        instance must be an instance providing IFilesystemNode.
 
         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
@@ -2068,10 +2076,10 @@ class IClient(Interface):
 
         @return: an instance that provides IFilesystemNode (or more usefully
                  one of its subclasses). File-specifying URIs will result in
-                 IFileNode or IMutableFileNode -providing instances, like
-                 ImmutableFileNode, LiteralFileNode, or MutableFileNode.
-                 Directory-specifying URIs will result in
-                 IDirectoryNode-providing instances, like DirectoryNode.
+                 IFileNode-providing instances, like ImmutableFileNode,
+                 LiteralFileNode, or MutableFileNode. Directory-specifying
+                 URIs will result in IDirectoryNode-providing instances, like
+                 DirectoryNode.
         """
 
 class INodeMaker(Interface):
@@ -2083,7 +2091,7 @@ class INodeMaker(Interface):
     or modify its contents.
 
     The NodeMaker encapsulates all the authorities that these
-    IfilesystemNodes require (like references to the StorageFarmBroker). Each
+    IFilesystemNodes require (like references to the StorageFarmBroker). Each
     Tahoe process will typically have a single NodeMaker, but unit tests may
     create simplified/mocked forms for testing purposes.
     """
diff --git a/src/allmydata/test/common.py b/src/allmydata/test/common.py
index 311dc873..98bb6aed 100644
--- a/src/allmydata/test/common.py
+++ b/src/allmydata/test/common.py
@@ -8,7 +8,7 @@ from twisted.web.error import Error as WebError
 from foolscap.api import flushEventualQueue, fireEventually
 from allmydata import uri, dirnode, client
 from allmydata.introducer.server import IntroducerNode
-from allmydata.interfaces import IMutableFileNode, IFileNode, \
+from allmydata.interfaces import IMutableFileNode, IImmutableFileNode, \
      FileTooLargeError, NotEnoughSharesError, ICheckable
 from allmydata.check_results import CheckResults, CheckAndRepairResults, \
      DeepCheckResults, DeepCheckAndRepairResults
@@ -32,9 +32,9 @@ def flush_but_dont_ignore(res):
     return d
 
 class FakeCHKFileNode:
-    """I provide IFileNode, but all of my data is stored in a class-level
-    dictionary."""
-    implements(IFileNode)
+    """I provide IImmutableFileNode, but all of my data is stored in a
+    class-level dictionary."""
+    implements(IImmutableFileNode)
     all_contents = {}
     bad_shares = {}
 
diff --git a/src/allmydata/test/test_client.py b/src/allmydata/test/test_client.py
index 38cbdc22..1194826a 100644
--- a/src/allmydata/test/test_client.py
+++ b/src/allmydata/test/test_client.py
@@ -9,7 +9,8 @@ from allmydata import client
 from allmydata.storage_client import StorageFarmBroker
 from allmydata.introducer.client import IntroducerClient
 from allmydata.util import base32, fileutil
-from allmydata.interfaces import IFilesystemNode, IFileNode, IDirectoryNode
+from allmydata.interfaces import IFilesystemNode, IFileNode, \
+     IImmutableFileNode, IMutableFileNode, IDirectoryNode
 from foolscap.api import flushEventualQueue
 import common_util as testutil
 
@@ -247,13 +248,44 @@ class NodeMaker(unittest.TestCase):
         n = c.create_node_from_uri("URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277")
         self.failUnless(IFilesystemNode.providedBy(n))
         self.failUnless(IFileNode.providedBy(n))
+        self.failUnless(IImmutableFileNode.providedBy(n))
+        self.failIf(IMutableFileNode.providedBy(n))
         self.failIf(IDirectoryNode.providedBy(n))
         self.failUnless(n.is_readonly())
         self.failIf(n.is_mutable())
 
+        n = c.create_node_from_uri("URI:LIT:n5xgk")
+        self.failUnless(IFilesystemNode.providedBy(n))
+        self.failUnless(IFileNode.providedBy(n))
+        self.failUnless(IImmutableFileNode.providedBy(n))
+        self.failIf(IMutableFileNode.providedBy(n))
+        self.failIf(IDirectoryNode.providedBy(n))
+        self.failUnless(n.is_readonly())
+        self.failIf(n.is_mutable())
+
+        n = c.create_node_from_uri("URI:SSK:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
+        self.failUnless(IFilesystemNode.providedBy(n))
+        self.failUnless(IFileNode.providedBy(n))
+        self.failIf(IImmutableFileNode.providedBy(n))
+        self.failUnless(IMutableFileNode.providedBy(n))
+        self.failIf(IDirectoryNode.providedBy(n))
+        self.failIf(n.is_readonly())
+        self.failUnless(n.is_mutable())
+
+        n = c.create_node_from_uri("URI:SSK-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
+        self.failUnless(IFilesystemNode.providedBy(n))
+        self.failUnless(IFileNode.providedBy(n))
+        self.failIf(IImmutableFileNode.providedBy(n))
+        self.failUnless(IMutableFileNode.providedBy(n))
+        self.failIf(IDirectoryNode.providedBy(n))
+        self.failUnless(n.is_readonly())
+        self.failUnless(n.is_mutable())
+
         n = c.create_node_from_uri("URI:DIR2:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
         self.failUnless(IFilesystemNode.providedBy(n))
         self.failIf(IFileNode.providedBy(n))
+        self.failIf(IImmutableFileNode.providedBy(n))
+        self.failIf(IMutableFileNode.providedBy(n))
         self.failUnless(IDirectoryNode.providedBy(n))
         self.failIf(n.is_readonly())
         self.failUnless(n.is_mutable())
@@ -261,6 +293,8 @@ class NodeMaker(unittest.TestCase):
         n = c.create_node_from_uri("URI:DIR2-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
         self.failUnless(IFilesystemNode.providedBy(n))
         self.failIf(IFileNode.providedBy(n))
+        self.failIf(IImmutableFileNode.providedBy(n))
+        self.failIf(IMutableFileNode.providedBy(n))
         self.failUnless(IDirectoryNode.providedBy(n))
         self.failUnless(n.is_readonly())
         self.failUnless(n.is_mutable())
@@ -269,5 +303,7 @@ class NodeMaker(unittest.TestCase):
         n = c.create_node_from_uri(future)
         self.failUnless(IFilesystemNode.providedBy(n))
         self.failIf(IFileNode.providedBy(n))
+        self.failIf(IImmutableFileNode.providedBy(n))
+        self.failIf(IMutableFileNode.providedBy(n))
         self.failIf(IDirectoryNode.providedBy(n))
         self.failUnlessEqual(n.get_uri(), future)
diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py
index 65f781ba..fd591bed 100644
--- a/src/allmydata/test/test_dirnode.py
+++ b/src/allmydata/test/test_dirnode.py
@@ -6,7 +6,7 @@ from twisted.internet import defer
 from allmydata import uri, dirnode
 from allmydata.client import Client
 from allmydata.immutable import upload
-from allmydata.interfaces import IFileNode, IMutableFileNode, \
+from allmydata.interfaces import IImmutableFileNode, IMutableFileNode, \
      ExistingChildError, NoSuchChildError, NotDeepImmutableError, \
      IDeepCheckResults, IDeepCheckAndRepairResults, CannotPackUnknownNodeError
 from allmydata.mutable.filenode import MutableFileNode
@@ -762,7 +762,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
             uploadable1 = upload.Data("some data", convergence="converge")
             d.addCallback(lambda res: n.add_file(u"newfile", uploadable1))
             d.addCallback(lambda newnode:
-                          self.failUnless(IFileNode.providedBy(newnode)))
+                          self.failUnless(IImmutableFileNode.providedBy(newnode)))
             uploadable2 = upload.Data("some data", convergence="stuff")
             d.addCallback(lambda res:
                           self.shouldFail(ExistingChildError, "add_file-no",
@@ -784,7 +784,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
                                                  uploadable3,
                                                  {"key": "value"}))
             d.addCallback(lambda newnode:
-                          self.failUnless(IFileNode.providedBy(newnode)))
+                          self.failUnless(IImmutableFileNode.providedBy(newnode)))
             d.addCallback(lambda res: n.get_metadata_for(u"newfile-metadata"))
             d.addCallback(lambda metadata:
                               self.failUnless((set(metadata.keys()) == set(["key", "tahoe"])) and
diff --git a/src/allmydata/web/directory.py b/src/allmydata/web/directory.py
index b4fe195e..2c8a609d 100644
--- a/src/allmydata/web/directory.py
+++ b/src/allmydata/web/directory.py
@@ -14,8 +14,8 @@ from foolscap.api import fireEventually
 
 from allmydata.util import base32, time_format
 from allmydata.uri import from_string_dirnode
-from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \
-     IFilesystemNode, ExistingChildError, NoSuchChildError
+from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
+     IImmutableFileNode, IMutableFileNode, ExistingChildError, NoSuchChildError
 from allmydata.monitor import Monitor, OperationCancelledError
 from allmydata import dirnode
 from allmydata.web.common import text_plain, WebError, \
@@ -40,8 +40,6 @@ class BlockingFileError(Exception):
 def make_handler_for(node, client, parentnode=None, name=None):
     if parentnode:
         assert IDirectoryNode.providedBy(parentnode)
-    if IMutableFileNode.providedBy(node):
-        return FileNodeHandler(client, node, parentnode, name)
     if IFileNode.providedBy(node):
         return FileNodeHandler(client, node, parentnode, name)
     if IDirectoryNode.providedBy(node):
@@ -687,7 +685,7 @@ class DirectoryAsHTML(rend.Page):
 
             info_link = "%s/uri/%s?t=info" % (root, quoted_uri)
 
-        elif IFileNode.providedBy(target):
+        elif IImmutableFileNode.providedBy(target):
             dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)
 
             ctx.fillSlots("filename",
@@ -789,17 +787,16 @@ def DirectoryJSONMetadata(ctx, dirnode):
             assert IFilesystemNode.providedBy(childnode), childnode
             rw_uri = childnode.get_uri()
             ro_uri = childnode.get_readonly_uri()
-            if (IDirectoryNode.providedBy(childnode)
-                or IFileNode.providedBy(childnode)):
+            if IFileNode.providedBy(childnode):
                 if childnode.is_readonly():
                     rw_uri = None
-            if IFileNode.providedBy(childnode):
                 kiddata = ("filenode", {'size': childnode.get_size(),
                                         'mutable': childnode.is_mutable(),
                                         })
             elif IDirectoryNode.providedBy(childnode):
-                kiddata = ("dirnode", {'mutable': childnode.is_mutable(),
-                                       })
+                if childnode.is_readonly():
+                    rw_uri = None
+                kiddata = ("dirnode", {'mutable': childnode.is_mutable()})
             else:
                 kiddata = ("unknown", {})
             kiddata[1]["metadata"] = metadata
-- 
2.45.2