| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  | """RPC Implemention, originally written for the Python Idle IDE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For security reasons, GvR requested that Idle's Python execution server process | 
					
						
							|  |  |  | connect to the Idle process, which listens for the connection.  Since Idle has | 
					
						
							|  |  |  | has only one client per server, this was not a limitation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    +---------------------------------+ +-------------+ | 
					
						
							|  |  |  |    | SocketServer.BaseRequestHandler | | SocketIO    | | 
					
						
							|  |  |  |    +---------------------------------+ +-------------+ | 
					
						
							|  |  |  |                    ^                   | register()  | | 
					
						
							|  |  |  |                    |                   | unregister()| | 
					
						
							|  |  |  |                    |                   +-------------+ | 
					
						
							|  |  |  |                    |                      ^  ^ | 
					
						
							|  |  |  |                    |                      |  | | 
					
						
							|  |  |  |                    | + -------------------+  | | 
					
						
							|  |  |  |                    | |                       | | 
					
						
							|  |  |  |    +-------------------------+        +-----------------+ | 
					
						
							|  |  |  |    | RPCHandler              |        | RPCClient       | | 
					
						
							|  |  |  |    | [attribute of RPCServer]|        |                 | | 
					
						
							|  |  |  |    +-------------------------+        +-----------------+ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The RPCServer handler class is expected to provide register/unregister methods. | 
					
						
							|  |  |  | RPCHandler inherits the mix-in class SocketIO, which provides these methods. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | See the Idle run.main() docstring for further information on how this was | 
					
						
							|  |  |  | accomplished in Idle. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | import socket | 
					
						
							|  |  |  | import select | 
					
						
							|  |  |  | import SocketServer | 
					
						
							|  |  |  | import struct | 
					
						
							|  |  |  | import cPickle as pickle | 
					
						
							|  |  |  | import threading | 
					
						
							|  |  |  | import traceback | 
					
						
							|  |  |  | import copy_reg | 
					
						
							|  |  |  | import types | 
					
						
							|  |  |  | import marshal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def unpickle_code(ms): | 
					
						
							|  |  |  |     co = marshal.loads(ms) | 
					
						
							|  |  |  |     assert isinstance(co, types.CodeType) | 
					
						
							|  |  |  |     return co | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def pickle_code(co): | 
					
						
							|  |  |  |     assert isinstance(co, types.CodeType) | 
					
						
							|  |  |  |     ms = marshal.dumps(co) | 
					
						
							|  |  |  |     return unpickle_code, (ms,) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  | # XXX KBK 24Aug02 function pickling capability not used in Idle | 
					
						
							|  |  |  | #  def unpickle_function(ms): | 
					
						
							|  |  |  | #      return ms | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  | #  def pickle_function(fn): | 
					
						
							|  |  |  | #      assert isinstance(fn, type.FunctionType) | 
					
						
							|  |  |  | #      return `fn` | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | copy_reg.pickle(types.CodeType, pickle_code, unpickle_code) | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  | # copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | BUFSIZE = 8*1024 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RPCServer(SocketServer.TCPServer): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, addr, handlerclass=None): | 
					
						
							|  |  |  |         if handlerclass is None: | 
					
						
							|  |  |  |             handlerclass = RPCHandler | 
					
						
							|  |  |  |         SocketServer.TCPServer.__init__(self, addr, handlerclass) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |     def server_bind(self): | 
					
						
							|  |  |  |         "Override TCPServer method, no bind() phase for connecting entity" | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def server_activate(self): | 
					
						
							|  |  |  |         """Override TCPServer method, connect() instead of listen()
 | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |         Due to the reversed connection, self.server_address is actually the | 
					
						
							|  |  |  |         address of the Idle Client to which we are connecting. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.socket.connect(self.server_address) | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |     def get_request(self): | 
					
						
							|  |  |  |         "Override TCPServer method, return already connected socket" | 
					
						
							|  |  |  |         return self.socket, self.server_address | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |     def handle_error(self, request, client_address): | 
					
						
							|  |  |  |         """Override TCPServer method, no error message if exiting""" | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except SystemExit: | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             TCPServer.handle_error(request, client_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | objecttable = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SocketIO: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |     nextseq = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     def __init__(self, sock, objtable=None, debugging=None): | 
					
						
							|  |  |  |         self.mainthread = threading.currentThread() | 
					
						
							|  |  |  |         if debugging is not None: | 
					
						
							|  |  |  |             self.debugging = debugging | 
					
						
							|  |  |  |         self.sock = sock | 
					
						
							|  |  |  |         if objtable is None: | 
					
						
							|  |  |  |             objtable = objecttable | 
					
						
							|  |  |  |         self.objtable = objtable | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         self.cvar = threading.Condition() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         self.responses = {} | 
					
						
							|  |  |  |         self.cvars = {} | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         self.interrupted = False | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         sock = self.sock | 
					
						
							|  |  |  |         self.sock = None | 
					
						
							|  |  |  |         if sock is not None: | 
					
						
							|  |  |  |             sock.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def debug(self, *args): | 
					
						
							|  |  |  |         if not self.debugging: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2002-12-06 21:45:24 +00:00
										 |  |  |         s = self.location + " " + str(threading.currentThread().getName()) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         for a in args: | 
					
						
							|  |  |  |             s = s + " " + str(a) | 
					
						
							| 
									
										
										
										
											2002-12-06 21:45:24 +00:00
										 |  |  |         print>>sys.__stderr__, s | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def register(self, oid, object): | 
					
						
							|  |  |  |         self.objtable[oid] = object | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def unregister(self, oid): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del self.objtable[oid] | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def localcall(self, request): | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |         self.debug("localcall:", request) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             how, (oid, methodname, args, kwargs) = request | 
					
						
							|  |  |  |         except TypeError: | 
					
						
							|  |  |  |             return ("ERROR", "Bad request format") | 
					
						
							|  |  |  |         assert how == "call" | 
					
						
							|  |  |  |         if not self.objtable.has_key(oid): | 
					
						
							|  |  |  |             return ("ERROR", "Unknown object id: %s" % `oid`) | 
					
						
							|  |  |  |         obj = self.objtable[oid] | 
					
						
							|  |  |  |         if methodname == "__methods__": | 
					
						
							|  |  |  |             methods = {} | 
					
						
							|  |  |  |             _getmethods(obj, methods) | 
					
						
							|  |  |  |             return ("OK", methods) | 
					
						
							|  |  |  |         if methodname == "__attributes__": | 
					
						
							|  |  |  |             attributes = {} | 
					
						
							|  |  |  |             _getattributes(obj, attributes) | 
					
						
							|  |  |  |             return ("OK", attributes) | 
					
						
							|  |  |  |         if not hasattr(obj, methodname): | 
					
						
							|  |  |  |             return ("ERROR", "Unsupported method name: %s" % `methodname`) | 
					
						
							|  |  |  |         method = getattr(obj, methodname) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             ret = method(*args, **kwargs) | 
					
						
							|  |  |  |             if isinstance(ret, RemoteObject): | 
					
						
							|  |  |  |                 ret = remoteref(ret) | 
					
						
							|  |  |  |             return ("OK", ret) | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         except SystemExit: | 
					
						
							|  |  |  |             raise | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         except: | 
					
						
							| 
									
										
										
										
											2003-01-31 05:06:43 +00:00
										 |  |  |             self.debug("localcall:EXCEPTION") | 
					
						
							| 
									
										
										
										
											2003-02-27 23:04:17 +00:00
										 |  |  |             traceback.print_exc(file=sys.__stderr__) | 
					
						
							| 
									
										
										
										
											2003-01-31 05:06:43 +00:00
										 |  |  |             return ("EXCEPTION", None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     def remotecall(self, oid, methodname, args, kwargs): | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         self.debug("remotecall:asynccall: ", oid, methodname) | 
					
						
							|  |  |  |         # XXX KBK 06Feb03 self.interrupted logic may not be necessary if | 
					
						
							|  |  |  |         #                 subprocess is threaded. | 
					
						
							|  |  |  |         if self.interrupted: | 
					
						
							|  |  |  |             self.interrupted = False | 
					
						
							|  |  |  |             raise KeyboardInterrupt | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         seq = self.asynccall(oid, methodname, args, kwargs) | 
					
						
							| 
									
										
										
										
											2002-12-06 21:45:24 +00:00
										 |  |  |         return self.asyncreturn(seq) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def asynccall(self, oid, methodname, args, kwargs): | 
					
						
							|  |  |  |         request = ("call", (oid, methodname, args, kwargs)) | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         seq = self.newseq() | 
					
						
							|  |  |  |         self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs) | 
					
						
							|  |  |  |         self.putmessage((seq, request)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         return seq | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def asyncreturn(self, seq): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug("asyncreturn:%d:call getresponse(): " % seq) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         response = self.getresponse(seq) | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug(("asyncreturn:%d:response: " % seq), response) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         return self.decoderesponse(response) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def decoderesponse(self, response): | 
					
						
							|  |  |  |         how, what = response | 
					
						
							|  |  |  |         if how == "OK": | 
					
						
							|  |  |  |             return what | 
					
						
							|  |  |  |         if how == "EXCEPTION": | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             self.debug("decoderesponse: EXCEPTION") | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         if how == "ERROR": | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |             self.debug("decoderesponse: Internal ERROR:", what) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             raise RuntimeError, what | 
					
						
							|  |  |  |         raise SystemError, (how, what) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def mainloop(self): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         """Listen on socket until I/O not ready or EOF
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pollpacket() will loop looking for seq number None, which never | 
					
						
							|  |  |  |         comes.  The loop will exit when self.ioready() returns 0. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self.getresponse(None) | 
					
						
							|  |  |  |         except EOFError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getresponse(self, myseq): | 
					
						
							|  |  |  |         response = self._getresponse(myseq) | 
					
						
							|  |  |  |         if response is not None: | 
					
						
							|  |  |  |             how, what = response | 
					
						
							|  |  |  |             if how == "OK": | 
					
						
							|  |  |  |                 response = how, self._proxify(what) | 
					
						
							|  |  |  |         return response | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _proxify(self, obj): | 
					
						
							|  |  |  |         if isinstance(obj, RemoteProxy): | 
					
						
							|  |  |  |             return RPCProxy(self, obj.oid) | 
					
						
							|  |  |  |         if isinstance(obj, types.ListType): | 
					
						
							|  |  |  |             return map(self._proxify, obj) | 
					
						
							|  |  |  |         # XXX Check for other types -- not currently needed | 
					
						
							|  |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _getresponse(self, myseq): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug("_getresponse:myseq:", myseq) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         if threading.currentThread() is self.mainthread: | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |             # Main thread: does all reading of requests or responses | 
					
						
							|  |  |  |             # Loop here until there is message traffic on the socket | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             while 1: | 
					
						
							|  |  |  |                 response = self.pollresponse(myseq, None) | 
					
						
							|  |  |  |                 if response is not None: | 
					
						
							|  |  |  |                     return response | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Auxiliary thread: wait for notification from main thread | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             self.cvar.acquire() | 
					
						
							|  |  |  |             self.cvars[myseq] = self.cvar | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             while not self.responses.has_key(myseq): | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                 self.cvar.wait() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             response = self.responses[myseq] | 
					
						
							|  |  |  |             del self.responses[myseq] | 
					
						
							|  |  |  |             del self.cvars[myseq] | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             self.cvar.release() | 
					
						
							|  |  |  |             return response | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def newseq(self): | 
					
						
							|  |  |  |         self.nextseq = seq = self.nextseq + 2 | 
					
						
							|  |  |  |         return seq | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def putmessage(self, message): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug("putmessage:%d:" % message[0]) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             s = pickle.dumps(message) | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             print >>sys.__stderr__, "Cannot pickle:", `message` | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         s = struct.pack("<i", len(s)) + s | 
					
						
							|  |  |  |         while len(s) > 0: | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 n = self.sock.send(s) | 
					
						
							|  |  |  |             except AttributeError: | 
					
						
							|  |  |  |                 # socket was closed | 
					
						
							|  |  |  |                 raise IOError | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 s = s[n:] | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def ioready(self, wait=0.0): | 
					
						
							|  |  |  |         r, w, x = select.select([self.sock.fileno()], [], [], wait) | 
					
						
							|  |  |  |         return len(r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     buffer = "" | 
					
						
							|  |  |  |     bufneed = 4 | 
					
						
							|  |  |  |     bufstate = 0 # meaning: 0 => reading count; 1 => reading data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pollpacket(self, wait=0.0): | 
					
						
							|  |  |  |         self._stage0() | 
					
						
							|  |  |  |         if len(self.buffer) < self.bufneed: | 
					
						
							|  |  |  |             if not self.ioready(wait): | 
					
						
							|  |  |  |                 return None | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 s = self.sock.recv(BUFSIZE) | 
					
						
							|  |  |  |             except socket.error: | 
					
						
							|  |  |  |                 raise EOFError | 
					
						
							|  |  |  |             if len(s) == 0: | 
					
						
							|  |  |  |                 raise EOFError | 
					
						
							|  |  |  |             self.buffer += s | 
					
						
							|  |  |  |             self._stage0() | 
					
						
							|  |  |  |         return self._stage1() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _stage0(self): | 
					
						
							|  |  |  |         if self.bufstate == 0 and len(self.buffer) >= 4: | 
					
						
							|  |  |  |             s = self.buffer[:4] | 
					
						
							|  |  |  |             self.buffer = self.buffer[4:] | 
					
						
							|  |  |  |             self.bufneed = struct.unpack("<i", s)[0] | 
					
						
							|  |  |  |             self.bufstate = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _stage1(self): | 
					
						
							|  |  |  |         if self.bufstate == 1 and len(self.buffer) >= self.bufneed: | 
					
						
							|  |  |  |             packet = self.buffer[:self.bufneed] | 
					
						
							|  |  |  |             self.buffer = self.buffer[self.bufneed:] | 
					
						
							|  |  |  |             self.bufneed = 4 | 
					
						
							|  |  |  |             self.bufstate = 0 | 
					
						
							|  |  |  |             return packet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pollmessage(self, wait=0.0): | 
					
						
							|  |  |  |         packet = self.pollpacket(wait) | 
					
						
							|  |  |  |         if packet is None: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             message = pickle.loads(packet) | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             print >>sys.__stderr__, "-----------------------" | 
					
						
							|  |  |  |             print >>sys.__stderr__, "cannot unpickle packet:", `packet` | 
					
						
							|  |  |  |             traceback.print_stack(file=sys.__stderr__) | 
					
						
							|  |  |  |             print >>sys.__stderr__, "-----------------------" | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         return message | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pollresponse(self, myseq, wait=0.0): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         """Handle messages received on the socket.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Some messages received may be asynchronous 'call' commands, and | 
					
						
							|  |  |  |         some may be responses intended for other threads. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Loop until message with myseq sequence number is received.  Save others | 
					
						
							|  |  |  |         in self.responses and notify the owning thread, except that 'call' | 
					
						
							|  |  |  |         commands are handed off to localcall() and the response sent back | 
					
						
							|  |  |  |         across the link with the appropriate sequence number. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         while 1: | 
					
						
							|  |  |  |             message = self.pollmessage(wait) | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |             if message is None:  # socket not ready | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 return None | 
					
						
							|  |  |  |             wait = 0.0 | 
					
						
							|  |  |  |             seq, resq = message | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |             self.debug("pollresponse:%d:myseq:%s" % (seq, myseq)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             if resq[0] == "call": | 
					
						
							| 
									
										
										
										
											2003-01-25 21:33:40 +00:00
										 |  |  |                 self.debug("pollresponse:%d:localcall:call:" % seq) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 response = self.localcall(resq) | 
					
						
							| 
									
										
										
										
											2003-01-25 21:33:40 +00:00
										 |  |  |                 self.debug("pollresponse:%d:localcall:response:%s" | 
					
						
							|  |  |  |                            % (seq, response)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 self.putmessage((seq, response)) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             elif seq == myseq: | 
					
						
							|  |  |  |                 return resq | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                 self.cvar.acquire() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 cv = self.cvars.get(seq) | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                 # response involving unknown sequence number is discarded, | 
					
						
							|  |  |  |                 # probably intended for prior incarnation | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 if cv is not None: | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                     self.responses[seq] = resq | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                     cv.notify() | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                 self.cvar.release() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  | #----------------- end class SocketIO -------------------- | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class RemoteObject: | 
					
						
							|  |  |  |     # Token mix-in class | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def remoteref(obj): | 
					
						
							|  |  |  |     oid = id(obj) | 
					
						
							|  |  |  |     objecttable[oid] = obj | 
					
						
							|  |  |  |     return RemoteProxy(oid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RemoteProxy: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, oid): | 
					
						
							|  |  |  |         self.oid = oid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RPCHandler(SocketServer.BaseRequestHandler, SocketIO): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-12-06 21:45:24 +00:00
										 |  |  |     debugging = False | 
					
						
							|  |  |  |     location = "#S"  # Server | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, sock, addr, svr): | 
					
						
							|  |  |  |         svr.current_handler = self ## cgt xxx | 
					
						
							|  |  |  |         SocketIO.__init__(self, sock) | 
					
						
							|  |  |  |         SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle(self): | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |         "handle() method required by SocketServer" | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         self.mainloop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_remote_proxy(self, oid): | 
					
						
							|  |  |  |         return RPCProxy(self, oid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RPCClient(SocketIO): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-12-06 21:45:24 +00:00
										 |  |  |     debugging = False | 
					
						
							|  |  |  |     location = "#C"  # Client | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |     nextseq = 1 # Requests coming from the client are odd numbered | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM): | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  |         self.listening_sock = socket.socket(family, type) | 
					
						
							|  |  |  |         self.listening_sock.setsockopt(socket.SOL_SOCKET, | 
					
						
							|  |  |  |                                        socket.SO_REUSEADDR, 1) | 
					
						
							|  |  |  |         self.listening_sock.bind(address) | 
					
						
							|  |  |  |         self.listening_sock.listen(1) | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def accept(self): | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  |         working_sock, address = self.listening_sock.accept() | 
					
						
							| 
									
										
										
										
											2002-12-23 22:51:03 +00:00
										 |  |  |         if self.debugging: | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |             print>>sys.__stderr__, "****** Connection request from ", address | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |         if address[0] == '127.0.0.1': | 
					
						
							| 
									
										
										
										
											2002-08-25 14:08:07 +00:00
										 |  |  |             SocketIO.__init__(self, working_sock) | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2002-12-23 22:51:03 +00:00
										 |  |  |             print>>sys.__stderr__, "** Invalid host: ", address | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |             raise socket.error | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_remote_proxy(self, oid): | 
					
						
							|  |  |  |         return RPCProxy(self, oid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RPCProxy: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __methods = None | 
					
						
							|  |  |  |     __attributes = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, sockio, oid): | 
					
						
							|  |  |  |         self.sockio = sockio | 
					
						
							|  |  |  |         self.oid = oid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getattr__(self, name): | 
					
						
							|  |  |  |         if self.__methods is None: | 
					
						
							|  |  |  |             self.__getmethods() | 
					
						
							|  |  |  |         if self.__methods.get(name): | 
					
						
							|  |  |  |             return MethodProxy(self.sockio, self.oid, name) | 
					
						
							|  |  |  |         if self.__attributes is None: | 
					
						
							|  |  |  |             self.__getattributes() | 
					
						
							|  |  |  |         if not self.__attributes.has_key(name): | 
					
						
							|  |  |  |             raise AttributeError, name | 
					
						
							|  |  |  |     __getattr__.DebuggerStepThrough=1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getattributes(self): | 
					
						
							|  |  |  |         self.__attributes = self.sockio.remotecall(self.oid, | 
					
						
							|  |  |  |                                                 "__attributes__", (), {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getmethods(self): | 
					
						
							|  |  |  |         self.__methods = self.sockio.remotecall(self.oid, | 
					
						
							|  |  |  |                                                 "__methods__", (), {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _getmethods(obj, methods): | 
					
						
							|  |  |  |     # Helper to get a list of methods from an object | 
					
						
							|  |  |  |     # Adds names to dictionary argument 'methods' | 
					
						
							|  |  |  |     for name in dir(obj): | 
					
						
							|  |  |  |         attr = getattr(obj, name) | 
					
						
							|  |  |  |         if callable(attr): | 
					
						
							|  |  |  |             methods[name] = 1 | 
					
						
							|  |  |  |     if type(obj) == types.InstanceType: | 
					
						
							|  |  |  |         _getmethods(obj.__class__, methods) | 
					
						
							|  |  |  |     if type(obj) == types.ClassType: | 
					
						
							|  |  |  |         for super in obj.__bases__: | 
					
						
							|  |  |  |             _getmethods(super, methods) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _getattributes(obj, attributes): | 
					
						
							|  |  |  |     for name in dir(obj): | 
					
						
							|  |  |  |         attr = getattr(obj, name) | 
					
						
							|  |  |  |         if not callable(attr): | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |             attributes[name] = 1 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class MethodProxy: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, sockio, oid, name): | 
					
						
							|  |  |  |         self.sockio = sockio | 
					
						
							|  |  |  |         self.oid = oid | 
					
						
							|  |  |  |         self.name = name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __call__(self, *args, **kwargs): | 
					
						
							|  |  |  |         value = self.sockio.remotecall(self.oid, self.name, args, kwargs) | 
					
						
							|  |  |  |         return value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Self Test | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def testServer(addr): | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |     # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     class RemotePerson: | 
					
						
							|  |  |  |         def __init__(self,name): | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |             self.name = name | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         def greet(self, name): | 
					
						
							|  |  |  |             print "(someone called greet)" | 
					
						
							|  |  |  |             print "Hello %s, I am %s." % (name, self.name) | 
					
						
							|  |  |  |             print | 
					
						
							|  |  |  |         def getName(self): | 
					
						
							|  |  |  |             print "(someone called getName)" | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |             print | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             return self.name | 
					
						
							|  |  |  |         def greet_this_guy(self, name): | 
					
						
							|  |  |  |             print "(someone called greet_this_guy)" | 
					
						
							|  |  |  |             print "About to greet %s ..." % name | 
					
						
							|  |  |  |             remote_guy = self.server.current_handler.get_remote_proxy(name) | 
					
						
							|  |  |  |             remote_guy.greet("Thomas Edison") | 
					
						
							|  |  |  |             print "Done." | 
					
						
							|  |  |  |             print | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     person = RemotePerson("Thomas Edison") | 
					
						
							|  |  |  |     svr = RPCServer(addr) | 
					
						
							|  |  |  |     svr.register('thomas', person) | 
					
						
							|  |  |  |     person.server = svr # only required if callbacks are used | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # svr.serve_forever() | 
					
						
							|  |  |  |     svr.handle_request()  # process once only | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def testClient(addr): | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |     "demonstrates RPC Client" | 
					
						
							|  |  |  |     # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     import time | 
					
						
							|  |  |  |     clt=RPCClient(addr) | 
					
						
							|  |  |  |     thomas = clt.get_remote_proxy("thomas") | 
					
						
							|  |  |  |     print "The remote person's name is ..." | 
					
						
							|  |  |  |     print thomas.getName() | 
					
						
							|  |  |  |     # print clt.remotecall("thomas", "getName", (), {}) | 
					
						
							|  |  |  |     print | 
					
						
							|  |  |  |     time.sleep(1) | 
					
						
							|  |  |  |     print "Getting remote thomas to say hi..." | 
					
						
							|  |  |  |     thomas.greet("Alexander Bell") | 
					
						
							|  |  |  |     #clt.remotecall("thomas","greet",("Alexander Bell",), {}) | 
					
						
							|  |  |  |     print "Done." | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |     print | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     time.sleep(2) | 
					
						
							|  |  |  |     # demonstrates remote server calling local instance | 
					
						
							|  |  |  |     class LocalPerson: | 
					
						
							|  |  |  |         def __init__(self,name): | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  |             self.name = name | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         def greet(self, name): | 
					
						
							|  |  |  |             print "You've greeted me!" | 
					
						
							|  |  |  |         def getName(self): | 
					
						
							|  |  |  |             return self.name | 
					
						
							|  |  |  |     person = LocalPerson("Alexander Bell") | 
					
						
							|  |  |  |     clt.register("alexander",person) | 
					
						
							|  |  |  |     thomas.greet_this_guy("alexander") | 
					
						
							|  |  |  |     # clt.remotecall("thomas","greet_this_guy",("alexander",), {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test(): | 
					
						
							|  |  |  |     addr=("localhost",8833) | 
					
						
							|  |  |  |     if len(sys.argv) == 2: | 
					
						
							|  |  |  |         if sys.argv[1]=='-server': | 
					
						
							|  |  |  |             testServer(addr) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |     testClient(addr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     test() |