blob: 615c1f43bb5dadaa60b0c1828549c812e7a9be96 [file] [log] [blame]
Kurt B. Kaiserb4179362002-07-26 00:06:42 +00001"""RPC Implemention, originally written for the Python Idle IDE
2
3For security reasons, GvR requested that Idle's Python execution server process
4connect to the Idle process, which listens for the connection. Since Idle has
5has only one client per server, this was not a limitation.
6
7 +---------------------------------+ +-------------+
8 | SocketServer.BaseRequestHandler | | SocketIO |
9 +---------------------------------+ +-------------+
10 ^ | register() |
11 | | unregister()|
12 | +-------------+
13 | ^ ^
14 | | |
15 | + -------------------+ |
16 | | |
17 +-------------------------+ +-----------------+
18 | RPCHandler | | RPCClient |
19 | [attribute of RPCServer]| | |
20 +-------------------------+ +-----------------+
21
22The RPCServer handler class is expected to provide register/unregister methods.
23RPCHandler inherits the mix-in class SocketIO, which provides these methods.
24
25See the Idle run.main() docstring for further information on how this was
26accomplished in Idle.
27
28"""
29
30import sys
Chui Tey5d2af632002-05-26 13:36:41 +000031import socket
32import select
33import SocketServer
34import struct
35import cPickle as pickle
36import threading
37import traceback
38import copy_reg
39import types
40import marshal
41
42def unpickle_code(ms):
43 co = marshal.loads(ms)
44 assert isinstance(co, types.CodeType)
45 return co
46
47def pickle_code(co):
48 assert isinstance(co, types.CodeType)
49 ms = marshal.dumps(co)
50 return unpickle_code, (ms,)
51
Kurt B. Kaiseradc63842002-08-25 14:08:07 +000052# XXX KBK 24Aug02 function pickling capability not used in Idle
53# def unpickle_function(ms):
54# return ms
Chui Tey5d2af632002-05-26 13:36:41 +000055
Kurt B. Kaiseradc63842002-08-25 14:08:07 +000056# def pickle_function(fn):
57# assert isinstance(fn, type.FunctionType)
58# return `fn`
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000059
Chui Tey5d2af632002-05-26 13:36:41 +000060copy_reg.pickle(types.CodeType, pickle_code, unpickle_code)
Kurt B. Kaiseradc63842002-08-25 14:08:07 +000061# copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function)
Chui Tey5d2af632002-05-26 13:36:41 +000062
63BUFSIZE = 8*1024
64
65class RPCServer(SocketServer.TCPServer):
66
67 def __init__(self, addr, handlerclass=None):
68 if handlerclass is None:
69 handlerclass = RPCHandler
Chui Tey5d2af632002-05-26 13:36:41 +000070 SocketServer.TCPServer.__init__(self, addr, handlerclass)
71
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000072 def server_bind(self):
73 "Override TCPServer method, no bind() phase for connecting entity"
74 pass
Chui Tey5d2af632002-05-26 13:36:41 +000075
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000076 def server_activate(self):
77 """Override TCPServer method, connect() instead of listen()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000078
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000079 Due to the reversed connection, self.server_address is actually the
80 address of the Idle Client to which we are connecting.
Chui Tey5d2af632002-05-26 13:36:41 +000081
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000082 """
83 self.socket.connect(self.server_address)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000084
Kurt B. Kaiserb4179362002-07-26 00:06:42 +000085 def get_request(self):
86 "Override TCPServer method, return already connected socket"
87 return self.socket, self.server_address
Chui Tey5d2af632002-05-26 13:36:41 +000088
Kurt B. Kaiser003091c2003-02-17 18:57:16 +000089 def handle_error(self, request, client_address):
90 """Override TCPServer method, no error message if exiting"""
91 try:
92 raise
93 except SystemExit:
94 raise
95 else:
96 TCPServer.handle_error(request, client_address)
97
98
Chui Tey5d2af632002-05-26 13:36:41 +000099objecttable = {}
100
101class SocketIO:
102
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000103 nextseq = 0
104
Chui Tey5d2af632002-05-26 13:36:41 +0000105 def __init__(self, sock, objtable=None, debugging=None):
106 self.mainthread = threading.currentThread()
107 if debugging is not None:
108 self.debugging = debugging
109 self.sock = sock
110 if objtable is None:
111 objtable = objecttable
112 self.objtable = objtable
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000113 self.cvar = threading.Condition()
Chui Tey5d2af632002-05-26 13:36:41 +0000114 self.responses = {}
115 self.cvars = {}
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000116 self.interrupted = False
Chui Tey5d2af632002-05-26 13:36:41 +0000117
118 def close(self):
119 sock = self.sock
120 self.sock = None
121 if sock is not None:
122 sock.close()
123
124 def debug(self, *args):
125 if not self.debugging:
126 return
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000127 s = self.location + " " + str(threading.currentThread().getName())
Chui Tey5d2af632002-05-26 13:36:41 +0000128 for a in args:
129 s = s + " " + str(a)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000130 print>>sys.__stderr__, s
Chui Tey5d2af632002-05-26 13:36:41 +0000131
132 def register(self, oid, object):
133 self.objtable[oid] = object
134
135 def unregister(self, oid):
136 try:
137 del self.objtable[oid]
138 except KeyError:
139 pass
140
141 def localcall(self, request):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000142 self.debug("localcall:", request)
Chui Tey5d2af632002-05-26 13:36:41 +0000143 try:
144 how, (oid, methodname, args, kwargs) = request
145 except TypeError:
146 return ("ERROR", "Bad request format")
147 assert how == "call"
148 if not self.objtable.has_key(oid):
149 return ("ERROR", "Unknown object id: %s" % `oid`)
150 obj = self.objtable[oid]
151 if methodname == "__methods__":
152 methods = {}
153 _getmethods(obj, methods)
154 return ("OK", methods)
155 if methodname == "__attributes__":
156 attributes = {}
157 _getattributes(obj, attributes)
158 return ("OK", attributes)
159 if not hasattr(obj, methodname):
160 return ("ERROR", "Unsupported method name: %s" % `methodname`)
161 method = getattr(obj, methodname)
162 try:
163 ret = method(*args, **kwargs)
164 if isinstance(ret, RemoteObject):
165 ret = remoteref(ret)
166 return ("OK", ret)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000167 except SystemExit:
168 raise
Kurt B. Kaiser9ac783d2003-03-10 20:42:24 +0000169 except socket.error:
170 pass
Chui Tey5d2af632002-05-26 13:36:41 +0000171 except:
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000172 self.debug("localcall:EXCEPTION")
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000173 traceback.print_exc(file=sys.__stderr__)
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000174 return ("EXCEPTION", None)
175
Chui Tey5d2af632002-05-26 13:36:41 +0000176 def remotecall(self, oid, methodname, args, kwargs):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000177 self.debug("remotecall:asynccall: ", oid, methodname)
178 # XXX KBK 06Feb03 self.interrupted logic may not be necessary if
179 # subprocess is threaded.
180 if self.interrupted:
181 self.interrupted = False
182 raise KeyboardInterrupt
Chui Tey5d2af632002-05-26 13:36:41 +0000183 seq = self.asynccall(oid, methodname, args, kwargs)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000184 return self.asyncreturn(seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000185
186 def asynccall(self, oid, methodname, args, kwargs):
187 request = ("call", (oid, methodname, args, kwargs))
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000188 seq = self.newseq()
189 self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
190 self.putmessage((seq, request))
Chui Tey5d2af632002-05-26 13:36:41 +0000191 return seq
192
193 def asyncreturn(self, seq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000194 self.debug("asyncreturn:%d:call getresponse(): " % seq)
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000195 response = self.getresponse(seq, wait=None)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000196 self.debug(("asyncreturn:%d:response: " % seq), response)
Chui Tey5d2af632002-05-26 13:36:41 +0000197 return self.decoderesponse(response)
198
199 def decoderesponse(self, response):
200 how, what = response
201 if how == "OK":
202 return what
203 if how == "EXCEPTION":
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000204 self.debug("decoderesponse: EXCEPTION")
205 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000206 if how == "ERROR":
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000207 self.debug("decoderesponse: Internal ERROR:", what)
Chui Tey5d2af632002-05-26 13:36:41 +0000208 raise RuntimeError, what
209 raise SystemError, (how, what)
210
211 def mainloop(self):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000212 """Listen on socket until I/O not ready or EOF
213
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000214 Main thread pollresponse() will loop looking for seq number None, which
215 never comes, and exit on EOFError.
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000216
217 """
Chui Tey5d2af632002-05-26 13:36:41 +0000218 try:
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000219 self.getresponse(myseq=None, wait=None)
Chui Tey5d2af632002-05-26 13:36:41 +0000220 except EOFError:
221 pass
222
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000223 def getresponse(self, myseq, wait):
224 response = self._getresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000225 if response is not None:
226 how, what = response
227 if how == "OK":
228 response = how, self._proxify(what)
229 return response
230
231 def _proxify(self, obj):
232 if isinstance(obj, RemoteProxy):
233 return RPCProxy(self, obj.oid)
234 if isinstance(obj, types.ListType):
235 return map(self._proxify, obj)
236 # XXX Check for other types -- not currently needed
237 return obj
238
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000239 def _getresponse(self, myseq, wait):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000240 self.debug("_getresponse:myseq:", myseq)
Chui Tey5d2af632002-05-26 13:36:41 +0000241 if threading.currentThread() is self.mainthread:
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000242 # Main thread: does all reading of requests or responses
Kurt B. Kaiser7c221322003-03-11 22:55:56 +0000243 # Loop here, blocking each time until socket is ready.
Chui Tey5d2af632002-05-26 13:36:41 +0000244 while 1:
Kurt B. Kaiser94afd302003-03-12 20:52:00 +0000245 response = self.pollresponse(myseq, wait)
Chui Tey5d2af632002-05-26 13:36:41 +0000246 if response is not None:
247 return response
248 else:
249 # Auxiliary thread: wait for notification from main thread
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000250 self.cvar.acquire()
251 self.cvars[myseq] = self.cvar
Chui Tey5d2af632002-05-26 13:36:41 +0000252 while not self.responses.has_key(myseq):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000253 self.cvar.wait()
Chui Tey5d2af632002-05-26 13:36:41 +0000254 response = self.responses[myseq]
255 del self.responses[myseq]
256 del self.cvars[myseq]
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000257 self.cvar.release()
258 return response
Chui Tey5d2af632002-05-26 13:36:41 +0000259
260 def newseq(self):
261 self.nextseq = seq = self.nextseq + 2
262 return seq
263
264 def putmessage(self, message):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000265 self.debug("putmessage:%d:" % message[0])
Chui Tey5d2af632002-05-26 13:36:41 +0000266 try:
267 s = pickle.dumps(message)
268 except:
269 print >>sys.__stderr__, "Cannot pickle:", `message`
270 raise
271 s = struct.pack("<i", len(s)) + s
272 while len(s) > 0:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000273 try:
274 n = self.sock.send(s)
275 except AttributeError:
276 # socket was closed
277 raise IOError
278 else:
279 s = s[n:]
Chui Tey5d2af632002-05-26 13:36:41 +0000280
281 def ioready(self, wait=0.0):
282 r, w, x = select.select([self.sock.fileno()], [], [], wait)
283 return len(r)
284
285 buffer = ""
286 bufneed = 4
287 bufstate = 0 # meaning: 0 => reading count; 1 => reading data
288
289 def pollpacket(self, wait=0.0):
290 self._stage0()
291 if len(self.buffer) < self.bufneed:
292 if not self.ioready(wait):
293 return None
294 try:
295 s = self.sock.recv(BUFSIZE)
296 except socket.error:
297 raise EOFError
298 if len(s) == 0:
299 raise EOFError
300 self.buffer += s
301 self._stage0()
302 return self._stage1()
303
304 def _stage0(self):
305 if self.bufstate == 0 and len(self.buffer) >= 4:
306 s = self.buffer[:4]
307 self.buffer = self.buffer[4:]
308 self.bufneed = struct.unpack("<i", s)[0]
309 self.bufstate = 1
310
311 def _stage1(self):
312 if self.bufstate == 1 and len(self.buffer) >= self.bufneed:
313 packet = self.buffer[:self.bufneed]
314 self.buffer = self.buffer[self.bufneed:]
315 self.bufneed = 4
316 self.bufstate = 0
317 return packet
318
319 def pollmessage(self, wait=0.0):
320 packet = self.pollpacket(wait)
321 if packet is None:
322 return None
323 try:
324 message = pickle.loads(packet)
325 except:
326 print >>sys.__stderr__, "-----------------------"
327 print >>sys.__stderr__, "cannot unpickle packet:", `packet`
328 traceback.print_stack(file=sys.__stderr__)
329 print >>sys.__stderr__, "-----------------------"
330 raise
331 return message
332
333 def pollresponse(self, myseq, wait=0.0):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000334 """Handle messages received on the socket.
335
336 Some messages received may be asynchronous 'call' commands, and
337 some may be responses intended for other threads.
338
339 Loop until message with myseq sequence number is received. Save others
340 in self.responses and notify the owning thread, except that 'call'
341 commands are handed off to localcall() and the response sent back
342 across the link with the appropriate sequence number.
343
344 """
Chui Tey5d2af632002-05-26 13:36:41 +0000345 while 1:
346 message = self.pollmessage(wait)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000347 if message is None: # socket not ready
Chui Tey5d2af632002-05-26 13:36:41 +0000348 return None
Kurt B. Kaiser7c221322003-03-11 22:55:56 +0000349 #wait = 0.0 # poll on subsequent passes instead of blocking
Chui Tey5d2af632002-05-26 13:36:41 +0000350 seq, resq = message
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000351 self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
Chui Tey5d2af632002-05-26 13:36:41 +0000352 if resq[0] == "call":
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000353 self.debug("pollresponse:%d:localcall:call:" % seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000354 response = self.localcall(resq)
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000355 self.debug("pollresponse:%d:localcall:response:%s"
356 % (seq, response))
Chui Tey5d2af632002-05-26 13:36:41 +0000357 self.putmessage((seq, response))
358 continue
359 elif seq == myseq:
360 return resq
361 else:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000362 self.cvar.acquire()
Chui Tey5d2af632002-05-26 13:36:41 +0000363 cv = self.cvars.get(seq)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000364 # response involving unknown sequence number is discarded,
365 # probably intended for prior incarnation
Chui Tey5d2af632002-05-26 13:36:41 +0000366 if cv is not None:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000367 self.responses[seq] = resq
Chui Tey5d2af632002-05-26 13:36:41 +0000368 cv.notify()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000369 self.cvar.release()
Chui Tey5d2af632002-05-26 13:36:41 +0000370 continue
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000371
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000372#----------------- end class SocketIO --------------------
Chui Tey5d2af632002-05-26 13:36:41 +0000373
374class RemoteObject:
375 # Token mix-in class
376 pass
377
378def remoteref(obj):
379 oid = id(obj)
380 objecttable[oid] = obj
381 return RemoteProxy(oid)
382
383class RemoteProxy:
384
385 def __init__(self, oid):
386 self.oid = oid
387
388class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
389
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000390 debugging = False
391 location = "#S" # Server
Chui Tey5d2af632002-05-26 13:36:41 +0000392
393 def __init__(self, sock, addr, svr):
394 svr.current_handler = self ## cgt xxx
395 SocketIO.__init__(self, sock)
396 SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
397
Chui Tey5d2af632002-05-26 13:36:41 +0000398 def handle(self):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000399 "handle() method required by SocketServer"
Chui Tey5d2af632002-05-26 13:36:41 +0000400 self.mainloop()
401
402 def get_remote_proxy(self, oid):
403 return RPCProxy(self, oid)
404
405class RPCClient(SocketIO):
406
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000407 debugging = False
408 location = "#C" # Client
409
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000410 nextseq = 1 # Requests coming from the client are odd numbered
Chui Tey5d2af632002-05-26 13:36:41 +0000411
412 def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000413 self.listening_sock = socket.socket(family, type)
414 self.listening_sock.setsockopt(socket.SOL_SOCKET,
415 socket.SO_REUSEADDR, 1)
416 self.listening_sock.bind(address)
417 self.listening_sock.listen(1)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000418
419 def accept(self):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000420 working_sock, address = self.listening_sock.accept()
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000421 if self.debugging:
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000422 print>>sys.__stderr__, "****** Connection request from ", address
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000423 if address[0] == '127.0.0.1':
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000424 SocketIO.__init__(self, working_sock)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000425 else:
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000426 print>>sys.__stderr__, "** Invalid host: ", address
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000427 raise socket.error
Chui Tey5d2af632002-05-26 13:36:41 +0000428
429 def get_remote_proxy(self, oid):
430 return RPCProxy(self, oid)
431
432class RPCProxy:
433
434 __methods = None
435 __attributes = None
436
437 def __init__(self, sockio, oid):
438 self.sockio = sockio
439 self.oid = oid
440
441 def __getattr__(self, name):
442 if self.__methods is None:
443 self.__getmethods()
444 if self.__methods.get(name):
445 return MethodProxy(self.sockio, self.oid, name)
446 if self.__attributes is None:
447 self.__getattributes()
448 if not self.__attributes.has_key(name):
449 raise AttributeError, name
450 __getattr__.DebuggerStepThrough=1
451
452 def __getattributes(self):
453 self.__attributes = self.sockio.remotecall(self.oid,
454 "__attributes__", (), {})
455
456 def __getmethods(self):
457 self.__methods = self.sockio.remotecall(self.oid,
458 "__methods__", (), {})
459
460def _getmethods(obj, methods):
461 # Helper to get a list of methods from an object
462 # Adds names to dictionary argument 'methods'
463 for name in dir(obj):
464 attr = getattr(obj, name)
465 if callable(attr):
466 methods[name] = 1
467 if type(obj) == types.InstanceType:
468 _getmethods(obj.__class__, methods)
469 if type(obj) == types.ClassType:
470 for super in obj.__bases__:
471 _getmethods(super, methods)
472
473def _getattributes(obj, attributes):
474 for name in dir(obj):
475 attr = getattr(obj, name)
476 if not callable(attr):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000477 attributes[name] = 1
Chui Tey5d2af632002-05-26 13:36:41 +0000478
479class MethodProxy:
480
481 def __init__(self, sockio, oid, name):
482 self.sockio = sockio
483 self.oid = oid
484 self.name = name
485
486 def __call__(self, *args, **kwargs):
487 value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
488 return value
489
490#
491# Self Test
492#
493
494def testServer(addr):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000495 # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
Chui Tey5d2af632002-05-26 13:36:41 +0000496 class RemotePerson:
497 def __init__(self,name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000498 self.name = name
Chui Tey5d2af632002-05-26 13:36:41 +0000499 def greet(self, name):
500 print "(someone called greet)"
501 print "Hello %s, I am %s." % (name, self.name)
502 print
503 def getName(self):
504 print "(someone called getName)"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000505 print
Chui Tey5d2af632002-05-26 13:36:41 +0000506 return self.name
507 def greet_this_guy(self, name):
508 print "(someone called greet_this_guy)"
509 print "About to greet %s ..." % name
510 remote_guy = self.server.current_handler.get_remote_proxy(name)
511 remote_guy.greet("Thomas Edison")
512 print "Done."
513 print
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000514
Chui Tey5d2af632002-05-26 13:36:41 +0000515 person = RemotePerson("Thomas Edison")
516 svr = RPCServer(addr)
517 svr.register('thomas', person)
518 person.server = svr # only required if callbacks are used
519
520 # svr.serve_forever()
521 svr.handle_request() # process once only
522
523def testClient(addr):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000524 "demonstrates RPC Client"
525 # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
Chui Tey5d2af632002-05-26 13:36:41 +0000526 import time
527 clt=RPCClient(addr)
528 thomas = clt.get_remote_proxy("thomas")
529 print "The remote person's name is ..."
530 print thomas.getName()
531 # print clt.remotecall("thomas", "getName", (), {})
532 print
533 time.sleep(1)
534 print "Getting remote thomas to say hi..."
535 thomas.greet("Alexander Bell")
536 #clt.remotecall("thomas","greet",("Alexander Bell",), {})
537 print "Done."
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000538 print
Chui Tey5d2af632002-05-26 13:36:41 +0000539 time.sleep(2)
Chui Tey5d2af632002-05-26 13:36:41 +0000540 # demonstrates remote server calling local instance
541 class LocalPerson:
542 def __init__(self,name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000543 self.name = name
Chui Tey5d2af632002-05-26 13:36:41 +0000544 def greet(self, name):
545 print "You've greeted me!"
546 def getName(self):
547 return self.name
548 person = LocalPerson("Alexander Bell")
549 clt.register("alexander",person)
550 thomas.greet_this_guy("alexander")
551 # clt.remotecall("thomas","greet_this_guy",("alexander",), {})
552
553def test():
554 addr=("localhost",8833)
555 if len(sys.argv) == 2:
556 if sys.argv[1]=='-server':
557 testServer(addr)
558 return
559 testClient(addr)
560
561if __name__ == '__main__':
562 test()