dirnode lookup: use distinct NoSuchChildError instead of the generic KeyError when a child can't be found

This commit is contained in:
Brian Warner
2008-10-27 13:15:25 -07:00
parent 5fd6633289
commit fca158e83a
6 changed files with 35 additions and 23 deletions

View File

@ -8,7 +8,7 @@ from allmydata.mutable.common import NotMutableError
from allmydata.mutable.node import MutableFileNode from allmydata.mutable.node import MutableFileNode
from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\ from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\
IURI, IFileNode, IMutableFileURI, IFilesystemNode, \ IURI, IFileNode, IMutableFileURI, IFilesystemNode, \
ExistingChildError, ICheckable, IDeepCheckable ExistingChildError, NoSuchChildError, ICheckable, IDeepCheckable
from allmydata.checker_results import DeepCheckResults, \ from allmydata.checker_results import DeepCheckResults, \
DeepCheckAndRepairResults DeepCheckAndRepairResults
from allmydata.monitor import Monitor from allmydata.monitor import Monitor
@ -28,7 +28,7 @@ class Deleter:
children = self.node._unpack_contents(old_contents) children = self.node._unpack_contents(old_contents)
if self.name not in children: if self.name not in children:
if self.must_exist: if self.must_exist:
raise KeyError(self.name) raise NoSuchChildError(self.name)
self.old_child = None self.old_child = None
return None return None
self.old_child, metadata = children[self.name] self.old_child, metadata = children[self.name]
@ -44,6 +44,8 @@ class MetadataSetter:
def modify(self, old_contents): def modify(self, old_contents):
children = self.node._unpack_contents(old_contents) children = self.node._unpack_contents(old_contents)
if self.name not in children:
raise NoSuchChildError(self.name)
children[self.name] = (children[self.name][0], self.metadata) children[self.name] = (children[self.name][0], self.metadata)
new_contents = self.node._pack_contents(children) new_contents = self.node._pack_contents(children)
return new_contents return new_contents
@ -246,13 +248,13 @@ class NewDirectoryNode:
def _get(self, children, name): def _get(self, children, name):
child = children.get(name) child = children.get(name)
if child is None: if child is None:
raise KeyError(name) raise NoSuchChildError(name)
return child[0] return child[0]
def _get_with_metadata(self, children, name): def _get_with_metadata(self, children, name):
child = children.get(name) child = children.get(name)
if child is None: if child is None:
raise KeyError(name) raise NoSuchChildError(name)
return child return child
def get(self, name): def get(self, name):

View File

@ -9,7 +9,8 @@ from twisted.protocols import ftp
from twisted.cred import error, portal, checkers, credentials from twisted.cred import error, portal, checkers, credentials
from twisted.web.client import getPage from twisted.web.client import getPage
from allmydata.interfaces import IDirectoryNode, ExistingChildError from allmydata.interfaces import IDirectoryNode, ExistingChildError, \
NoSuchChildError
from allmydata.immutable.download import ConsumerAdapter from allmydata.immutable.download import ConsumerAdapter
from allmydata.immutable.upload import FileHandle from allmydata.immutable.upload import FileHandle
from allmydata.util import base32 from allmydata.util import base32
@ -86,7 +87,7 @@ class Handler:
return defer.succeed(node) return defer.succeed(node)
d = node.get(path[0]) d = node.get(path[0])
def _maybe_create(f): def _maybe_create(f):
f.trap(KeyError) f.trap(NoSuchChildError)
return node.create_empty_directory(path[0]) return node.create_empty_directory(path[0])
d.addErrback(_maybe_create) d.addErrback(_maybe_create)
d.addCallback(self._get_or_create_directories, path[1:]) d.addCallback(self._get_or_create_directories, path[1:])
@ -159,7 +160,7 @@ class Handler:
return d return d
def _convert_error(self, f): def _convert_error(self, f):
if f.check(KeyError): if f.check(NoSuchChildError):
childname = f.value.args[0].encode("utf-8") childname = f.value.args[0].encode("utf-8")
msg = "'%s' doesn't exist" % childname msg = "'%s' doesn't exist" % childname
raise ftp.FileNotFoundError(msg) raise ftp.FileNotFoundError(msg)

View File

