| 
									
										
										
										
											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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    +---------------------------------+ +-------------+ | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  |    | SocketServer.BaseRequestHandler | | SocketIO    | | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  |    +---------------------------------+ +-------------+ | 
					
						
							|  |  |  |                    ^                   | 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 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | import socket | 
					
						
							|  |  |  | import select | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  | import SocketServer | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | import struct | 
					
						
							|  |  |  | import cPickle as pickle | 
					
						
							|  |  |  | import threading | 
					
						
							| 
									
										
										
										
											2008-05-11 19:39:48 +00:00
										 |  |  | import queue | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | import traceback | 
					
						
							| 
									
										
										
										
											2008-05-20 07:49:57 +00:00
										 |  |  | import copy_reg | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | import types | 
					
						
							|  |  |  | import marshal | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  | #      return repr(fn) | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-20 07:49:57 +00:00
										 |  |  | copy_reg.pickle(types.CodeType, pickle_code, unpickle_code) | 
					
						
							|  |  |  | # copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | BUFSIZE = 8*1024 | 
					
						
							| 
									
										
										
										
											2003-06-05 23:51:29 +00:00
										 |  |  | LOCALHOST = '127.0.0.1' | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  | class RPCServer(SocketServer.TCPServer): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, addr, handlerclass=None): | 
					
						
							|  |  |  |         if handlerclass is None: | 
					
						
							|  |  |  |             handlerclass = RPCHandler | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  |         SocketServer.TCPServer.__init__(self, addr, handlerclass) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							| 
									
										
										
										
											2003-03-22 19:15:58 +00:00
										 |  |  |         """Override TCPServer method
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Error message goes to __stderr__.  No error message if exiting | 
					
						
							|  |  |  |         normally or socket raised EOF.  Other exceptions not handled in | 
					
						
							|  |  |  |         server code will cause os._exit. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except SystemExit: | 
					
						
							|  |  |  |             raise | 
					
						
							| 
									
										
										
										
											2003-03-22 19:15:58 +00:00
										 |  |  |         except: | 
					
						
							| 
									
										
										
										
											2003-03-22 20:11:14 +00:00
										 |  |  |             erf = sys.__stderr__ | 
					
						
							|  |  |  |             print>>erf, '\n' + '-'*40 | 
					
						
							| 
									
										
										
										
											2003-03-22 19:15:58 +00:00
										 |  |  |             print>>erf, 'Unhandled server exception!' | 
					
						
							|  |  |  |             print>>erf, 'Thread: %s' % threading.currentThread().getName() | 
					
						
							| 
									
										
										
										
											2003-03-22 20:11:14 +00:00
										 |  |  |             print>>erf, 'Client Address: ', client_address | 
					
						
							| 
									
										
										
										
											2003-03-22 19:15:58 +00:00
										 |  |  |             print>>erf, 'Request: ', repr(request) | 
					
						
							|  |  |  |             traceback.print_exc(file=erf) | 
					
						
							|  |  |  |             print>>erf, '\n*** Unrecoverable, server exiting!' | 
					
						
							|  |  |  |             print>>erf, '-'*40 | 
					
						
							| 
									
										
										
										
											2003-03-22 20:11:14 +00:00
										 |  |  |             os._exit(0) | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  | #----------------- end class RPCServer -------------------- | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | objecttable = {} | 
					
						
							| 
									
										
										
										
											2008-05-11 19:39:48 +00:00
										 |  |  | request_queue = queue.Queue(0) | 
					
						
							|  |  |  | response_queue = queue.Queue(0) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  | class SocketIO(object): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         self.sockthread = threading.currentThread() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         if debugging is not None: | 
					
						
							|  |  |  |             self.debugging = debugging | 
					
						
							|  |  |  |         self.sock = sock | 
					
						
							|  |  |  |         if objtable is None: | 
					
						
							|  |  |  |             objtable = objecttable | 
					
						
							|  |  |  |         self.objtable = objtable | 
					
						
							|  |  |  |         self.responses = {} | 
					
						
							|  |  |  |         self.cvars = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         sock = self.sock | 
					
						
							|  |  |  |         self.sock = None | 
					
						
							|  |  |  |         if sock is not None: | 
					
						
							|  |  |  |             sock.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def exithook(self): | 
					
						
							|  |  |  |         "override for specific exit action" | 
					
						
							|  |  |  |         os._exit() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def localcall(self, seq, 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") | 
					
						
							|  |  |  |         if not self.objtable.has_key(oid): | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             return ("ERROR", "Unknown object id: %r" % (oid,)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         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): | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             return ("ERROR", "Unsupported method name: %r" % (methodname,)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         method = getattr(obj, methodname) | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             if how == 'CALL': | 
					
						
							|  |  |  |                 ret = method(*args, **kwargs) | 
					
						
							|  |  |  |                 if isinstance(ret, RemoteObject): | 
					
						
							|  |  |  |                     ret = remoteref(ret) | 
					
						
							|  |  |  |                 return ("OK", ret) | 
					
						
							|  |  |  |             elif how == 'QUEUE': | 
					
						
							|  |  |  |                 request_queue.put((seq, (method, args, kwargs))) | 
					
						
							|  |  |  |                 return("QUEUED", None) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 return ("ERROR", "Unsupported message type: %s" % how) | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |         except SystemExit: | 
					
						
							|  |  |  |             raise | 
					
						
							| 
									
										
										
										
											2003-03-10 20:42:24 +00:00
										 |  |  |         except socket.error: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             raise | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         except: | 
					
						
							| 
									
										
										
										
											2004-12-23 04:39:55 +00:00
										 |  |  |             msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\ | 
					
						
							|  |  |  |                   " Object: %s \n Method: %s \n Args: %s\n" | 
					
						
							|  |  |  |             print>>sys.__stderr__, msg % (oid, method, args) | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def remotequeue(self, oid, methodname, args, kwargs): | 
					
						
							|  |  |  |         self.debug("remotequeue:asyncqueue: ", oid, methodname) | 
					
						
							|  |  |  |         seq = self.asyncqueue(oid, methodname, args, kwargs) | 
					
						
							|  |  |  |         return self.asyncreturn(seq) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     def asynccall(self, oid, methodname, args, kwargs): | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         request = ("CALL", (oid, methodname, args, kwargs)) | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         seq = self.newseq() | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         if threading.currentThread() != self.sockthread: | 
					
						
							|  |  |  |             cvar = threading.Condition() | 
					
						
							|  |  |  |             self.cvars[seq] = cvar | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs) | 
					
						
							|  |  |  |         self.putmessage((seq, request)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         return seq | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def asyncqueue(self, oid, methodname, args, kwargs): | 
					
						
							|  |  |  |         request = ("QUEUE", (oid, methodname, args, kwargs)) | 
					
						
							|  |  |  |         seq = self.newseq() | 
					
						
							|  |  |  |         if threading.currentThread() != self.sockthread: | 
					
						
							|  |  |  |             cvar = threading.Condition() | 
					
						
							|  |  |  |             self.cvars[seq] = cvar | 
					
						
							|  |  |  |         self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs) | 
					
						
							|  |  |  |         self.putmessage((seq, request)) | 
					
						
							|  |  |  |         return seq | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     def asyncreturn(self, seq): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug("asyncreturn:%d:call getresponse(): " % seq) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         response = self.getresponse(seq, wait=0.05) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         if how == "QUEUED": | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         if how == "EXCEPTION": | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             self.debug("decoderesponse: EXCEPTION") | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         if how == "EOF": | 
					
						
							|  |  |  |             self.debug("decoderesponse: EOF") | 
					
						
							|  |  |  |             self.decode_interrupthook() | 
					
						
							|  |  |  |             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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def decode_interrupthook(self): | 
					
						
							|  |  |  |         "" | 
					
						
							|  |  |  |         raise EOFError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     def mainloop(self): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         """Listen on socket until I/O not ready or EOF
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         pollresponse() will loop looking for seq number None, which | 
					
						
							| 
									
										
										
										
											2003-03-12 20:52:00 +00:00
										 |  |  |         never comes, and exit on EOFError. | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             self.getresponse(myseq=None, wait=0.05) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         except EOFError: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             self.debug("mainloop:return") | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-03-12 20:52:00 +00:00
										 |  |  |     def getresponse(self, myseq, wait): | 
					
						
							|  |  |  |         response = self._getresponse(myseq, wait) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-03-12 20:52:00 +00:00
										 |  |  |     def _getresponse(self, myseq, wait): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         self.debug("_getresponse:myseq:", myseq) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         if threading.currentThread() is self.sockthread: | 
					
						
							|  |  |  |             # this thread does all reading of requests or responses | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             while 1: | 
					
						
							| 
									
										
										
										
											2003-03-12 20:52:00 +00:00
										 |  |  |                 response = self.pollresponse(myseq, wait) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 if response is not None: | 
					
						
							|  |  |  |                     return response | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             # wait for notification from socket handling thread | 
					
						
							|  |  |  |             cvar = self.cvars[myseq] | 
					
						
							|  |  |  |             cvar.acquire() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             while not self.responses.has_key(myseq): | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 cvar.wait() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             response = self.responses[myseq] | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             self.debug("_getresponse:%s: thread woke up: response: %s" % | 
					
						
							|  |  |  |                        (myseq, response)) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             del self.responses[myseq] | 
					
						
							|  |  |  |             del self.cvars[myseq] | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             cvar.release() | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             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) | 
					
						
							| 
									
										
										
										
											2004-01-21 19:21:11 +00:00
										 |  |  |         except pickle.PicklingError: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             print >>sys.__stderr__, "Cannot pickle:", repr(message) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             raise | 
					
						
							|  |  |  |         s = struct.pack("<i", len(s)) + s | 
					
						
							|  |  |  |         while len(s) > 0: | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2004-01-21 19:21:11 +00:00
										 |  |  |                 r, w, x = select.select([], [self.sock], []) | 
					
						
							|  |  |  |                 n = self.sock.send(s[:BUFSIZE]) | 
					
						
							| 
									
										
										
										
											2005-05-10 03:44:24 +00:00
										 |  |  |             except (AttributeError, TypeError): | 
					
						
							|  |  |  |                 raise IOError, "socket no longer exists" | 
					
						
							|  |  |  |             except socket.error: | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 s = s[n:] | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     buffer = "" | 
					
						
							|  |  |  |     bufneed = 4 | 
					
						
							|  |  |  |     bufstate = 0 # meaning: 0 => reading count; 1 => reading data | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def pollpacket(self, wait): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         self._stage0() | 
					
						
							|  |  |  |         if len(self.buffer) < self.bufneed: | 
					
						
							| 
									
										
										
										
											2004-01-21 19:21:11 +00:00
										 |  |  |             r, w, x = select.select([self.sock.fileno()], [], [], wait) | 
					
						
							|  |  |  |             if len(r) == 0: | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def pollmessage(self, wait): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         packet = self.pollpacket(wait) | 
					
						
							|  |  |  |         if packet is None: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             message = pickle.loads(packet) | 
					
						
							| 
									
										
										
										
											2004-01-21 19:21:11 +00:00
										 |  |  |         except pickle.UnpicklingError: | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             print >>sys.__stderr__, "-----------------------" | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             print >>sys.__stderr__, "cannot unpickle packet:", repr(packet) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             traceback.print_stack(file=sys.__stderr__) | 
					
						
							|  |  |  |             print >>sys.__stderr__, "-----------------------" | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         return message | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def pollresponse(self, myseq, wait): | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |         """Handle messages received on the socket.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         Some messages received may be asynchronous 'call' or 'queue' requests, | 
					
						
							|  |  |  |         and some may be responses for other threads. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         'call' requests are passed to self.localcall() with the expectation of | 
					
						
							|  |  |  |         immediate execution, during which time the socket is not serviced. | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |         'queue' requests are used for tasks (which may block or hang) to be | 
					
						
							|  |  |  |         processed in a different thread.  These requests are fed into | 
					
						
							|  |  |  |         request_queue by self.localcall().  Responses to queued requests are | 
					
						
							|  |  |  |         taken from response_queue and sent across the link with the associated | 
					
						
							|  |  |  |         sequence numbers.  Messages in the queues are (sequence_number, | 
					
						
							|  |  |  |         request/response) tuples and code using this module removing messages | 
					
						
							|  |  |  |         from the request_queue is responsible for returning the correct | 
					
						
							|  |  |  |         sequence number in the response_queue. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pollresponse() will loop until a response message with the myseq | 
					
						
							|  |  |  |         sequence number is received, and will save other responses in | 
					
						
							|  |  |  |         self.responses and notify the owning thread. | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |         while 1: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             # send queued response if there is one available | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 qmsg = response_queue.get(0) | 
					
						
							| 
									
										
										
										
											2008-05-11 19:39:48 +00:00
										 |  |  |             except queue.Empty: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 seq, response = qmsg | 
					
						
							|  |  |  |                 message = (seq, ('OK', response)) | 
					
						
							|  |  |  |                 self.putmessage(message) | 
					
						
							|  |  |  |             # poll for message on link | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 message = self.pollmessage(wait) | 
					
						
							|  |  |  |                 if message is None:  # socket not ready | 
					
						
							|  |  |  |                     return None | 
					
						
							|  |  |  |             except EOFError: | 
					
						
							|  |  |  |                 self.handle_EOF() | 
					
						
							|  |  |  |                 return None | 
					
						
							|  |  |  |             except AttributeError: | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 return None | 
					
						
							|  |  |  |             seq, resq = message | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             how = resq[0] | 
					
						
							| 
									
										
										
										
											2003-01-25 03:26:35 +00:00
										 |  |  |             self.debug("pollresponse:%d:myseq:%s" % (seq, myseq)) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             # process or queue a request | 
					
						
							|  |  |  |             if how in ("CALL", "QUEUE"): | 
					
						
							| 
									
										
										
										
											2003-01-25 21:33:40 +00:00
										 |  |  |                 self.debug("pollresponse:%d:localcall:call:" % seq) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 response = self.localcall(seq, resq) | 
					
						
							| 
									
										
										
										
											2003-01-25 21:33:40 +00:00
										 |  |  |                 self.debug("pollresponse:%d:localcall:response:%s" | 
					
						
							|  |  |  |                            % (seq, response)) | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 if how == "CALL": | 
					
						
							|  |  |  |                     self.putmessage((seq, response)) | 
					
						
							|  |  |  |                 elif how == "QUEUE": | 
					
						
							|  |  |  |                     # don't acknowledge the 'queue' request! | 
					
						
							|  |  |  |                     pass | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             # return if completed message transaction | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             elif seq == myseq: | 
					
						
							|  |  |  |                 return resq | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |             # must be a response for a different thread: | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 cv = self.cvars.get(seq, None) | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                 # response involving unknown sequence number is discarded, | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                 # probably intended for prior incarnation of server | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 if cv is not None: | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                     cv.acquire() | 
					
						
							| 
									
										
										
										
											2003-02-17 18:57:16 +00:00
										 |  |  |                     self.responses[seq] = resq | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                     cv.notify() | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |                     cv.release() | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2002-12-31 16:03:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  |     def handle_EOF(self): | 
					
						
							|  |  |  |         "action taken upon link being closed by peer" | 
					
						
							|  |  |  |         self.EOFhook() | 
					
						
							|  |  |  |         self.debug("handle_EOF") | 
					
						
							|  |  |  |         for key in self.cvars: | 
					
						
							|  |  |  |             cv = self.cvars[key] | 
					
						
							|  |  |  |             cv.acquire() | 
					
						
							|  |  |  |             self.responses[key] = ('EOF', None) | 
					
						
							|  |  |  |             cv.notify() | 
					
						
							|  |  |  |             cv.release() | 
					
						
							|  |  |  |         # call our (possibly overridden) exit function | 
					
						
							|  |  |  |         self.exithook() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def EOFhook(self): | 
					
						
							|  |  |  |         "Classes using rpc client/server can override to augment EOF action" | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-26 00:06:42 +00:00
										 |  |  | #----------------- end class SocketIO -------------------- | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  | class RemoteObject(object): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     # Token mix-in class | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def remoteref(obj): | 
					
						
							|  |  |  |     oid = id(obj) | 
					
						
							|  |  |  |     objecttable[oid] = obj | 
					
						
							|  |  |  |     return RemoteProxy(oid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  | class RemoteProxy(object): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, oid): | 
					
						
							|  |  |  |         self.oid = oid | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  | class RPCHandler(SocketServer.BaseRequestHandler, SocketIO): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +00:00
										 |  |  |         SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr) | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def handle(self): | 
					
						
							| 
									
										
										
										
											2008-05-24 18:31:28 +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 | 
					
						
							| 
									
										
										
										
											2003-06-05 23:51:29 +00:00
										 |  |  |         if address[0] == LOCALHOST: | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  | class RPCProxy(object): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     __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() | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  |         if self.__attributes.has_key(name): | 
					
						
							|  |  |  |             value = self.sockio.remotecall(self.oid, '__getattribute__', | 
					
						
							|  |  |  |                                            (name,), {}) | 
					
						
							|  |  |  |             return value | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |             raise AttributeError, name | 
					
						
							| 
									
										
										
										
											2003-05-08 20:26:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  |     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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-21 22:10:32 +00:00
										 |  |  | class MethodProxy(object): | 
					
						
							| 
									
										
										
										
											2002-05-26 13:36:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-09-10 02:42:18 +00:00
										 |  |  | # XXX KBK 09Sep03  We need a proper unit test for this module.  Previously | 
					
						
							|  |  |  | #                  existing test code was removed at Rev 1.27. |