run.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import sys
  2. import linecache
  3. import time
  4. import socket
  5. import traceback
  6. import thread
  7. import threading
  8. import Queue
  9. from idlelib import CallTips
  10. from idlelib import AutoComplete
  11. from idlelib import RemoteDebugger
  12. from idlelib import RemoteObjectBrowser
  13. from idlelib import StackViewer
  14. from idlelib import rpc
  15. from idlelib import PyShell
  16. from idlelib import IOBinding
  17. import __main__
  18. LOCALHOST = '127.0.0.1'
  19. import warnings
  20. def idle_showwarning_subproc(
  21. message, category, filename, lineno, file=None, line=None):
  22. """Show Idle-format warning after replacing warnings.showwarning.
  23. The only difference is the formatter called.
  24. """
  25. if file is None:
  26. file = sys.stderr
  27. try:
  28. file.write(PyShell.idle_formatwarning(
  29. message, category, filename, lineno, line))
  30. except IOError:
  31. pass # the file (probably stderr) is invalid - this warning gets lost.
  32. _warnings_showwarning = None
  33. def capture_warnings(capture):
  34. "Replace warning.showwarning with idle_showwarning_subproc, or reverse."
  35. global _warnings_showwarning
  36. if capture:
  37. if _warnings_showwarning is None:
  38. _warnings_showwarning = warnings.showwarning
  39. warnings.showwarning = idle_showwarning_subproc
  40. else:
  41. if _warnings_showwarning is not None:
  42. warnings.showwarning = _warnings_showwarning
  43. _warnings_showwarning = None
  44. capture_warnings(True)
  45. # Thread shared globals: Establish a queue between a subthread (which handles
  46. # the socket) and the main thread (which runs user code), plus global
  47. # completion, exit and interruptable (the main thread) flags:
  48. exit_now = False
  49. quitting = False
  50. interruptable = False
  51. def main(del_exitfunc=False):
  52. """Start the Python execution server in a subprocess
  53. In the Python subprocess, RPCServer is instantiated with handlerclass
  54. MyHandler, which inherits register/unregister methods from RPCHandler via
  55. the mix-in class SocketIO.
  56. When the RPCServer 'server' is instantiated, the TCPServer initialization
  57. creates an instance of run.MyHandler and calls its handle() method.
  58. handle() instantiates a run.Executive object, passing it a reference to the
  59. MyHandler object. That reference is saved as attribute rpchandler of the
  60. Executive instance. The Executive methods have access to the reference and
  61. can pass it on to entities that they command
  62. (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
  63. call MyHandler(SocketIO) register/unregister methods via the reference to
  64. register and unregister themselves.
  65. """
  66. global exit_now
  67. global quitting
  68. global no_exitfunc
  69. no_exitfunc = del_exitfunc
  70. #time.sleep(15) # test subprocess not responding
  71. try:
  72. assert(len(sys.argv) > 1)
  73. port = int(sys.argv[-1])
  74. except:
  75. print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv."
  76. return
  77. capture_warnings(True)
  78. sys.argv[:] = [""]
  79. sockthread = threading.Thread(target=manage_socket,
  80. name='SockThread',
  81. args=((LOCALHOST, port),))
  82. sockthread.setDaemon(True)
  83. sockthread.start()
  84. while 1:
  85. try:
  86. if exit_now:
  87. try:
  88. exit()
  89. except KeyboardInterrupt:
  90. # exiting but got an extra KBI? Try again!
  91. continue
  92. try:
  93. seq, request = rpc.request_queue.get(block=True, timeout=0.05)
  94. except Queue.Empty:
  95. continue
  96. method, args, kwargs = request
  97. ret = method(*args, **kwargs)
  98. rpc.response_queue.put((seq, ret))
  99. except KeyboardInterrupt:
  100. if quitting:
  101. exit_now = True
  102. continue
  103. except SystemExit:
  104. capture_warnings(False)
  105. raise
  106. except:
  107. type, value, tb = sys.exc_info()
  108. try:
  109. print_exception()
  110. rpc.response_queue.put((seq, None))
  111. except:
  112. # Link didn't work, print same exception to __stderr__
  113. traceback.print_exception(type, value, tb, file=sys.__stderr__)
  114. exit()
  115. else:
  116. continue
  117. def manage_socket(address):
  118. for i in range(3):
  119. time.sleep(i)
  120. try:
  121. server = MyRPCServer(address, MyHandler)
  122. break
  123. except socket.error as err:
  124. print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
  125. + err.args[1] + ", retrying...."
  126. else:
  127. print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
  128. "IDLE GUI failed, exiting."
  129. show_socket_error(err, address)
  130. global exit_now
  131. exit_now = True
  132. return
  133. server.handle_request() # A single request only
  134. def show_socket_error(err, address):
  135. import Tkinter
  136. import tkMessageBox
  137. root = Tkinter.Tk()
  138. root.withdraw()
  139. if err.args[0] == 61: # connection refused
  140. msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
  141. "to your personal firewall configuration. It is safe to "\
  142. "allow this internal connection because no data is visible on "\
  143. "external ports." % address
  144. tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
  145. else:
  146. tkMessageBox.showerror("IDLE Subprocess Error",
  147. "Socket Error: %s" % err.args[1], parent=root)
  148. root.destroy()
  149. def print_exception():
  150. import linecache
  151. linecache.checkcache()
  152. flush_stdout()
  153. efile = sys.stderr
  154. typ, val, tb = excinfo = sys.exc_info()
  155. sys.last_type, sys.last_value, sys.last_traceback = excinfo
  156. tbe = traceback.extract_tb(tb)
  157. print>>efile, '\nTraceback (most recent call last):'
  158. exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
  159. "RemoteDebugger.py", "bdb.py")
  160. cleanup_traceback(tbe, exclude)
  161. traceback.print_list(tbe, file=efile)
  162. lines = traceback.format_exception_only(typ, val)
  163. for line in lines:
  164. print>>efile, line,
  165. def cleanup_traceback(tb, exclude):
  166. "Remove excluded traces from beginning/end of tb; get cached lines"
  167. orig_tb = tb[:]
  168. while tb:
  169. for rpcfile in exclude:
  170. if tb[0][0].count(rpcfile):
  171. break # found an exclude, break for: and delete tb[0]
  172. else:
  173. break # no excludes, have left RPC code, break while:
  174. del tb[0]
  175. while tb:
  176. for rpcfile in exclude:
  177. if tb[-1][0].count(rpcfile):
  178. break
  179. else:
  180. break
  181. del tb[-1]
  182. if len(tb) == 0:
  183. # exception was in IDLE internals, don't prune!
  184. tb[:] = orig_tb[:]
  185. print>>sys.stderr, "** IDLE Internal Exception: "
  186. rpchandler = rpc.objecttable['exec'].rpchandler
  187. for i in range(len(tb)):
  188. fn, ln, nm, line = tb[i]
  189. if nm == '?':
  190. nm = "-toplevel-"
  191. if fn.startswith("<pyshell#") and IOBinding.encoding != 'utf-8':
  192. ln -= 1 # correction for coding cookie
  193. if not line and fn.startswith("<pyshell#"):
  194. line = rpchandler.remotecall('linecache', 'getline',
  195. (fn, ln), {})
  196. tb[i] = fn, ln, nm, line
  197. def flush_stdout():
  198. try:
  199. if sys.stdout.softspace:
  200. sys.stdout.softspace = 0
  201. sys.stdout.write("\n")
  202. except (AttributeError, EOFError):
  203. pass
  204. def exit():
  205. """Exit subprocess, possibly after first deleting sys.exitfunc
  206. If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
  207. sys.exitfunc will be removed before exiting. (VPython support)
  208. """
  209. if no_exitfunc:
  210. try:
  211. del sys.exitfunc
  212. except AttributeError:
  213. pass
  214. capture_warnings(False)
  215. sys.exit(0)
  216. class MyRPCServer(rpc.RPCServer):
  217. def handle_error(self, request, client_address):
  218. """Override RPCServer method for IDLE
  219. Interrupt the MainThread and exit server if link is dropped.
  220. """
  221. global quitting
  222. try:
  223. raise
  224. except SystemExit:
  225. raise
  226. except EOFError:
  227. global exit_now
  228. exit_now = True
  229. thread.interrupt_main()
  230. except:
  231. erf = sys.__stderr__
  232. print>>erf, '\n' + '-'*40
  233. print>>erf, 'Unhandled server exception!'
  234. print>>erf, 'Thread: %s' % threading.currentThread().getName()
  235. print>>erf, 'Client Address: ', client_address
  236. print>>erf, 'Request: ', repr(request)
  237. traceback.print_exc(file=erf)
  238. print>>erf, '\n*** Unrecoverable, server exiting!'
  239. print>>erf, '-'*40
  240. quitting = True
  241. thread.interrupt_main()
  242. class MyHandler(rpc.RPCHandler):
  243. def handle(self):
  244. """Override base method"""
  245. executive = Executive(self)
  246. self.register("exec", executive)
  247. self.console = self.get_remote_proxy("console")
  248. sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
  249. IOBinding.encoding)
  250. sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
  251. IOBinding.encoding)
  252. sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
  253. IOBinding.encoding)
  254. # Keep a reference to stdin so that it won't try to exit IDLE if
  255. # sys.stdin gets changed from within IDLE's shell. See issue17838.
  256. self._keep_stdin = sys.stdin
  257. self.interp = self.get_remote_proxy("interp")
  258. rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
  259. def exithook(self):
  260. "override SocketIO method - wait for MainThread to shut us down"
  261. time.sleep(10)
  262. def EOFhook(self):
  263. "Override SocketIO method - terminate wait on callback and exit thread"
  264. global quitting
  265. quitting = True
  266. thread.interrupt_main()
  267. def decode_interrupthook(self):
  268. "interrupt awakened thread"
  269. global quitting
  270. quitting = True
  271. thread.interrupt_main()
  272. class Executive(object):
  273. def __init__(self, rpchandler):
  274. self.rpchandler = rpchandler
  275. self.locals = __main__.__dict__
  276. self.calltip = CallTips.CallTips()
  277. self.autocomplete = AutoComplete.AutoComplete()
  278. def runcode(self, code):
  279. global interruptable
  280. try:
  281. self.usr_exc_info = None
  282. interruptable = True
  283. try:
  284. exec code in self.locals
  285. finally:
  286. interruptable = False
  287. except SystemExit:
  288. # Scripts that raise SystemExit should just
  289. # return to the interactive prompt
  290. pass
  291. except:
  292. self.usr_exc_info = sys.exc_info()
  293. if quitting:
  294. exit()
  295. print_exception()
  296. jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
  297. if jit:
  298. self.rpchandler.interp.open_remote_stack_viewer()
  299. else:
  300. flush_stdout()
  301. def interrupt_the_server(self):
  302. if interruptable:
  303. thread.interrupt_main()
  304. def start_the_debugger(self, gui_adap_oid):
  305. return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
  306. def stop_the_debugger(self, idb_adap_oid):
  307. "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
  308. self.rpchandler.unregister(idb_adap_oid)
  309. def get_the_calltip(self, name):
  310. return self.calltip.fetch_tip(name)
  311. def get_the_completion_list(self, what, mode):
  312. return self.autocomplete.fetch_completions(what, mode)
  313. def stackviewer(self, flist_oid=None):
  314. if self.usr_exc_info:
  315. typ, val, tb = self.usr_exc_info
  316. else:
  317. return None
  318. flist = None
  319. if flist_oid is not None:
  320. flist = self.rpchandler.get_remote_proxy(flist_oid)
  321. while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
  322. tb = tb.tb_next
  323. sys.last_type = typ
  324. sys.last_value = val
  325. item = StackViewer.StackTreeItem(flist, tb)
  326. return RemoteObjectBrowser.remote_object_tree_item(item)
  327. capture_warnings(False) # Make sure turned off; see issue 18081