@ -653,6 +653,9 @@ class ExistingChildError(Exception):
"""A directory node was asked to add or replace a child that already """A directory node was asked to add or replace a child that already
exists, and overwrite= was set to False.""" exists, and overwrite= was set to False."""
class NoSuchChildError(Exception):
"""A directory node was asked to fetch a child which does not exist."""
class IDirectoryNode(IMutableFilesystemNode): class IDirectoryNode(IMutableFilesystemNode):
"""I represent a name-to-child mapping, holding the tahoe equivalent of a """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 directory. All child names are unicode strings, and all children are some
@ -691,13 +694,15 @@ class IDirectoryNode(IMutableFilesystemNode):
def get(name): def get(name):
"""I return a Deferred that fires with a specific named child node, """I return a Deferred that fires with a specific named child node,
either an IFileNode or an IDirectoryNode. The child name must be a either an IFileNode or an IDirectoryNode. The child name must be a
unicode string.""" unicode string. I raise NoSuchChildError if I do not have a child by
that name."""
def get_metadata_for(name): def get_metadata_for(name):
"""I return a Deferred that fires with the metadata dictionary for a """I return a Deferred that fires with the metadata dictionary for a
specific named child node. This metadata is stored in the *edge*, not 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 in the child, so it is attached to the parent dirnode rather than the
child dir-or-file-node. The child name must be a unicode string.""" child dir-or-file-node. The child name must be a unicode string. I
raise NoSuchChildError if I do not have a child by that name."""
def set_metadata_for(name, metadata): def set_metadata_for(name, metadata):
"""I replace any existing metadata for the named child with the new """I replace any existing metadata for the named child with the new
@ -705,14 +710,15 @@ class IDirectoryNode(IMutableFilesystemNode):
stored in the *edge*, not in the child, so it is attached to the 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 parent dirnode rather than the child dir-or-file-node. I return a
Deferred (that fires with this dirnode) when the operation is Deferred (that fires with this dirnode) when the operation is
complete.""" complete. I raise NoSuchChildError if I do not have a child by that
name."""
def get_child_at_path(path): def get_child_at_path(path):
"""Transform a child path into an IDirectoryNode or IFileNode. """Transform a child path into an IDirectoryNode or IFileNode.
I perform a recursive series of 'get' operations to find the named I perform a recursive series of 'get' operations to find the named
descendant node. I return a Deferred that fires with the node, or descendant node. I return a Deferred that fires with the node, or
errbacks with IndexError if the node could not be found. errbacks with NoSuchChildError if the node could not be found.
The path can be either a single string (slash-separated) or a list of The path can be either a single string (slash-separated) or a list of
path-name elements. All elements must be unicode strings. path-name elements. All elements must be unicode strings.
@ -792,7 +798,8 @@ class IDirectoryNode(IMutableFilesystemNode):
def delete(name): def delete(name):
"""I remove the child at the specific name. I return a Deferred that """I remove the child at the specific name. I return a Deferred that
fires when the operation finishes. The child name must be a unicode fires when the operation finishes. The child name must be a unicode
string.""" string. I raise NoSuchChildError if I do not have a child by that
name."""
def create_empty_directory(name, overwrite=True): def create_empty_directory(name, overwrite=True):
"""I create and attach an empty directory at the given name. The """I create and attach an empty directory at the given name. The
@ -805,7 +812,8 @@ class IDirectoryNode(IMutableFilesystemNode):
is referenced by name. On the new parent, the child will live under is referenced by name. On the new parent, the child will live under
'new_child_name', which defaults to 'current_child_name'. TODO: what 'new_child_name', which defaults to 'current_child_name'. TODO: what
should we do about metadata? I return a Deferred that fires when the should we do about metadata? I return a Deferred that fires when the
operation finishes. The child name must be a unicode string.""" operation finishes. The child name must be a unicode string. I raise
NoSuchChildError if I do not have a child by that name."""
def build_manifest(): def build_manifest():
"""Return a Monitor. The Monitor's results will be a list of (path, """Return a Monitor. The Monitor's results will be a list of (path,

View File

