1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
import sys
import os
import time
import socket
import traceback
import threading
import Queue
import boolcheck
import CallTips
import RemoteDebugger
import RemoteObjectBrowser
import StackViewer
import rpc
import interrupt
import __main__
LOCALHOST = '127.0.0.1'
# Thread shared globals: Establish a queue between a subthread (which handles
# the socket) and the main thread (which runs user code), plus global
# completion and exit flags:
exit_now = False
quitting = False
def main(del_exitfunc=False):
"""Start the Python execution server in a subprocess
In the Python subprocess, RPCServer is instantiated with handlerclass
MyHandler, which inherits register/unregister methods from RPCHandler via
the mix-in class SocketIO.
When the RPCServer 'server' is instantiated, the TCPServer initialization
creates an instance of run.MyHandler and calls its handle() method.
handle() instantiates a run.Executive object, passing it a reference to the
MyHandler object. That reference is saved as attribute rpchandler of the
Executive instance. The Executive methods have access to the reference and
can pass it on to entities that they command
(e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
call MyHandler(SocketIO) register/unregister methods via the reference to
register and unregister themselves.
"""
global exit_now
global quitting
global no_exitfunc
no_exitfunc = del_exitfunc
port = 8833
if sys.argv[1:]:
port = int(sys.argv[1])
sys.argv[:] = [""]
sockthread = threading.Thread(target=manage_socket,
name='SockThread',
args=((LOCALHOST, port),))
sockthread.setDaemon(True)
sockthread.start()
while 1:
try:
if exit_now:
try:
exit()
except KeyboardInterrupt:
# exiting but got an extra KBI? Try again!
continue
try:
seq, request = rpc.request_queue.get(0)
except Queue.Empty:
time.sleep(0.05)
continue
method, args, kwargs = request
ret = method(*args, **kwargs)
rpc.response_queue.put((seq, ret))
except KeyboardInterrupt:
if quitting:
exit_now = True
continue
except SystemExit:
raise
except:
type, value, tb = sys.exc_info()
try:
print_exception()
rpc.response_queue.put((seq, None))
except:
# Link didn't work, print same exception to __stderr__
traceback.print_exception(type, value, tb, file=sys.__stderr__)
exit()
else:
continue
def manage_socket(address):
for i in range(6):
time.sleep(i)
try:
server = MyRPCServer(address, MyHandler)
break
except socket.error, err:
if i < 3:
print>>sys.__stderr__, ".. ",
else:
print>>sys.__stderr__,"\nPython subprocess socket error: "\
+ err[1] + ", retrying...."
else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
global exit_now
exit_now = True
return
server.handle_request() # A single request only
def print_exception():
flush_stdout()
efile = sys.stderr
typ, val, tb = sys.exc_info()
tbe = traceback.extract_tb(tb)
print >>efile, '\nTraceback (most recent call last):'
exclude = ("run.py", "rpc.py", "threading.py", "Queue.py",
"RemoteDebugger.py", "bdb.py")
cleanup_traceback(tbe, exclude)
traceback.print_list(tbe, file=efile)
lines = traceback.format_exception_only(typ, val)
for line in lines:
print>>efile, line,
def cleanup_traceback(tb, exclude):
"Remove excluded traces from beginning/end of tb; get cached lines"
orig_tb = tb[:]
while tb:
for rpcfile in exclude:
if tb[0][0].count(rpcfile):
break # found an exclude, break for: and delete tb[0]
else:
break # no excludes, have left RPC code, break while:
del tb[0]
while tb:
for rpcfile in exclude:
if tb[-1][0].count(rpcfile):
break
else:
break
del tb[-1]
if len(tb) == 0:
# exception was in IDLE internals, don't prune!
tb[:] = orig_tb[:]
print>>sys.stderr, "** IDLE Internal Exception: "
rpchandler = rpc.objecttable['exec'].rpchandler
for i in range(len(tb)):
fn, ln, nm, line = tb[i]
if nm == '?':
nm = "-toplevel-"
if not line and fn.startswith("<pyshell#"):
line = rpchandler.remotecall('linecache', 'getline',
(fn, ln), {})
tb[i] = fn, ln, nm, line
def flush_stdout():
try:
if sys.stdout.softspace:
sys.stdout.softspace = 0
sys.stdout.write("\n")
except (AttributeError, EOFError):
pass
def exit():
"""Exit subprocess, possibly after first deleting sys.exitfunc
If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
sys.exitfunc will be removed before exiting. (VPython support)
"""
if no_exitfunc:
del sys.exitfunc
sys.exit(0)
class MyRPCServer(rpc.RPCServer):
def handle_error(self, request, client_address):
"""Override RPCServer method for IDLE
Interrupt the MainThread and exit server if link is dropped.
"""
try:
raise
except SystemExit:
raise
except EOFError:
global exit_now
exit_now = True
interrupt.interrupt_main()
except:
erf = sys.__stderr__
print>>erf, '\n' + '-'*40
print>>erf, 'Unhandled server exception!'
print>>erf, 'Thread: %s' % threading.currentThread().getName()
print>>erf, 'Client Address: ', client_address
print>>erf, 'Request: ', repr(request)
traceback.print_exc(file=erf)
print>>erf, '\n*** Unrecoverable, server exiting!'
print>>erf, '-'*40
exit()
class MyHandler(rpc.RPCHandler):
def handle(self):
"""Override base method"""
executive = Executive(self)
self.register("exec", executive)
sys.stdin = self.console = self.get_remote_proxy("stdin")
sys.stdout = self.get_remote_proxy("stdout")
sys.stderr = self.get_remote_proxy("stderr")
self.interp = self.get_remote_proxy("interp")
rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
def exithook(self):
"override SocketIO method - wait for MainThread to shut us down"
time.sleep(10)
def EOFhook(self):
"Override SocketIO method - terminate wait on callback and exit thread"
global quitting
quitting = True
interrupt.interrupt_main()
def decode_interrupthook(self):
"interrupt awakened thread"
global quitting
quitting = True
interrupt.interrupt_main()
class Executive:
def __init__(self, rpchandler):
self.rpchandler = rpchandler
self.locals = __main__.__dict__
self.calltip = CallTips.CallTips()
def runcode(self, code):
try:
self.usr_exc_info = None
exec code in self.locals
except:
self.usr_exc_info = sys.exc_info()
if quitting:
exit()
# even print a user code SystemExit exception, continue
print_exception()
jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
if jit:
self.rpchandler.interp.open_remote_stack_viewer()
else:
flush_stdout()
def interrupt_the_server(self):
interrupt.interrupt_main()
def start_the_debugger(self, gui_adap_oid):
return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
def stop_the_debugger(self, idb_adap_oid):
"Unregister the Idb Adapter. Link objects and Idb then subject to GC"
self.rpchandler.unregister(idb_adap_oid)
def get_the_calltip(self, name):
return self.calltip.fetch_tip(name)
def stackviewer(self, flist_oid=None):
if self.usr_exc_info:
typ, val, tb = self.usr_exc_info
else:
return None
flist = None
if flist_oid is not None:
flist = self.rpchandler.get_remote_proxy(flist_oid)
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
tb = tb.tb_next
sys.last_type = typ
sys.last_value = val
item = StackViewer.StackTreeItem(flist, tb)
return RemoteObjectBrowser.remote_object_tree_item(item)
|