1441 lines · 50.0 KB · Python
RawDownload
#! /usr/bin/env python import osimport os.pathimport sysimport stringimport getoptimport reimport socketimport timeimport threadingimport tracebackimport typesimport macosxSupport import linecachefrom code import InteractiveInterpreter try:    from Tkinter import *except ImportError:    print>>sys.__stderr__, "** IDLE can't import Tkinter.  " \                           "Your Python may not be configured for Tk. **"    sys.exit(1)import tkMessageBox from EditorWindow import EditorWindow, fixwordbreaksfrom FileList import FileListfrom ColorDelegator import ColorDelegatorfrom UndoDelegator import UndoDelegatorfrom OutputWindow import OutputWindowfrom configHandler import idleConfimport idlever import rpcimport Debuggerimport RemoteDebugger IDENTCHARS = string.ascii_letters + string.digits + "_"LOCALHOST = '127.0.0.1' try:    from signal import SIGTERMexcept ImportError:    SIGTERM = 15 # Override warnings module to write to warning_stream.  Initialize to send IDLE# internal warnings to the console.  ScriptBinding.check_syntax() will# temporarily redirect the stream to the shell window to display warnings when# checking user's code.global warning_streamwarning_stream = sys.__stderr__try:    import warningsexcept ImportError:    passelse:    def idle_showwarning(message, category, filename, lineno):        file = warning_stream        try:            file.write(warnings.formatwarning(message, category, filename, lineno))        except IOError:            pass  ## file (probably __stderr__) is invalid, warning dropped.    warnings.showwarning = idle_showwarning    def idle_formatwarning(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 def extended_linecache_checkcache(filename=None,                                  orig_checkcache=linecache.checkcache):    """Extend linecache.checkcache to preserve the <pyshell#...> entries     Rather than repeating the linecache code, patch it to save the    <pyshell#...> entries, call the original linecache.checkcache()    (which destroys them), and then restore the saved entries.     orig_checkcache is bound at definition time to the original    method, allowing it to be patched.     """    cache = linecache.cache    save = {}    for filename in cache.keys():        if filename[:1] + filename[-1:] == '<>':            save[filename] = cache[filename]    orig_checkcache()    cache.update(save) # Patch linecache.checkcache():linecache.checkcache = extended_linecache_checkcache  class PyShellEditorWindow(EditorWindow):    "Regular text edit window in IDLE, supports breakpoints"     def __init__(self, *args):        self.breakpoints = []        EditorWindow.__init__(self, *args)        self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)        self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)        self.text.bind("<<open-python-shell>>", self.flist.open_shell)         self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),                                           'breakpoints.lst')        # whenever a file is changed, restore breakpoints        if self.io.filename: self.restore_file_breaks()        def filename_changed_hook(old_hook=self.io.filename_change_hook,                                  self=self):            self.restore_file_breaks()            old_hook()        self.io.set_filename_change_hook(filename_changed_hook)     rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),                   ("Clear Breakpoint", "<<clear-breakpoint-here>>")]     def set_breakpoint(self, lineno):        text = self.text        filename = self.io.filename        text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))        try:            i = self.breakpoints.index(lineno)        except ValueError:  # only add if missing, i.e. do once            self.breakpoints.append(lineno)        try:    # update the subprocess debugger            debug = self.flist.pyshell.interp.debugger            debug.set_breakpoint_here(filename, lineno)        except: # but debugger may not be active right now....            pass     def set_breakpoint_here(self, event=None):        text = self.text        filename = self.io.filename        if not filename:            text.bell()            return        lineno = int(float(text.index("insert")))        self.set_breakpoint(lineno)     def clear_breakpoint_here(self, event=None):        text = self.text        filename = self.io.filename        if not filename:            text.bell()            return        lineno = int(float(text.index("insert")))        try:            self.breakpoints.remove(lineno)        except:            pass        text.tag_remove("BREAK", "insert linestart",\                        "insert lineend +1char")        try:            debug = self.flist.pyshell.interp.debugger            debug.clear_breakpoint_here(filename, lineno)        except:            pass     def clear_file_breaks(self):        if self.breakpoints:            text = self.text            filename = self.io.filename            if not filename:                text.bell()                return            self.breakpoints = []            text.tag_remove("BREAK", "1.0", END)            try:                debug = self.flist.pyshell.interp.debugger                debug.clear_file_breaks(filename)            except:                pass     def store_file_breaks(self):        "Save breakpoints when file is saved"        # XXX 13 Dec 2002 KBK Currently the file must be saved before it can        #     be run.  The breaks are saved at that time.  If we introduce        #     a temporary file save feature the save breaks functionality        #     needs to be re-verified, since the breaks at the time the        #     temp file is created may differ from the breaks at the last        #     permanent save of the file.  Currently, a break introduced        #     after a save will be effective, but not persistent.        #     This is necessary to keep the saved breaks synched with the        #     saved file.        #        #     Breakpoints are set as tagged ranges in the text.  Certain        #     kinds of edits cause these ranges to be deleted: Inserting        #     or deleting a line just before a breakpoint, and certain        #     deletions prior to a breakpoint.  These issues need to be        #     investigated and understood.  It's not clear if they are        #     Tk issues or IDLE issues, or whether they can actually        #     be fixed.  Since a modified file has to be saved before it is        #     run, and since self.breakpoints (from which the subprocess        #     debugger is loaded) is updated during the save, the visible        #     breaks stay synched with the subprocess even if one of these        #     unexpected breakpoint deletions occurs.        breaks = self.breakpoints        filename = self.io.filename        try:            lines = open(self.breakpointPath,"r").readlines()        except IOError:            lines = []        new_file = open(self.breakpointPath,"w")        for line in lines:            if not line.startswith(filename + '='):                new_file.write(line)        self.update_breakpoints()        breaks = self.breakpoints        if breaks:            new_file.write(filename + '=' + str(breaks) + '\n')        new_file.close()     def restore_file_breaks(self):        self.text.update()   # this enables setting "BREAK" tags to be visible        filename = self.io.filename        if filename is None:            return        if os.path.isfile(self.breakpointPath):            lines = open(self.breakpointPath,"r").readlines()            for line in lines:                if line.startswith(filename + '='):                    breakpoint_linenumbers = eval(line[len(filename)+1:])                    for breakpoint_linenumber in breakpoint_linenumbers:                        self.set_breakpoint(breakpoint_linenumber)     def update_breakpoints(self):        "Retrieves all the breakpoints in the current window"        text = self.text        ranges = text.tag_ranges("BREAK")        linenumber_list = self.ranges_to_linenumbers(ranges)        self.breakpoints = linenumber_list     def ranges_to_linenumbers(self, ranges):        lines = []        for index in range(0, len(ranges), 2):            lineno = int(float(ranges[index]))            end = int(float(ranges[index+1]))            while lineno < end:                lines.append(lineno)                lineno += 1        return lines # XXX 13 Dec 2002 KBK Not used currently#    def saved_change_hook(self):#        "Extend base method - clear breaks if module is modified"#        if not self.get_saved():#            self.clear_file_breaks()#        EditorWindow.saved_change_hook(self)     def _close(self):        "Extend base method - clear breaks when module is closed"        self.clear_file_breaks()        EditorWindow._close(self)  class PyShellFileList(FileList):    "Extend base class: IDLE supports a shell and breakpoints"     # override FileList's class variable, instances return PyShellEditorWindow    # instead of EditorWindow when new edit windows are created.    EditorWindow = PyShellEditorWindow     pyshell = None     def open_shell(self, event=None):        if self.pyshell:            self.pyshell.top.wakeup()        else:            self.pyshell = PyShell(self)            if self.pyshell:                if not self.pyshell.begin():                    return None        return self.pyshell  class ModifiedColorDelegator(ColorDelegator):    "Extend base class: colorizer for the shell window itself"     def __init__(self):        ColorDelegator.__init__(self)        self.LoadTagDefs()     def recolorize_main(self):        self.tag_remove("TODO", "1.0", "iomark")        self.tag_add("SYNC", "1.0", "iomark")        ColorDelegator.recolorize_main(self)     def LoadTagDefs(self):        ColorDelegator.LoadTagDefs(self)        theme = idleConf.GetOption('main','Theme','name')        self.tagdefs.update({            "stdin": {'background':None,'foreground':None},            "stdout": idleConf.GetHighlight(theme, "stdout"),            "stderr": idleConf.GetHighlight(theme, "stderr"),            "console": idleConf.GetHighlight(theme, "console"),            None: idleConf.GetHighlight(theme, "normal"),        }) class ModifiedUndoDelegator(UndoDelegator):    "Extend base class: forbid insert/delete before the I/O mark"     def insert(self, index, chars, tags=None):        try:            if self.delegate.compare(index, "<", "iomark"):                self.delegate.bell()                return        except TclError:            pass        UndoDelegator.insert(self, index, chars, tags)     def delete(self, index1, index2=None):        try:            if self.delegate.compare(index1, "<", "iomark"):                self.delegate.bell()                return        except TclError:            pass        UndoDelegator.delete(self, index1, index2)  class MyRPCClient(rpc.RPCClient):     def handle_EOF(self):        "Override the base class - just re-raise EOFError"        raise EOFError  class ModifiedInterpreter(InteractiveInterpreter):     def __init__(self, tkconsole):        self.tkconsole = tkconsole        locals = sys.modules['__main__'].__dict__        InteractiveInterpreter.__init__(self, locals=locals)        self.save_warnings_filters = None        self.restarting = False        self.subprocess_arglist = self.build_subprocess_arglist()     port = 8833    rpcclt = None    rpcpid = None     def spawn_subprocess(self):        args = self.subprocess_arglist        self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)     def build_subprocess_arglist(self):        w = ['-W' + s for s in sys.warnoptions]        if 1/2 > 0: # account for new division            w.append('-Qnew')        # Maybe IDLE is installed and is being accessed via sys.path,        # or maybe it's not installed and the idle.py script is being        # run from the IDLE source directory.        del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',                                       default=False, type='bool')        if __name__ == 'idlelib.PyShell':            command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)        else:            command = "__import__('run').main(%r)" % (del_exitf,)        if sys.platform[:3] == 'win' and ' ' in sys.executable:            # handle embedded space in path by quoting the argument            decorated_exec = '"%s"' % sys.executable        else:            decorated_exec = sys.executable        return [decorated_exec] + w + ["-c", command, str(self.port)]     def start_subprocess(self):        # spawning first avoids passing a listening socket to the subprocess        self.spawn_subprocess()        #time.sleep(20) # test to simulate GUI not accepting connection        addr = (LOCALHOST, self.port)        # Idle starts listening for connection on localhost        for i in range(3):            time.sleep(i)            try:                self.rpcclt = MyRPCClient(addr)                break            except socket.error, err:                pass        else:            self.display_port_binding_error()            return None        # Accept the connection from the Python execution server        self.rpcclt.listening_sock.settimeout(10)        try:            self.rpcclt.accept()        except socket.timeout, err:            self.display_no_subprocess_error()            return None        self.rpcclt.register("stdin", self.tkconsole)        self.rpcclt.register("stdout", self.tkconsole.stdout)        self.rpcclt.register("stderr", self.tkconsole.stderr)        self.rpcclt.register("flist", self.tkconsole.flist)        self.rpcclt.register("linecache", linecache)        self.rpcclt.register("interp", self)        self.transfer_path()        self.poll_subprocess()        return self.rpcclt     def restart_subprocess(self):        if self.restarting:            return self.rpcclt        self.restarting = True        # close only the subprocess debugger        debug = self.getdebugger()        if debug:            try:                # Only close subprocess debugger, don't unregister gui_adap!                RemoteDebugger.close_subprocess_debugger(self.rpcclt)            except:                pass        # Kill subprocess, spawn a new one, accept connection.        self.rpcclt.close()        self.unix_terminate()        console = self.tkconsole        was_executing = console.executing        console.executing = False        self.spawn_subprocess()        try:            self.rpcclt.accept()        except socket.timeout, err:            self.display_no_subprocess_error()            return None        self.transfer_path()        # annotate restart in shell window and mark it        console.text.delete("iomark", "end-1c")        if was_executing:            console.write('\n')            console.showprompt()        halfbar = ((int(console.width) - 16) // 2) * '='        console.write(halfbar + ' RESTART ' + halfbar)        console.text.mark_set("restart", "end-1c")        console.text.mark_gravity("restart", "left")        console.showprompt()        # restart subprocess debugger        if debug:            # Restarted debugger connects to current instance of debug GUI            gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)            # reload remote debugger breakpoints for all PyShellEditWindows            debug.load_breakpoints()        self.restarting = False        return self.rpcclt     def __request_interrupt(self):        self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})     def interrupt_subprocess(self):        threading.Thread(target=self.__request_interrupt).start()     def kill_subprocess(self):        try:            self.rpcclt.close()        except AttributeError:  # no socket            pass        self.unix_terminate()        self.tkconsole.executing = False        self.rpcclt = None     def unix_terminate(self):        "UNIX: make sure subprocess is terminated and collect status"        if hasattr(os, 'kill'):            try:                os.kill(self.rpcpid, SIGTERM)            except OSError:                # process already terminated:                return            else:                try:                    os.waitpid(self.rpcpid, 0)                except OSError:                    return     def transfer_path(self):        self.runcommand("""if 1:        import sys as _sys        _sys.path = %r        del _sys        \n""" % (sys.path,))     active_seq = None     def poll_subprocess(self):        clt = self.rpcclt        if clt is None:            return        try:            response = clt.pollresponse(self.active_seq, wait=0.05)        except (EOFError, IOError, KeyboardInterrupt):            # lost connection or subprocess terminated itself, restart            # [the KBI is from rpc.SocketIO.handle_EOF()]            if self.tkconsole.closing:                return            response = None            self.restart_subprocess()        if response:

Lines 1–500 of 1441.