@ -7,7 +7,8 @@ from allmydata import uri, dirnode
from allmydata.immutable import upload from allmydata.immutable import upload
from allmydata.interfaces import IURI, IClient, IMutableFileNode, \ from allmydata.interfaces import IURI, IClient, IMutableFileNode, \
INewDirectoryURI, IReadonlyNewDirectoryURI, IFileNode, \ INewDirectoryURI, IReadonlyNewDirectoryURI, IFileNode, \
ExistingChildError, IDeepCheckResults, IDeepCheckAndRepairResults ExistingChildError, NoSuchChildError, \
IDeepCheckResults, IDeepCheckAndRepairResults
from allmydata.util import hashutil, testutil from allmydata.util import hashutil, testutil
from allmydata.monitor import Monitor from allmydata.monitor import Monitor
from allmydata.test.common import make_chk_file_uri, make_mutable_file_uri, \ from allmydata.test.common import make_chk_file_uri, make_mutable_file_uri, \
@ -104,7 +105,7 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin, testutil.StallMixin):
self.failUnless(u"child" in children) self.failUnless(u"child" in children)
d.addCallback(_check1) d.addCallback(_check1)
d.addCallback(lambda res: d.addCallback(lambda res:
self.shouldFail(KeyError, "get bogus", None, self.shouldFail(NoSuchChildError, "get bogus", None,
dn.get, u"bogus")) dn.get, u"bogus"))
def _corrupt(res): def _corrupt(res):
filenode = dn._node filenode = dn._node
@ -381,8 +382,8 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin, testutil.StallMixin):
["ctime", "mtime"])) ["ctime", "mtime"]))
d.addCallback(lambda res: d.addCallback(lambda res:
self.shouldFail(KeyError, "gcamap-no", self.shouldFail(NoSuchChildError, "gcamap-no",
"'nope'", "nope",
n.get_child_and_metadata_at_path, n.get_child_and_metadata_at_path,
u"subdir/nope")) u"subdir/nope"))
d.addCallback(lambda res: d.addCallback(lambda res:

View File

@ -15,7 +15,7 @@ from allmydata.util import log, base32
from allmydata.scripts import runner from allmydata.scripts import runner
from allmydata.interfaces import IDirectoryNode, IFileNode, IFileURI, \ from allmydata.interfaces import IDirectoryNode, IFileNode, IFileURI, \
ICheckerResults, ICheckAndRepairResults, IDeepCheckResults, \ ICheckerResults, ICheckAndRepairResults, IDeepCheckResults, \
IDeepCheckAndRepairResults IDeepCheckAndRepairResults, NoSuchChildError
from allmydata.monitor import Monitor, OperationCancelledError from allmydata.monitor import Monitor, OperationCancelledError
from allmydata.mutable.common import NotMutableError from allmydata.mutable.common import NotMutableError
from allmydata.mutable import layout as mutable_layout from allmydata.mutable import layout as mutable_layout
@ -900,7 +900,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"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, u"missing")) d1.addCallback(lambda res: self.shouldFail2(NoSuchChildError, "get(missing)", "missing", dirnode.get, u"missing"))
personal = self._personal_node personal = self._personal_node
d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope")) d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope"))

View File

@ -14,7 +14,7 @@ from foolscap.eventual import fireEventually
from allmydata.util import base32 from allmydata.util import base32
from allmydata.uri import from_string_dirnode from allmydata.uri import from_string_dirnode
from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \ from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \
ExistingChildError ExistingChildError, NoSuchChildError
from allmydata.monitor import Monitor from allmydata.monitor import Monitor
from allmydata.web.common import text_plain, WebError, \ from allmydata.web.common import text_plain, WebError, \
IClient, IOpHandleTable, NeedOperationHandleError, \ IClient, IOpHandleTable, NeedOperationHandleError, \
@ -72,7 +72,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
t = get_arg(req, "t", "").strip() t = get_arg(req, "t", "").strip()
if isinstance(node_or_failure, Failure): if isinstance(node_or_failure, Failure):
f = node_or_failure f = node_or_failure
f.trap(KeyError) f.trap(NoSuchChildError)
# No child by this name. What should we do about it? # No child by this name. What should we do about it?
if DEBUG: print "no child", name if DEBUG: print "no child", name
if DEBUG: print "postpath", req.postpath if DEBUG: print "postpath", req.postpath
@ -235,7 +235,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
return defer.succeed(node) return defer.succeed(node)
d = node.get(path[0]) d = node.get(path[0])
def _maybe_create(f): def _maybe_create(f):
f.trap(KeyError) f.trap(NoSuchChildError)
return node.create_empty_directory(path[0]) return node.create_empty_directory(path[0])
d.addErrback(_maybe_create) d.addErrback(_maybe_create)
d.addCallback(self._get_or_create_directories, path[1:]) d.addCallback(self._get_or_create_directories, path[1:])
@ -268,7 +268,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
def _maybe_got_node(node_or_failure): def _maybe_got_node(node_or_failure):
if isinstance(node_or_failure, Failure): if isinstance(node_or_failure, Failure):
f = node_or_failure f = node_or_failure
f.trap(KeyError) f.trap(NoSuchChildError)
# create a placeholder which will see POST t=upload # create a placeholder which will see POST t=upload
return PlaceHolderNodeHandler(self.node, name) return PlaceHolderNodeHandler(self.node, name)
else: else: