blob: 54455a260208e7b784734c62fab54a3fb41051c7 [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
Chui Tey5d2af632002-05-26 13:36:41 +0000169 except:
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000170 self.debug("localcall:EXCEPTION")
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000171 traceback.print_exc(file=sys.__stderr__)
Kurt B. Kaiser8cd0def2003-01-31 05:06:43 +0000172 return ("EXCEPTION", None)
173
Chui Tey5d2af632002-05-26 13:36:41 +0000174 def remotecall(self, oid, methodname, args, kwargs):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000175 self.debug("remotecall:asynccall: ", oid, methodname)
176 # XXX KBK 06Feb03 self.interrupted logic may not be necessary if
177 # subprocess is threaded.
178 if self.interrupted:
179 self.interrupted = False
180 raise KeyboardInterrupt
Chui Tey5d2af632002-05-26 13:36:41 +0000181 seq = self.asynccall(oid, methodname, args, kwargs)
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000182 return self.asyncreturn(seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000183
184 def asynccall(self, oid, methodname, args, kwargs):
185 request = ("call", (oid, methodname, args, kwargs))
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000186 seq = self.newseq()
187 self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
188 self.putmessage((seq, request))
Chui Tey5d2af632002-05-26 13:36:41 +0000189 return seq
190
191 def asyncreturn(self, seq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000192 self.debug("asyncreturn:%d:call getresponse(): " % seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000193 response = self.getresponse(seq)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000194 self.debug(("asyncreturn:%d:response: " % seq), response)
Chui Tey5d2af632002-05-26 13:36:41 +0000195 return self.decoderesponse(response)
196
197 def decoderesponse(self, response):
198 how, what = response
199 if how == "OK":
200 return what
201 if how == "EXCEPTION":
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000202 self.debug("decoderesponse: EXCEPTION")
203 return None
Chui Tey5d2af632002-05-26 13:36:41 +0000204 if how == "ERROR":
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000205 self.debug("decoderesponse: Internal ERROR:", what)
Chui Tey5d2af632002-05-26 13:36:41 +0000206 raise RuntimeError, what
207 raise SystemError, (how, what)
208
209 def mainloop(self):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000210 """Listen on socket until I/O not ready or EOF
211
212 pollpacket() will loop looking for seq number None, which never
213 comes. The loop will exit when self.ioready() returns 0.
214
215 """
Chui Tey5d2af632002-05-26 13:36:41 +0000216 try:
217 self.getresponse(None)
218 except EOFError:
219 pass
220
221 def getresponse(self, myseq):
222 response = self._getresponse(myseq)
223 if response is not None:
224 how, what = response
225 if how == "OK":
226 response = how, self._proxify(what)
227 return response
228
229 def _proxify(self, obj):
230 if isinstance(obj, RemoteProxy):
231 return RPCProxy(self, obj.oid)
232 if isinstance(obj, types.ListType):
233 return map(self._proxify, obj)
234 # XXX Check for other types -- not currently needed
235 return obj
236
237 def _getresponse(self, myseq):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000238 self.debug("_getresponse:myseq:", myseq)
Chui Tey5d2af632002-05-26 13:36:41 +0000239 if threading.currentThread() is self.mainthread:
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000240 # Main thread: does all reading of requests or responses
241 # Loop here until there is message traffic on the socket
Chui Tey5d2af632002-05-26 13:36:41 +0000242 while 1:
243 response = self.pollresponse(myseq, None)
244 if response is not None:
245 return response
246 else:
247 # Auxiliary thread: wait for notification from main thread
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000248 self.cvar.acquire()
249 self.cvars[myseq] = self.cvar
Chui Tey5d2af632002-05-26 13:36:41 +0000250 while not self.responses.has_key(myseq):
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000251 self.cvar.wait()
Chui Tey5d2af632002-05-26 13:36:41 +0000252 response = self.responses[myseq]
253 del self.responses[myseq]
254 del self.cvars[myseq]
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000255 self.cvar.release()
256 return response
Chui Tey5d2af632002-05-26 13:36:41 +0000257
258 def newseq(self):
259 self.nextseq = seq = self.nextseq + 2
260 return seq
261
262 def putmessage(self, message):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000263 self.debug("putmessage:%d:" % message[0])
Chui Tey5d2af632002-05-26 13:36:41 +0000264 try:
265 s = pickle.dumps(message)
266 except:
267 print >>sys.__stderr__, "Cannot pickle:", `message`
268 raise
269 s = struct.pack("<i", len(s)) + s
270 while len(s) > 0:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000271 try:
272 n = self.sock.send(s)
273 except AttributeError:
274 # socket was closed
275 raise IOError
276 else:
277 s = s[n:]
Chui Tey5d2af632002-05-26 13:36:41 +0000278
279 def ioready(self, wait=0.0):
280 r, w, x = select.select([self.sock.fileno()], [], [], wait)
281 return len(r)
282
283 buffer = ""
284 bufneed = 4
285 bufstate = 0 # meaning: 0 => reading count; 1 => reading data
286
287 def pollpacket(self, wait=0.0):
288 self._stage0()
289 if len(self.buffer) < self.bufneed:
290 if not self.ioready(wait):
291 return None
292 try:
293 s = self.sock.recv(BUFSIZE)
294 except socket.error:
295 raise EOFError
296 if len(s) == 0:
297 raise EOFError
298 self.buffer += s
299 self._stage0()
300 return self._stage1()
301
302 def _stage0(self):
303 if self.bufstate == 0 and len(self.buffer) >= 4:
304 s = self.buffer[:4]
305 self.buffer = self.buffer[4:]
306 self.bufneed = struct.unpack("<i", s)[0]
307 self.bufstate = 1
308
309 def _stage1(self):
310 if self.bufstate == 1 and len(self.buffer) >= self.bufneed:
311 packet = self.buffer[:self.bufneed]
312 self.buffer = self.buffer[self.bufneed:]
313 self.bufneed = 4
314 self.bufstate = 0
315 return packet
316
317 def pollmessage(self, wait=0.0):
318 packet = self.pollpacket(wait)
319 if packet is None:
320 return None
321 try:
322 message = pickle.loads(packet)
323 except:
324 print >>sys.__stderr__, "-----------------------"
325 print >>sys.__stderr__, "cannot unpickle packet:", `packet`
326 traceback.print_stack(file=sys.__stderr__)
327 print >>sys.__stderr__, "-----------------------"
328 raise
329 return message
330
331 def pollresponse(self, myseq, wait=0.0):
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000332 """Handle messages received on the socket.
333
334 Some messages received may be asynchronous 'call' commands, and
335 some may be responses intended for other threads.
336
337 Loop until message with myseq sequence number is received. Save others
338 in self.responses and notify the owning thread, except that 'call'
339 commands are handed off to localcall() and the response sent back
340 across the link with the appropriate sequence number.
341
342 """
Chui Tey5d2af632002-05-26 13:36:41 +0000343 while 1:
344 message = self.pollmessage(wait)
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000345 if message is None: # socket not ready
Chui Tey5d2af632002-05-26 13:36:41 +0000346 return None
347 wait = 0.0
348 seq, resq = message
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000349 self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
Chui Tey5d2af632002-05-26 13:36:41 +0000350 if resq[0] == "call":
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000351 self.debug("pollresponse:%d:localcall:call:" % seq)
Chui Tey5d2af632002-05-26 13:36:41 +0000352 response = self.localcall(resq)
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000353 self.debug("pollresponse:%d:localcall:response:%s"
354 % (seq, response))
Chui Tey5d2af632002-05-26 13:36:41 +0000355 self.putmessage((seq, response))
356 continue
357 elif seq == myseq:
358 return resq
359 else:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000360 self.cvar.acquire()
Chui Tey5d2af632002-05-26 13:36:41 +0000361 cv = self.cvars.get(seq)
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000362 # response involving unknown sequence number is discarded,
363 # probably intended for prior incarnation
Chui Tey5d2af632002-05-26 13:36:41 +0000364 if cv is not None:
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000365 self.responses[seq] = resq
Chui Tey5d2af632002-05-26 13:36:41 +0000366 cv.notify()
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000367 self.cvar.release()
Chui Tey5d2af632002-05-26 13:36:41 +0000368 continue
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000370#----------------- end class SocketIO --------------------
Chui Tey5d2af632002-05-26 13:36:41 +0000371
372class RemoteObject:
373 # Token mix-in class
374 pass
375
376def remoteref(obj):
377 oid = id(obj)
378 objecttable[oid] = obj
379 return RemoteProxy(oid)
380
381class RemoteProxy:
382
383 def __init__(self, oid):
384 self.oid = oid
385
386class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
387
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000388 debugging = False
389 location = "#S" # Server
Chui Tey5d2af632002-05-26 13:36:41 +0000390
391 def __init__(self, sock, addr, svr):
392 svr.current_handler = self ## cgt xxx
393 SocketIO.__init__(self, sock)
394 SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
395
Chui Tey5d2af632002-05-26 13:36:41 +0000396 def handle(self):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000397 "handle() method required by SocketServer"
Chui Tey5d2af632002-05-26 13:36:41 +0000398 self.mainloop()
399
400 def get_remote_proxy(self, oid):
401 return RPCProxy(self, oid)
402
403class RPCClient(SocketIO):
404
Kurt B. Kaiser0930c432002-12-06 21:45:24 +0000405 debugging = False
406 location = "#C" # Client
407
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000408 nextseq = 1 # Requests coming from the client are odd numbered
Chui Tey5d2af632002-05-26 13:36:41 +0000409
410 def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000411 self.listening_sock = socket.socket(family, type)
412 self.listening_sock.setsockopt(socket.SOL_SOCKET,
413 socket.SO_REUSEADDR, 1)
414 self.listening_sock.bind(address)
415 self.listening_sock.listen(1)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000416
417 def accept(self):
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000418 working_sock, address = self.listening_sock.accept()
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000419 if self.debugging:
Kurt B. Kaiser0a0e6c32003-01-25 03:26:35 +0000420 print>>sys.__stderr__, "****** Connection request from ", address
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000421 if address[0] == '127.0.0.1':
Kurt B. Kaiseradc63842002-08-25 14:08:07 +0000422 SocketIO.__init__(self, working_sock)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000423 else:
Kurt B. Kaiser74d93c82002-12-23 22:51:03 +0000424 print>>sys.__stderr__, "** Invalid host: ", address
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000425 raise socket.error
Chui Tey5d2af632002-05-26 13:36:41 +0000426
427 def get_remote_proxy(self, oid):
428 return RPCProxy(self, oid)
429
430class RPCProxy:
431
432 __methods = None
433 __attributes = None
434
435 def __init__(self, sockio, oid):
436 self.sockio = sockio
437 self.oid = oid
438
439 def __getattr__(self, name):
440 if self.__methods is None:
441 self.__getmethods()
442 if self.__methods.get(name):
443 return MethodProxy(self.sockio, self.oid, name)
444 if self.__attributes is None:
445 self.__getattributes()
446 if not self.__attributes.has_key(name):
447 raise AttributeError, name
448 __getattr__.DebuggerStepThrough=1
449
450 def __getattributes(self):
451 self.__attributes = self.sockio.remotecall(self.oid,
452 "__attributes__", (), {})
453
454 def __getmethods(self):
455 self.__methods = self.sockio.remotecall(self.oid,
456 "__methods__", (), {})
457
458def _getmethods(obj, methods):
459 # Helper to get a list of methods from an object
460 # Adds names to dictionary argument 'methods'
461 for name in dir(obj):
462 attr = getattr(obj, name)
463 if callable(attr):
464 methods[name] = 1
465 if type(obj) == types.InstanceType:
466 _getmethods(obj.__class__, methods)
467 if type(obj) == types.ClassType:
468 for super in obj.__bases__:
469 _getmethods(super, methods)
470
471def _getattributes(obj, attributes):
472 for name in dir(obj):
473 attr = getattr(obj, name)
474 if not callable(attr):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000475 attributes[name] = 1
Chui Tey5d2af632002-05-26 13:36:41 +0000476
477class MethodProxy:
478
479 def __init__(self, sockio, oid, name):
480 self.sockio = sockio
481 self.oid = oid
482 self.name = name
483
484 def __call__(self, *args, **kwargs):
485 value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
486 return value
487
488#
489# Self Test
490#
491
492def testServer(addr):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000493 # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
Chui Tey5d2af632002-05-26 13:36:41 +0000494 class RemotePerson:
495 def __init__(self,name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000496 self.name = name
Chui Tey5d2af632002-05-26 13:36:41 +0000497 def greet(self, name):
498 print "(someone called greet)"
499 print "Hello %s, I am %s." % (name, self.name)
500 print
501 def getName(self):
502 print "(someone called getName)"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000503 print
Chui Tey5d2af632002-05-26 13:36:41 +0000504 return self.name
505 def greet_this_guy(self, name):
506 print "(someone called greet_this_guy)"
507 print "About to greet %s ..." % name
508 remote_guy = self.server.current_handler.get_remote_proxy(name)
509 remote_guy.greet("Thomas Edison")
510 print "Done."
511 print
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000512
Chui Tey5d2af632002-05-26 13:36:41 +0000513 person = RemotePerson("Thomas Edison")
514 svr = RPCServer(addr)
515 svr.register('thomas', person)
516 person.server = svr # only required if callbacks are used
517
518 # svr.serve_forever()
519 svr.handle_request() # process once only
520
521def testClient(addr):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000522 "demonstrates RPC Client"
523 # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
Chui Tey5d2af632002-05-26 13:36:41 +0000524 import time
525 clt=RPCClient(addr)
526 thomas = clt.get_remote_proxy("thomas")
527 print "The remote person's name is ..."
528 print thomas.getName()
529 # print clt.remotecall("thomas", "getName", (), {})
530 print
531 time.sleep(1)
532 print "Getting remote thomas to say hi..."
533 thomas.greet("Alexander Bell")
534 #clt.remotecall("thomas","greet",("Alexander Bell",), {})
535 print "Done."
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000536 print
Chui Tey5d2af632002-05-26 13:36:41 +0000537 time.sleep(2)
Chui Tey5d2af632002-05-26 13:36:41 +0000538 # demonstrates remote server calling local instance
539 class LocalPerson:
540 def __init__(self,name):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000541 self.name = name
Chui Tey5d2af632002-05-26 13:36:41 +0000542 def greet(self, name):
543 print "You've greeted me!"
544 def getName(self):
545 return self.name
546 person = LocalPerson("Alexander Bell")
547 clt.register("alexander",person)
548 thomas.greet_this_guy("alexander")
549 # clt.remotecall("thomas","greet_this_guy",("alexander",), {})
550
551def test():
552 addr=("localhost",8833)
553 if len(sys.argv) == 2:
554 if sys.argv[1]=='-server':
555 testServer(addr)
556 return
557 testClient(addr)
558
559if __name__ == '__main__':
560 test()