run.py 10.8 KB
Newer Older
Chui Tey's avatar
Chui Tey committed
1
import sys
2
import os
3
import linecache
4 5
import time
import socket
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
6
import traceback
7
import thread
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
8 9
import threading
import Queue
10 11

import CallTips
12 13
import AutoComplete

14 15 16
import RemoteDebugger
import RemoteObjectBrowser
import StackViewer
Chui Tey's avatar
Chui Tey committed
17 18
import rpc

19 20
import __main__

21 22
LOCALHOST = '127.0.0.1'

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
try:
    import warnings
except ImportError:
    pass
else:
    def idle_formatwarning_subproc(message, category, filename, lineno):
        """Format warnings the IDLE way"""
        s = "\nWarning (from warnings module):\n"
        s += '  File \"%s\", line %s\n' % (filename, lineno)
        line = linecache.getline(filename, lineno).strip()
        if line:
            s += "    %s\n" % line
        s += "%s: %s\n" % (category.__name__, message)
        return s
    warnings.formatwarning = idle_formatwarning_subproc

Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
39 40
# Thread shared globals: Establish a queue between a subthread (which handles
# the socket) and the main thread (which runs user code), plus global
41
# completion, exit and interruptable (the main thread) flags:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
42

Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
43 44
exit_now = False
quitting = False
45
interruptable = False
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
46

47
def main(del_exitfunc=False):
48 49
    """Start the Python execution server in a subprocess

50 51 52
    In the Python subprocess, RPCServer is instantiated with handlerclass
    MyHandler, which inherits register/unregister methods from RPCHandler via
    the mix-in class SocketIO.
53

Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
54
    When the RPCServer 'server' is instantiated, the TCPServer initialization
55 56 57 58 59 60 61 62
    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.
63 64

    """
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
65 66
    global exit_now
    global quitting
67 68
    global no_exitfunc
    no_exitfunc = del_exitfunc
Chui Tey's avatar
Chui Tey committed
69
    port = 8833
70
    #time.sleep(15) # test subprocess not responding
Chui Tey's avatar
Chui Tey committed
71 72 73
    if sys.argv[1:]:
        port = int(sys.argv[1])
    sys.argv[:] = [""]
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
74 75
    sockthread = threading.Thread(target=manage_socket,
                                  name='SockThread',
76
                                  args=((LOCALHOST, port),))
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
77 78 79 80
    sockthread.setDaemon(True)
    sockthread.start()
    while 1:
        try:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
81 82
            if exit_now:
                try:
83
                    exit()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
84 85 86
                except KeyboardInterrupt:
                    # exiting but got an extra KBI? Try again!
                    continue
87
            try:
88
                seq, request = rpc.request_queue.get(block=True, timeout=0.05)
89 90 91 92 93
            except Queue.Empty:
                continue
            method, args, kwargs = request
            ret = method(*args, **kwargs)
            rpc.response_queue.put((seq, ret))
94
        except KeyboardInterrupt:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
95 96
            if quitting:
                exit_now = True
97
            continue
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
98 99
        except SystemExit:
            raise
100
        except:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
101
            type, value, tb = sys.exc_info()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
102 103 104 105
            try:
                print_exception()
                rpc.response_queue.put((seq, None))
            except:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
106 107
                # Link didn't work, print same exception to __stderr__
                traceback.print_exception(type, value, tb, file=sys.__stderr__)
108
                exit()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
109 110
            else:
                continue
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
111 112

def manage_socket(address):
113
    for i in range(3):
114 115
        time.sleep(i)
        try:
116
            server = MyRPCServer(address, MyHandler)
117 118
            break
        except socket.error, err:
119 120
            print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
                                        + err[1] + ", retrying...."
121
    else:
122 123 124
        print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
                               "IDLE GUI failed, exiting."
        show_socket_error(err, address)
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
125 126
        global exit_now
        exit_now = True
127
        return
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
128 129
    server.handle_request() # A single request only

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
def show_socket_error(err, address):
    import Tkinter
    import tkMessageBox
    root = Tkinter.Tk()
    root.withdraw()
    if err[0] == 61: # connection refused
        msg = "IDLE's subprocess can't connect to %s:%d.  This may be due "\
              "to your personal firewall configuration.  It is safe to "\
              "allow this internal connection because no data is visible on "\
              "external ports." % address
        tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
    else:
        tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1])
    root.destroy()

