PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/lib-python/modified-2.7/idlelib/rpc.py

https://bitbucket.org/dac_io/pypy
Python | 600 lines | 513 code | 35 blank | 52 comment | 21 complexity | b19e20ee21e13164f645de2512f2eab0 MD5 | raw file
  1. """RPC Implemention, originally written for the Python Idle IDE
  2. For security reasons, GvR requested that Idle's Python execution server process
  3. connect to the Idle process, which listens for the connection. Since Idle has
  4. has only one client per server, this was not a limitation.
  5. +---------------------------------+ +-------------+
  6. | SocketServer.BaseRequestHandler | | SocketIO |
  7. +---------------------------------+ +-------------+
  8. ^ | register() |
  9. | | unregister()|
  10. | +-------------+
  11. | ^ ^
  12. | | |
  13. | + -------------------+ |
  14. | | |
  15. +-------------------------+ +-----------------+
  16. | RPCHandler | | RPCClient |
  17. | [attribute of RPCServer]| | |
  18. +-------------------------+ +-----------------+
  19. The RPCServer handler class is expected to provide register/unregister methods.
  20. RPCHandler inherits the mix-in class SocketIO, which provides these methods.
  21. See the Idle run.main() docstring for further information on how this was
  22. accomplished in Idle.
  23. """
  24. import sys
  25. import os
  26. import socket
  27. import select
  28. import SocketServer
  29. import struct
  30. import cPickle as pickle
  31. import threading
  32. import Queue
  33. import traceback
  34. import copy_reg
  35. import types
  36. import marshal
  37. def unpickle_code(ms):
  38. co = marshal.loads(ms)
  39. assert isinstance(co, types.CodeType)
  40. return co
  41. def pickle_code(co):
  42. assert isinstance(co, types.CodeType)
  43. ms = marshal.dumps(co)
  44. return unpickle_code, (ms,)
  45. # XXX KBK 24Aug02 function pickling capability not used in Idle
  46. # def unpickle_function(ms):
  47. # return ms
  48. # def pickle_function(fn):
  49. # assert isinstance(fn, type.FunctionType)
  50. # return repr(fn)
  51. copy_reg.pickle(types.CodeType, pickle_code, unpickle_code)
  52. # copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function)
  53. BUFSIZE = 8*1024
  54. LOCALHOST = '127.0.0.1'
  55. class RPCServer(SocketServer.TCPServer):
  56. def __init__(self, addr, handlerclass=None):
  57. if handlerclass is None:
  58. handlerclass = RPCHandler
  59. SocketServer.TCPServer.__init__(self, addr, handlerclass)
  60. def server_bind(self):
  61. "Override TCPServer method, no bind() phase for connecting entity"
  62. pass
  63. def server_activate(self):
  64. """Override TCPServer method, connect() instead of listen()
  65. Due to the reversed connection, self.server_address is actually the
  66. address of the Idle Client to which we are connecting.
  67. """
  68. self.socket.connect(self.server_address)
  69. def get_request(self):
  70. "Override TCPServer method, return already connected socket"
  71. return self.socket, self.server_address
  72. def handle_error(self, request, client_address):
  73. """Override TCPServer method
  74. Error message goes to __stderr__. No error message if exiting
  75. normally or socket raised EOF. Other exceptions not handled in
  76. server code will cause os._exit.
  77. """
  78. try:
  79. raise
  80. except SystemExit:
  81. raise
  82. except:
  83. erf = sys.__stderr__
  84. print>>erf, '\n' + '-'*40
  85. print>>erf, 'Unhandled server exception!'
  86. print>>erf, 'Thread: %s' % threading.currentThread().getName()
  87. print>>erf, 'Client Address: ', client_address
  88. print>>erf, 'Request: ', repr(request)
  89. traceback.print_exc(file=erf)
  90. print>>erf, '\n*** Unrecoverable, server exiting!'
  91. print>>erf, '-'*40
  92. os._exit(0)
  93. #----------------- end class RPCServer --------------------
  94. objecttable = {}
  95. request_queue = Queue.Queue(0)
  96. response_queue = Queue.Queue(0)
  97. class SocketIO(object):
  98. nextseq = 0
  99. def __init__(self, sock, objtable=None, debugging=None):
  100. self.sockthread = threading.currentThread()
  101. if debugging is not None:
  102. self.debugging = debugging
  103. self.sock = sock
  104. if objtable is None:
  105. objtable = objecttable
  106. self.objtable = objtable
  107. self.responses = {}
  108. self.cvars = {}
  109. def close(self):
  110. sock = self.sock
  111. self.sock = None
  112. if sock is not None:
  113. sock.close()
  114. def exithook(self):
  115. "override for specific exit action"
  116. os._exit()
  117. def debug(self, *args):
  118. if not self.debugging:
  119. return
  120. s = self.location + " " + str(threading.currentThread().getName())
  121. for a in args:
  122. s = s + " " + str(a)
  123. print>>sys.__stderr__, s
  124. def register(self, oid, object):
  125. self.objtable[oid] = object
  126. def unregister(self, oid):
  127. try:
  128. del self.objtable[oid]
  129. except KeyError:
  130. pass
  131. def localcall(self, seq, request):
  132. self.debug("localcall:", request)
  133. try:
  134. how, (oid, methodname, args, kwargs) = request
  135. except TypeError:
  136. return ("ERROR", "Bad request format")
  137. if oid not in self.objtable:
  138. return ("ERROR", "Unknown object id: %r" % (oid,))
  139. obj = self.objtable[oid]
  140. if methodname == "__methods__":
  141. methods = {}
  142. _getmethods(obj, methods)
  143. return ("OK", methods)
  144. if methodname == "__attributes__":
  145. attributes = {}
  146. _getattributes(obj, attributes)
  147. return ("OK", attributes)
  148. if not hasattr(obj, methodname):
  149. return ("ERROR", "Unsupported method name: %r" % (methodname,))
  150. method = getattr(obj, methodname)
  151. try:
  152. if how == 'CALL':
  153. ret = method(*args, **kwargs)
  154. if isinstance(ret, RemoteObject):
  155. ret = remoteref(ret)
  156. return ("OK", ret)
  157. elif how == 'QUEUE':
  158. request_queue.put((seq, (method, args, kwargs)))
  159. return("QUEUED", None)
  160. else:
  161. return ("ERROR", "Unsupported message type: %s" % how)
  162. except SystemExit:
  163. raise
  164. except socket.error:
  165. raise
  166. except:
  167. msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\
  168. " Object: %s \n Method: %s \n Args: %s\n"
  169. print>>sys.__stderr__, msg % (oid, method, args)
  170. traceback.print_exc(file=sys.__stderr__)
  171. return ("EXCEPTION", None)
  172. def remotecall(self, oid, methodname, args, kwargs):
  173. self.debug("remotecall:asynccall: ", oid, methodname)
  174. seq = self.asynccall(oid, methodname, args, kwargs)
  175. return self.asyncreturn(seq)
  176. def remotequeue(self, oid, methodname, args, kwargs):
  177. self.debug("remotequeue:asyncqueue: ", oid, methodname)
  178. seq = self.asyncqueue(oid, methodname, args, kwargs)
  179. return self.asyncreturn(seq)
  180. def asynccall(self, oid, methodname, args, kwargs):
  181. request = ("CALL", (oid, methodname, args, kwargs))
  182. seq = self.newseq()
  183. if threading.currentThread() != self.sockthread:
  184. cvar = threading.Condition()
  185. self.cvars[seq] = cvar
  186. self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
  187. self.putmessage((seq, request))
  188. return seq
  189. def asyncqueue(self, oid, methodname, args, kwargs):
  190. request = ("QUEUE", (oid, methodname, args, kwargs))
  191. seq = self.newseq()
  192. if threading.currentThread() != self.sockthread:
  193. cvar = threading.Condition()
  194. self.cvars[seq] = cvar
  195. self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
  196. self.putmessage((seq, request))
  197. return seq
  198. def asyncreturn(self, seq):
  199. self.debug("asyncreturn:%d:call getresponse(): " % seq)
  200. response = self.getresponse(seq, wait=0.05)
  201. self.debug(("asyncreturn:%d:response: " % seq), response)
  202. return self.decoderesponse(response)
  203. def decoderesponse(self, response):
  204. how, what = response
  205. if how == "OK":
  206. return what
  207. if how == "QUEUED":
  208. return None
  209. if how == "EXCEPTION":
  210. self.debug("decoderesponse: EXCEPTION")
  211. return None
  212. if how == "EOF":
  213. self.debug("decoderesponse: EOF")
  214. self.decode_interrupthook()
  215. return None
  216. if how == "ERROR":
  217. self.debug("decoderesponse: Internal ERROR:", what)
  218. raise RuntimeError, what
  219. raise SystemError, (how, what)
  220. def decode_interrupthook(self):
  221. ""
  222. raise EOFError
  223. def mainloop(self):
  224. """Listen on socket until I/O not ready or EOF
  225. pollresponse() will loop looking for seq number None, which
  226. never comes, and exit on EOFError.
  227. """
  228. try:
  229. self.getresponse(myseq=None, wait=0.05)
  230. except EOFError:
  231. self.debug("mainloop:return")
  232. return
  233. def getresponse(self, myseq, wait):
  234. response = self._getresponse(myseq, wait)
  235. if response is not None:
  236. how, what = response
  237. if how == "OK":
  238. response = how, self._proxify(what)
  239. return response
  240. def _proxify(self, obj):
  241. if isinstance(obj, RemoteProxy):
  242. return RPCProxy(self, obj.oid)
  243. if isinstance(obj, types.ListType):
  244. return map(self._proxify, obj)
  245. # XXX Check for other types -- not currently needed
  246. return obj
  247. def _getresponse(self, myseq, wait):
  248. self.debug("_getresponse:myseq:", myseq)
  249. if threading.currentThread() is self.sockthread:
  250. # this thread does all reading of requests or responses
  251. while 1:
  252. response = self.pollresponse(myseq, wait)
  253. if response is not None:
  254. return response
  255. else:
  256. # wait for notification from socket handling thread
  257. cvar = self.cvars[myseq]
  258. cvar.acquire()
  259. while myseq not in self.responses:
  260. cvar.wait()
  261. response = self.responses[myseq]
  262. self.debug("_getresponse:%s: thread woke up: response: %s" %
  263. (myseq, response))
  264. del self.responses[myseq]
  265. del self.cvars[myseq]
  266. cvar.release()
  267. return response
  268. def newseq(self):
  269. self.nextseq = seq = self.nextseq + 2
  270. return seq
  271. def putmessage(self, message):
  272. self.debug("putmessage:%d:" % message[0])
  273. try:
  274. s = pickle.dumps(message)
  275. except pickle.PicklingError:
  276. print >>sys.__stderr__, "Cannot pickle:", repr(message)
  277. raise
  278. s = struct.pack("<i", len(s)) + s
  279. while len(s) > 0:
  280. try:
  281. r, w, x = select.select([], [self.sock], [])
  282. n = self.sock.send(s[:BUFSIZE])
  283. except (AttributeError, TypeError):
  284. raise IOError, "socket no longer exists"
  285. except socket.error:
  286. raise
  287. else:
  288. s = s[n:]
  289. buffer = ""
  290. bufneed = 4
  291. bufstate = 0 # meaning: 0 => reading count; 1 => reading data
  292. def pollpacket(self, wait):
  293. self._stage0()
  294. if len(self.buffer) < self.bufneed:
  295. r, w, x = select.select([self.sock.fileno()], [], [], wait)
  296. if len(r) == 0:
  297. return None
  298. try:
  299. s = self.sock.recv(BUFSIZE)
  300. except socket.error:
  301. raise EOFError
  302. if len(s) == 0:
  303. raise EOFError
  304. self.buffer += s
  305. self._stage0()
  306. return self._stage1()
  307. def _stage0(self):
  308. if self.bufstate == 0 and len(self.buffer) >= 4:
  309. s = self.buffer[:4]
  310. self.buffer = self.buffer[4:]
  311. self.bufneed = struct.unpack("<i", s)[0]
  312. self.bufstate = 1
  313. def _stage1(self):
  314. if self.bufstate == 1 and len(self.buffer) >= self.bufneed:
  315. packet = self.buffer[:self.bufneed]
  316. self.buffer = self.buffer[self.bufneed:]
  317. self.bufneed = 4
  318. self.bufstate = 0
  319. return packet
  320. def pollmessage(self, wait):
  321. packet = self.pollpacket(wait)
  322. if packet is None:
  323. return None
  324. try:
  325. message = pickle.loads(packet)
  326. except pickle.UnpicklingError:
  327. print >>sys.__stderr__, "-----------------------"
  328. print >>sys.__stderr__, "cannot unpickle packet:", repr(packet)
  329. traceback.print_stack(file=sys.__stderr__)
  330. print >>sys.__stderr__, "-----------------------"
  331. raise
  332. return message
  333. def pollresponse(self, myseq, wait):
  334. """Handle messages received on the socket.
  335. Some messages received may be asynchronous 'call' or 'queue' requests,
  336. and some may be responses for other threads.
  337. 'call' requests are passed to self.localcall() with the expectation of
  338. immediate execution, during which time the socket is not serviced.
  339. 'queue' requests are used for tasks (which may block or hang) to be
  340. processed in a different thread. These requests are fed into
  341. request_queue by self.localcall(). Responses to queued requests are
  342. taken from response_queue and sent across the link with the associated
  343. sequence numbers. Messages in the queues are (sequence_number,
  344. request/response) tuples and code using this module removing messages
  345. from the request_queue is responsible for returning the correct
  346. sequence number in the response_queue.
  347. pollresponse() will loop until a response message with the myseq
  348. sequence number is received, and will save other responses in
  349. self.responses and notify the owning thread.
  350. """
  351. while 1:
  352. # send queued response if there is one available
  353. try:
  354. qmsg = response_queue.get(0)
  355. except Queue.Empty:
  356. pass
  357. else:
  358. seq, response = qmsg
  359. message = (seq, ('OK', response))
  360. self.putmessage(message)
  361. # poll for message on link
  362. try:
  363. message = self.pollmessage(wait)
  364. if message is None: # socket not ready
  365. return None
  366. except EOFError:
  367. self.handle_EOF()
  368. return None
  369. except AttributeError:
  370. return None
  371. seq, resq = message
  372. how = resq[0]
  373. self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
  374. # process or queue a request
  375. if how in ("CALL", "QUEUE"):
  376. self.debug("pollresponse:%d:localcall:call:" % seq)
  377. response = self.localcall(seq, resq)
  378. self.debug("pollresponse:%d:localcall:response:%s"
  379. % (seq, response))
  380. if how == "CALL":
  381. self.putmessage((seq, response))
  382. elif how == "QUEUE":
  383. # don't acknowledge the 'queue' request!
  384. pass
  385. continue
  386. # return if completed message transaction
  387. elif seq == myseq:
  388. return resq
  389. # must be a response for a different thread:
  390. else:
  391. cv = self.cvars.get(seq, None)
  392. # response involving unknown sequence number is discarded,
  393. # probably intended for prior incarnation of server
  394. if cv is not None:
  395. cv.acquire()
  396. self.responses[seq] = resq
  397. cv.notify()
  398. cv.release()
  399. continue
  400. def handle_EOF(self):
  401. "action taken upon link being closed by peer"
  402. self.EOFhook()
  403. self.debug("handle_EOF")
  404. for key in self.cvars:
  405. cv = self.cvars[key]
  406. cv.acquire()
  407. self.responses[key] = ('EOF', None)
  408. cv.notify()
  409. cv.release()
  410. # call our (possibly overridden) exit function
  411. self.exithook()
  412. def EOFhook(self):
  413. "Classes using rpc client/server can override to augment EOF action"
  414. pass
  415. #----------------- end class SocketIO --------------------
  416. class RemoteObject(object):
  417. # Token mix-in class
  418. pass
  419. def remoteref(obj):
  420. oid = id(obj)
  421. objecttable[oid] = obj
  422. return RemoteProxy(oid)
  423. class RemoteProxy(object):
  424. def __init__(self, oid):
  425. self.oid = oid
  426. class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
  427. debugging = False
  428. location = "#S" # Server
  429. def __init__(self, sock, addr, svr):
  430. svr.current_handler = self ## cgt xxx
  431. SocketIO.__init__(self, sock)
  432. SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
  433. def handle(self):
  434. "handle() method required by SocketServer"
  435. self.mainloop()
  436. def get_remote_proxy(self, oid):
  437. return RPCProxy(self, oid)
  438. class RPCClient(SocketIO):
  439. debugging = False
  440. location = "#C" # Client
  441. nextseq = 1 # Requests coming from the client are odd numbered
  442. def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
  443. self.listening_sock = socket.socket(family, type)
  444. self.listening_sock.bind(address)
  445. self.listening_sock.listen(1)
  446. def accept(self):
  447. working_sock, address = self.listening_sock.accept()
  448. if self.debugging:
  449. print>>sys.__stderr__, "****** Connection request from ", address
  450. if address[0] == LOCALHOST:
  451. SocketIO.__init__(self, working_sock)
  452. else:
  453. print>>sys.__stderr__, "** Invalid host: ", address
  454. raise socket.error
  455. def get_remote_proxy(self, oid):
  456. return RPCProxy(self, oid)
  457. class RPCProxy(object):
  458. __methods = None
  459. __attributes = None
  460. def __init__(self, sockio, oid):
  461. self.sockio = sockio
  462. self.oid = oid
  463. def __getattr__(self, name):
  464. if self.__methods is None:
  465. self.__getmethods()
  466. if self.__methods.get(name):
  467. return MethodProxy(self.sockio, self.oid, name)
  468. if self.__attributes is None:
  469. self.__getattributes()
  470. if name in self.__attributes:
  471. value = self.sockio.remotecall(self.oid, '__getattribute__',
  472. (name,), {})
  473. return value
  474. else:
  475. raise AttributeError, name
  476. def __getattributes(self):
  477. self.__attributes = self.sockio.remotecall(self.oid,
  478. "__attributes__", (), {})
  479. def __getmethods(self):
  480. self.__methods = self.sockio.remotecall(self.oid,
  481. "__methods__", (), {})
  482. def _getmethods(obj, methods):
  483. # Helper to get a list of methods from an object
  484. # Adds names to dictionary argument 'methods'
  485. for name in dir(obj):
  486. attr = getattr(obj, name)
  487. if hasattr(attr, '__call__'):
  488. methods[name] = 1
  489. if type(obj) == types.InstanceType:
  490. _getmethods(obj.__class__, methods)
  491. if type(obj) == types.ClassType:
  492. for super in obj.__bases__:
  493. _getmethods(super, methods)
  494. def _getattributes(obj, attributes):
  495. for name in dir(obj):
  496. attr = getattr(obj, name)
  497. if not hasattr(attr, '__call__'):
  498. attributes[name] = 1
  499. class MethodProxy(object):
  500. def __init__(self, sockio, oid, name):
  501. self.sockio = sockio
  502. self.oid = oid
  503. self.name = name
  504. def __call__(self, *args, **kwargs):
  505. value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
  506. return value
  507. # XXX KBK 09Sep03 We need a proper unit test for this module. Previously
  508. # existing test code was removed at Rev 1.27 (r34098).