rrefutil: generically wrap any errback from callRemote() in a ServerFailure instance
authorZooko O'Whielacronx <zooko@zooko.com>
Wed, 31 Dec 2008 21:28:30 +0000 (14:28 -0700)
committerZooko O'Whielacronx <zooko@zooko.com>
Wed, 31 Dec 2008 21:28:30 +0000 (14:28 -0700)
This facilitates client code to easily catch ServerFailures without also catching exceptions arising from client-side code.
See also:
http://foolscap.lothar.com/trac/ticket/105 # make it easy to distinguish server-side failures/exceptions from client-side

src/allmydata/util/rrefutil.py

index 9a567ebb9faa434c6c29b184344352395fc6fbc2..d39f1d6aa4af0237c96986f0cdc62a3cfb12a960 100644 (file)
@@ -1,15 +1,33 @@
+import exceptions
 
 from foolscap.tokens import Violation
 
-class VersionedRemoteReference:
-    """I wrap a RemoteReference, and add a .version attribute."""
+from twisted.python import failure
 
-    def __init__(self, original, version):
+class ServerFailure(exceptions.Exception):
+    # If the server returns a Failure instead of the normal response to a protocol, then this
+    # exception will be raised, with the Failure that the server returned as its .remote_failure
+    # attribute.
+    def __init__(self, remote_failure):
+        self.remote_failure = remote_failure
+    def __repr__(self):
+        return repr(self.remote_failure)
+    def __str__(self):
+        return str(self.remote_failure)
+
+def _wrap_server_failure(f):
+    raise ServerFailure(f)
+
+class WrappedRemoteReference(object):
+    """I intercept any errback from the server and wrap it in a ServerFailure."""
+
+    def __init__(self, original):
         self.rref = original
-        self.version = version
 
     def callRemote(self, *args, **kwargs):
-        return self.rref.callRemote(*args, **kwargs)
+        d = self.rref.callRemote(*args, **kwargs)
+        d.addErrback(_wrap_server_failure)
+        return d
 
     def callRemoteOnly(self, *args, **kwargs):
         return self.rref.callRemoteOnly(*args, **kwargs)
@@ -17,6 +35,13 @@ class VersionedRemoteReference:
     def notifyOnDisconnect(self, *args, **kwargs):
         return self.rref.notifyOnDisconnect(*args, **kwargs)
 
+class VersionedRemoteReference(WrappedRemoteReference):
+    """I wrap a RemoteReference, and add a .version attribute. I also intercept any errback from
+    the server and wrap it in a ServerFailure."""
+
+    def __init__(self, original, version):
+        WrappedRemoteReference.__init__(self, original)
+        self.version = version
 
 def get_versioned_remote_reference(rref, default):
     """I return a Deferred that fires with a VersionedRemoteReference"""