145
def print_exception():
146 147
    import linecache
    linecache.checkcache()
148 149
    flush_stdout()
    efile = sys.stderr
150 151
    typ, val, tb = excinfo = sys.exc_info()
    sys.last_type, sys.last_value, sys.last_traceback = excinfo
152
    tbe = traceback.extract_tb(tb)
153
    print>>efile, '\nTraceback (most recent call last):'
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
    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

201 202 203 204 205 206 207 208 209 210
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)
Chui Tey's avatar
Chui Tey committed
211

212 213 214 215 216 217 218 219
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.

        """
220
        global quitting
221 222 223 224 225
        try:
            raise
        except SystemExit:
            raise
        except EOFError:
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
226 227
            global exit_now
            exit_now = True
228
            thread.interrupt_main()
229 230 231 232 233 234 235 236 237 238
        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
239 240
            quitting = True
            thread.interrupt_main()
241 242


Chui Tey's avatar
Chui Tey committed
243 244 245
class MyHandler(rpc.RPCHandler):

    def handle(self):
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
246
        """Override base method"""
Chui Tey's avatar
Chui Tey committed
247 248
        executive = Executive(self)
        self.register("exec", executive)
249
        sys.stdin = self.console = self.get_remote_proxy("stdin")
Chui Tey's avatar
Chui Tey committed
250 251
        sys.stdout = self.get_remote_proxy("stdout")
        sys.stderr = self.get_remote_proxy("stderr")
252 253 254
        import IOBinding
        sys.stdin.encoding = sys.stdout.encoding = \
                             sys.stderr.encoding = IOBinding.encoding
255
        self.interp = self.get_remote_proxy("interp")
256 257 258 259
        rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)

    def exithook(self):
        "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
260
        time.sleep(10)
261 262 263

    def EOFhook(self):
        "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
264 265
        global quitting
        quitting = True
266
        thread.interrupt_main()
267 268 269

    def decode_interrupthook(self):
        "interrupt awakened thread"
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
270 271
        global quitting
        quitting = True
272
        thread.interrupt_main()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
273

Chui Tey's avatar
Chui Tey committed
274

275
class Executive(object):
Chui Tey's avatar
Chui Tey committed
276 277

    def __init__(self, rpchandler):
278
        self.rpchandler = rpchandler
279
        self.locals = __main__.__dict__
280
        self.calltip = CallTips.CallTips()
281
        self.autocomplete = AutoComplete.AutoComplete()
Chui Tey's avatar
Chui Tey committed
282 283

    def runcode(self, code):
284
        global interruptable
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
285
        try:
286
            self.usr_exc_info = None
287 288 289 290 291
            interruptable = True
            try:
                exec code in self.locals
            finally:
                interruptable = False
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
292
        except:
293
            self.usr_exc_info = sys.exc_info()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
294
            if quitting:
295
                exit()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
296 297
            # even print a user code SystemExit exception, continue
            print_exception()
298 299 300
            jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
            if jit:
                self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
301
        else:
302
            flush_stdout()
Chui Tey's avatar
Chui Tey committed
303

Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
304
    def interrupt_the_server(self):
305 306
        if interruptable:
            thread.interrupt_main()
Kurt B. Kaiser's avatar
Kurt B. Kaiser committed
307

308
    def start_the_debugger(self, gui_adap_oid):
309 310 311 312 313
        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)
Chui Tey's avatar
Chui Tey committed
314

315 316 317
    def get_the_calltip(self, name):
        return self.calltip.fetch_tip(name)

318 319 320
    def get_the_completion_list(self, what, mode):
        return self.autocomplete.fetch_completions(what, mode)

Chui Tey's avatar
Chui Tey committed
321
    def stackviewer(self, flist_oid=None):
322 323 324
        if self.usr_exc_info:
            typ, val, tb = self.usr_exc_info
        else:
Chui Tey's avatar
Chui Tey committed
325 326 327
            return None
        flist = None
        if flist_oid is not None:
328
            flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey's avatar
Chui Tey committed
329 330
        while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
            tb = tb.tb_next
331 332
        sys.last_type = typ
        sys.last_value = val
Chui Tey's avatar
Chui Tey committed
333 334
        item = StackViewer.StackTreeItem(flist, tb)
        return RemoteObjectBrowser.remote_object_tree_item(item)