1 2 3from zope.interface import interface 4Interface = interface.Interface 5 6# TODO: move these here 7from foolscap.tokens import ISlicer, IRootSlicer, IUnslicer 8_ignored = [ISlicer, IRootSlicer, IUnslicer] # hush pyflakes 9 10class InvalidHintError(Exception): 11 """The hint was malformed and could not be used.""" 12 13class IConnectionHintHandler(Interface): 14 def hint_to_endpoint(hint, reactor, update_status): 15 """Return (endpoint, hostname), or a Deferred that fires with the 16 same, where endpoint is an IStreamClientEndpoint object, and hostname 17 is a string (for use in the HTTP headers during negotiation). The 18 endpoint, once connected, must be capable of handling .startTLS(). 19 Hints are strings which always start with 'TYPE:', and handlers are 20 registered for specific types (and will not be called with hints of 21 other types). update_status() can be called (with a string) to report 22 progress, and should typically be set just before waiting for some 23 connections step (e.g. connecting to a Tor daemon). Raise 24 InvalidHintError (or return a Deferred that errbacks with one) if the 25 hint could not be parsed or otherwise turned into an Endpoint. Set an 26 attribute named 'foolscap_connection_handler_error' on the exception 27 object to have `ConnectionInfo.connectorStatuses()` report that 28 string instead of an exception-class -based status message.""" 29 30 def describe(): 31 """Return a short string describing this handler, like 'tcp' or 32 'tor'. If this method is not implemented, the handler's repr will be 33 used.""" 34 35class DeadReferenceError(Exception): 36 """The RemoteReference is dead, Jim.""" 37 def __init__(self, why=None, remote_tubid=None, request=None): 38 self.why = why 39 self.remote_tubid = remote_tubid 40 self.request = request 41 42 def __str__(self): 43 args = [] 44 if self.why: 45 args.append(self.why) 46 if self.remote_tubid: 47 args.append("(to tubid=%s)" % self.remote_tubid) 48 if self.request: 49 iname, mname = self.request.getMethodNameInfo() 50 args.append("(during method=%s:%s)" % (iname, mname)) 51 return " ".join([str(a) for a in args]) 52 53 54class IReferenceable(Interface): 55 """This object is remotely referenceable. This means it is represented to 56 remote systems as an opaque identifier, and that round-trips preserve 57 identity. 58 """ 59 60 def processUniqueID(): 61 """Return a unique identifier (scoped to the process containing the 62 Referenceable). Most objects can just use C{id(self)}, but objects 63 which should be indistinguishable to a remote system may want 64 multiple objects to map to the same PUID.""" 65 66class IRemotelyCallable(Interface): 67 """This object is remotely callable. This means it defines some remote_* 68 methods and may have a schema which describes how those methods may be 69 invoked. 70 """ 71 72 def getInterfaceNames(): 73 """Return a list of RemoteInterface names to which this object knows 74 how to respond.""" 75 76 def doRemoteCall(methodname, args, kwargs): 77 """Invoke the given remote method. This method may raise an 78 exception, return normally, or return a Deferred.""" 79 80class ITub(Interface): 81 """This marks a Tub.""" 82 83class IBroker(Interface): 84 """This marks a broker.""" 85 86class IRemoteReference(Interface): 87 """This marks a RemoteReference.""" 88 89 def notifyOnDisconnect(callback, *args, **kwargs): 90 """Register a callback to run when we lose this connection. 91 92 The callback will be invoked with whatever extra arguments you 93 provide to this function. For example:: 94 95 def my_callback(name, number): 96 print name, number+4 97 cookie = rref.notifyOnDisconnect(my_callback, 'bob', number=3) 98 99 This function returns an opaque cookie. If you want to cancel the 100 notification, pass this same cookie back to dontNotifyOnDisconnect:: 101 102 rref.dontNotifyOnDisconnect(cookie) 103 104 Note that if the Tub is shutdown (via stopService), all 105 notifyOnDisconnect handlers are cancelled. 106 """ 107 108 def dontNotifyOnDisconnect(cookie): 109 """Deregister a callback that was registered with notifyOnDisconnect. 110 """ 111 112 def callRemote(name, *args, **kwargs): 113 """Invoke a method on the remote object with which I am associated. 114 115 I always return a Deferred. This will fire with the results of the 116 method when and if the remote end finishes. It will errback if any of 117 the following things occur:: 118 119 the arguments do not match the schema I believe is in use by the 120 far end (causes a Violation exception) 121 122 the connection to the far end has been lost (DeadReferenceError) 123 124 the arguments are not accepted by the schema in use by the far end 125 (Violation) 126 127 the method executed by the far end raises an exception (arbitrary) 128 129 the return value of the remote method is not accepted by the schema 130 in use by the far end (Violation) 131 132 the connection is lost before the response is returned 133 (ConnectionLost) 134 135 the return value is not accepted by the schema I believe is in use 136 by the far end (Violation) 137 """ 138 139 def callRemoteOnly(name, *args, **kwargs): 140 """Invoke a method on the remote object with which I am associated. 141 142 This form is for one-way messages that do not require results or even 143 acknowledgement of completion. I do not wait for the method to finish 144 executing. The remote end will be instructed to not send any 145 response. There is no way to know whether the method was successfully 146 delivered or not. 147 148 I always return None. 149 """ 150 151