python-2.5.2/win32/Lib/idlelib/PyShell.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 #! /usr/bin/env python
       
     2 
       
     3 import os
       
     4 import os.path
       
     5 import sys
       
     6 import string
       
     7 import getopt
       
     8 import re
       
     9 import socket
       
    10 import time
       
    11 import threading
       
    12 import traceback
       
    13 import types
       
    14 import macosxSupport
       
    15 
       
    16 import linecache
       
    17 from code import InteractiveInterpreter
       
    18 
       
    19 try:
       
    20     from Tkinter import *
       
    21 except ImportError:
       
    22     print>>sys.__stderr__, "** IDLE can't import Tkinter.  " \
       
    23                            "Your Python may not be configured for Tk. **"
       
    24     sys.exit(1)
       
    25 import tkMessageBox
       
    26 
       
    27 from EditorWindow import EditorWindow, fixwordbreaks
       
    28 from FileList import FileList
       
    29 from ColorDelegator import ColorDelegator
       
    30 from UndoDelegator import UndoDelegator
       
    31 from OutputWindow import OutputWindow
       
    32 from configHandler import idleConf
       
    33 import idlever
       
    34 
       
    35 import rpc
       
    36 import Debugger
       
    37 import RemoteDebugger
       
    38 
       
    39 IDENTCHARS = string.ascii_letters + string.digits + "_"
       
    40 LOCALHOST = '127.0.0.1'
       
    41 
       
    42 try:
       
    43     from signal import SIGTERM
       
    44 except ImportError:
       
    45     SIGTERM = 15
       
    46 
       
    47 # Override warnings module to write to warning_stream.  Initialize to send IDLE
       
    48 # internal warnings to the console.  ScriptBinding.check_syntax() will
       
    49 # temporarily redirect the stream to the shell window to display warnings when
       
    50 # checking user's code.
       
    51 global warning_stream
       
    52 warning_stream = sys.__stderr__
       
    53 try:
       
    54     import warnings
       
    55 except ImportError:
       
    56     pass
       
    57 else:
       
    58     def idle_showwarning(message, category, filename, lineno):
       
    59         file = warning_stream
       
    60         try:
       
    61             file.write(warnings.formatwarning(message, category, filename, lineno))
       
    62         except IOError:
       
    63             pass  ## file (probably __stderr__) is invalid, warning dropped.
       
    64     warnings.showwarning = idle_showwarning
       
    65     def idle_formatwarning(message, category, filename, lineno):
       
    66         """Format warnings the IDLE way"""
       
    67         s = "\nWarning (from warnings module):\n"
       
    68         s += '  File \"%s\", line %s\n' % (filename, lineno)
       
    69         line = linecache.getline(filename, lineno).strip()
       
    70         if line:
       
    71             s += "    %s\n" % line
       
    72         s += "%s: %s\n>>> " % (category.__name__, message)
       
    73         return s
       
    74     warnings.formatwarning = idle_formatwarning
       
    75 
       
    76 def extended_linecache_checkcache(filename=None,
       
    77                                   orig_checkcache=linecache.checkcache):
       
    78     """Extend linecache.checkcache to preserve the <pyshell#...> entries
       
    79 
       
    80     Rather than repeating the linecache code, patch it to save the
       
    81     <pyshell#...> entries, call the original linecache.checkcache()
       
    82     (which destroys them), and then restore the saved entries.
       
    83 
       
    84     orig_checkcache is bound at definition time to the original
       
    85     method, allowing it to be patched.
       
    86 
       
    87     """
       
    88     cache = linecache.cache
       
    89     save = {}
       
    90     for filename in cache.keys():
       
    91         if filename[:1] + filename[-1:] == '<>':
       
    92             save[filename] = cache[filename]
       
    93     orig_checkcache()
       
    94     cache.update(save)
       
    95 
       
    96 # Patch linecache.checkcache():
       
    97 linecache.checkcache = extended_linecache_checkcache
       
    98 
       
    99 
       
   100 class PyShellEditorWindow(EditorWindow):
       
   101     "Regular text edit window in IDLE, supports breakpoints"
       
   102 
       
   103     def __init__(self, *args):
       
   104         self.breakpoints = []
       
   105         EditorWindow.__init__(self, *args)
       
   106         self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
       
   107         self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
       
   108         self.text.bind("<<open-python-shell>>", self.flist.open_shell)
       
   109 
       
   110         self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
       
   111                                            'breakpoints.lst')
       
   112         # whenever a file is changed, restore breakpoints
       
   113         if self.io.filename: self.restore_file_breaks()
       
   114         def filename_changed_hook(old_hook=self.io.filename_change_hook,
       
   115                                   self=self):
       
   116             self.restore_file_breaks()
       
   117             old_hook()
       
   118         self.io.set_filename_change_hook(filename_changed_hook)
       
   119 
       
   120     rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
       
   121                    ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
       
   122 
       
   123     def set_breakpoint(self, lineno):
       
   124         text = self.text
       
   125         filename = self.io.filename
       
   126         text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
       
   127         try:
       
   128             i = self.breakpoints.index(lineno)
       
   129         except ValueError:  # only add if missing, i.e. do once
       
   130             self.breakpoints.append(lineno)
       
   131         try:    # update the subprocess debugger
       
   132             debug = self.flist.pyshell.interp.debugger
       
   133             debug.set_breakpoint_here(filename, lineno)
       
   134         except: # but debugger may not be active right now....
       
   135             pass
       
   136 
       
   137     def set_breakpoint_here(self, event=None):
       
   138         text = self.text
       
   139         filename = self.io.filename
       
   140         if not filename:
       
   141             text.bell()
       
   142             return
       
   143         lineno = int(float(text.index("insert")))
       
   144         self.set_breakpoint(lineno)
       
   145 
       
   146     def clear_breakpoint_here(self, event=None):
       
   147         text = self.text
       
   148         filename = self.io.filename
       
   149         if not filename:
       
   150             text.bell()
       
   151             return
       
   152         lineno = int(float(text.index("insert")))
       
   153         try:
       
   154             self.breakpoints.remove(lineno)
       
   155         except:
       
   156             pass
       
   157         text.tag_remove("BREAK", "insert linestart",\
       
   158                         "insert lineend +1char")
       
   159         try:
       
   160             debug = self.flist.pyshell.interp.debugger
       
   161             debug.clear_breakpoint_here(filename, lineno)
       
   162         except:
       
   163             pass
       
   164 
       
   165     def clear_file_breaks(self):
       
   166         if self.breakpoints:
       
   167             text = self.text
       
   168             filename = self.io.filename
       
   169             if not filename:
       
   170                 text.bell()
       
   171                 return
       
   172             self.breakpoints = []
       
   173             text.tag_remove("BREAK", "1.0", END)
       
   174             try:
       
   175                 debug = self.flist.pyshell.interp.debugger
       
   176                 debug.clear_file_breaks(filename)
       
   177             except:
       
   178                 pass
       
   179 
       
   180     def store_file_breaks(self):
       
   181         "Save breakpoints when file is saved"
       
   182         # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
       
   183         #     be run.  The breaks are saved at that time.  If we introduce
       
   184         #     a temporary file save feature the save breaks functionality
       
   185         #     needs to be re-verified, since the breaks at the time the
       
   186         #     temp file is created may differ from the breaks at the last
       
   187         #     permanent save of the file.  Currently, a break introduced
       
   188         #     after a save will be effective, but not persistent.
       
   189         #     This is necessary to keep the saved breaks synched with the
       
   190         #     saved file.
       
   191         #
       
   192         #     Breakpoints are set as tagged ranges in the text.  Certain
       
   193         #     kinds of edits cause these ranges to be deleted: Inserting
       
   194         #     or deleting a line just before a breakpoint, and certain
       
   195         #     deletions prior to a breakpoint.  These issues need to be
       
   196         #     investigated and understood.  It's not clear if they are
       
   197         #     Tk issues or IDLE issues, or whether they can actually
       
   198         #     be fixed.  Since a modified file has to be saved before it is
       
   199         #     run, and since self.breakpoints (from which the subprocess
       
   200         #     debugger is loaded) is updated during the save, the visible
       
   201         #     breaks stay synched with the subprocess even if one of these
       
   202         #     unexpected breakpoint deletions occurs.
       
   203         breaks = self.breakpoints
       
   204         filename = self.io.filename
       
   205         try:
       
   206             lines = open(self.breakpointPath,"r").readlines()
       
   207         except IOError:
       
   208             lines = []
       
   209         new_file = open(self.breakpointPath,"w")
       
   210         for line in lines:
       
   211             if not line.startswith(filename + '='):
       
   212                 new_file.write(line)
       
   213         self.update_breakpoints()
       
   214         breaks = self.breakpoints
       
   215         if breaks:
       
   216             new_file.write(filename + '=' + str(breaks) + '\n')
       
   217         new_file.close()
       
   218 
       
   219     def restore_file_breaks(self):
       
   220         self.text.update()   # this enables setting "BREAK" tags to be visible
       
   221         filename = self.io.filename
       
   222         if filename is None:
       
   223             return
       
   224         if os.path.isfile(self.breakpointPath):
       
   225             lines = open(self.breakpointPath,"r").readlines()
       
   226             for line in lines:
       
   227                 if line.startswith(filename + '='):
       
   228                     breakpoint_linenumbers = eval(line[len(filename)+1:])
       
   229                     for breakpoint_linenumber in breakpoint_linenumbers:
       
   230                         self.set_breakpoint(breakpoint_linenumber)
       
   231 
       
   232     def update_breakpoints(self):
       
   233         "Retrieves all the breakpoints in the current window"
       
   234         text = self.text
       
   235         ranges = text.tag_ranges("BREAK")
       
   236         linenumber_list = self.ranges_to_linenumbers(ranges)
       
   237         self.breakpoints = linenumber_list
       
   238 
       
   239     def ranges_to_linenumbers(self, ranges):
       
   240         lines = []
       
   241         for index in range(0, len(ranges), 2):
       
   242             lineno = int(float(ranges[index]))
       
   243             end = int(float(ranges[index+1]))
       
   244             while lineno < end:
       
   245                 lines.append(lineno)
       
   246                 lineno += 1
       
   247         return lines
       
   248 
       
   249 # XXX 13 Dec 2002 KBK Not used currently
       
   250 #    def saved_change_hook(self):
       
   251 #        "Extend base method - clear breaks if module is modified"
       
   252 #        if not self.get_saved():
       
   253 #            self.clear_file_breaks()
       
   254 #        EditorWindow.saved_change_hook(self)
       
   255 
       
   256     def _close(self):
       
   257         "Extend base method - clear breaks when module is closed"
       
   258         self.clear_file_breaks()
       
   259         EditorWindow._close(self)
       
   260 
       
   261 
       
   262 class PyShellFileList(FileList):
       
   263     "Extend base class: IDLE supports a shell and breakpoints"
       
   264 
       
   265     # override FileList's class variable, instances return PyShellEditorWindow
       
   266     # instead of EditorWindow when new edit windows are created.
       
   267     EditorWindow = PyShellEditorWindow
       
   268 
       
   269     pyshell = None
       
   270 
       
   271     def open_shell(self, event=None):
       
   272         if self.pyshell:
       
   273             self.pyshell.top.wakeup()
       
   274         else:
       
   275             self.pyshell = PyShell(self)
       
   276             if self.pyshell:
       
   277                 if not self.pyshell.begin():
       
   278                     return None
       
   279         return self.pyshell
       
   280 
       
   281 
       
   282 class ModifiedColorDelegator(ColorDelegator):
       
   283     "Extend base class: colorizer for the shell window itself"
       
   284 
       
   285     def __init__(self):
       
   286         ColorDelegator.__init__(self)
       
   287         self.LoadTagDefs()
       
   288 
       
   289     def recolorize_main(self):
       
   290         self.tag_remove("TODO", "1.0", "iomark")
       
   291         self.tag_add("SYNC", "1.0", "iomark")
       
   292         ColorDelegator.recolorize_main(self)
       
   293 
       
   294     def LoadTagDefs(self):
       
   295         ColorDelegator.LoadTagDefs(self)
       
   296         theme = idleConf.GetOption('main','Theme','name')
       
   297         self.tagdefs.update({
       
   298             "stdin": {'background':None,'foreground':None},
       
   299             "stdout": idleConf.GetHighlight(theme, "stdout"),
       
   300             "stderr": idleConf.GetHighlight(theme, "stderr"),
       
   301             "console": idleConf.GetHighlight(theme, "console"),
       
   302             None: idleConf.GetHighlight(theme, "normal"),
       
   303         })
       
   304 
       
   305 class ModifiedUndoDelegator(UndoDelegator):
       
   306     "Extend base class: forbid insert/delete before the I/O mark"
       
   307 
       
   308     def insert(self, index, chars, tags=None):
       
   309         try:
       
   310             if self.delegate.compare(index, "<", "iomark"):
       
   311                 self.delegate.bell()
       
   312                 return
       
   313         except TclError:
       
   314             pass
       
   315         UndoDelegator.insert(self, index, chars, tags)
       
   316 
       
   317     def delete(self, index1, index2=None):
       
   318         try:
       
   319             if self.delegate.compare(index1, "<", "iomark"):
       
   320                 self.delegate.bell()
       
   321                 return
       
   322         except TclError:
       
   323             pass
       
   324         UndoDelegator.delete(self, index1, index2)
       
   325 
       
   326 
       
   327 class MyRPCClient(rpc.RPCClient):
       
   328 
       
   329     def handle_EOF(self):
       
   330         "Override the base class - just re-raise EOFError"
       
   331         raise EOFError
       
   332 
       
   333 
       
   334 class ModifiedInterpreter(InteractiveInterpreter):
       
   335 
       
   336     def __init__(self, tkconsole):
       
   337         self.tkconsole = tkconsole
       
   338         locals = sys.modules['__main__'].__dict__
       
   339         InteractiveInterpreter.__init__(self, locals=locals)
       
   340         self.save_warnings_filters = None
       
   341         self.restarting = False
       
   342         self.subprocess_arglist = self.build_subprocess_arglist()
       
   343 
       
   344     port = 8833
       
   345     rpcclt = None
       
   346     rpcpid = None
       
   347 
       
   348     def spawn_subprocess(self):
       
   349         args = self.subprocess_arglist
       
   350         self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)
       
   351 
       
   352     def build_subprocess_arglist(self):
       
   353         w = ['-W' + s for s in sys.warnoptions]
       
   354         if 1/2 > 0: # account for new division
       
   355             w.append('-Qnew')
       
   356         # Maybe IDLE is installed and is being accessed via sys.path,
       
   357         # or maybe it's not installed and the idle.py script is being
       
   358         # run from the IDLE source directory.
       
   359         del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
       
   360                                        default=False, type='bool')
       
   361         if __name__ == 'idlelib.PyShell':
       
   362             command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
       
   363         else:
       
   364             command = "__import__('run').main(%r)" % (del_exitf,)
       
   365         if sys.platform[:3] == 'win' and ' ' in sys.executable:
       
   366             # handle embedded space in path by quoting the argument
       
   367             decorated_exec = '"%s"' % sys.executable
       
   368         else:
       
   369             decorated_exec = sys.executable
       
   370         return [decorated_exec] + w + ["-c", command, str(self.port)]
       
   371 
       
   372     def start_subprocess(self):
       
   373         # spawning first avoids passing a listening socket to the subprocess
       
   374         self.spawn_subprocess()
       
   375         #time.sleep(20) # test to simulate GUI not accepting connection
       
   376         addr = (LOCALHOST, self.port)
       
   377         # Idle starts listening for connection on localhost
       
   378         for i in range(3):
       
   379             time.sleep(i)
       
   380             try:
       
   381                 self.rpcclt = MyRPCClient(addr)
       
   382                 break
       
   383             except socket.error, err:
       
   384                 pass
       
   385         else:
       
   386             self.display_port_binding_error()
       
   387             return None
       
   388         # Accept the connection from the Python execution server
       
   389         self.rpcclt.listening_sock.settimeout(10)
       
   390         try:
       
   391             self.rpcclt.accept()
       
   392         except socket.timeout, err:
       
   393             self.display_no_subprocess_error()
       
   394             return None
       
   395         self.rpcclt.register("stdin", self.tkconsole)
       
   396         self.rpcclt.register("stdout", self.tkconsole.stdout)
       
   397         self.rpcclt.register("stderr", self.tkconsole.stderr)
       
   398         self.rpcclt.register("flist", self.tkconsole.flist)
       
   399         self.rpcclt.register("linecache", linecache)
       
   400         self.rpcclt.register("interp", self)
       
   401         self.transfer_path()
       
   402         self.poll_subprocess()
       
   403         return self.rpcclt
       
   404 
       
   405     def restart_subprocess(self):
       
   406         if self.restarting:
       
   407             return self.rpcclt
       
   408         self.restarting = True
       
   409         # close only the subprocess debugger
       
   410         debug = self.getdebugger()
       
   411         if debug:
       
   412             try:
       
   413                 # Only close subprocess debugger, don't unregister gui_adap!
       
   414                 RemoteDebugger.close_subprocess_debugger(self.rpcclt)
       
   415             except:
       
   416                 pass
       
   417         # Kill subprocess, spawn a new one, accept connection.
       
   418         self.rpcclt.close()
       
   419         self.unix_terminate()
       
   420         console = self.tkconsole
       
   421         was_executing = console.executing
       
   422         console.executing = False
       
   423         self.spawn_subprocess()
       
   424         try:
       
   425             self.rpcclt.accept()
       
   426         except socket.timeout, err:
       
   427             self.display_no_subprocess_error()
       
   428             return None
       
   429         self.transfer_path()
       
   430         # annotate restart in shell window and mark it
       
   431         console.text.delete("iomark", "end-1c")
       
   432         if was_executing:
       
   433             console.write('\n')
       
   434             console.showprompt()
       
   435         halfbar = ((int(console.width) - 16) // 2) * '='
       
   436         console.write(halfbar + ' RESTART ' + halfbar)
       
   437         console.text.mark_set("restart", "end-1c")
       
   438         console.text.mark_gravity("restart", "left")
       
   439         console.showprompt()
       
   440         # restart subprocess debugger
       
   441         if debug:
       
   442             # Restarted debugger connects to current instance of debug GUI
       
   443             gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
       
   444             # reload remote debugger breakpoints for all PyShellEditWindows
       
   445             debug.load_breakpoints()
       
   446         self.restarting = False
       
   447         return self.rpcclt
       
   448 
       
   449     def __request_interrupt(self):
       
   450         self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
       
   451 
       
   452     def interrupt_subprocess(self):
       
   453         threading.Thread(target=self.__request_interrupt).start()
       
   454 
       
   455     def kill_subprocess(self):
       
   456         try:
       
   457             self.rpcclt.close()
       
   458         except AttributeError:  # no socket
       
   459             pass
       
   460         self.unix_terminate()
       
   461         self.tkconsole.executing = False
       
   462         self.rpcclt = None
       
   463 
       
   464     def unix_terminate(self):
       
   465         "UNIX: make sure subprocess is terminated and collect status"
       
   466         if hasattr(os, 'kill'):
       
   467             try:
       
   468                 os.kill(self.rpcpid, SIGTERM)
       
   469             except OSError:
       
   470                 # process already terminated:
       
   471                 return
       
   472             else:
       
   473                 try:
       
   474                     os.waitpid(self.rpcpid, 0)
       
   475                 except OSError:
       
   476                     return
       
   477 
       
   478     def transfer_path(self):
       
   479         self.runcommand("""if 1:
       
   480         import sys as _sys
       
   481         _sys.path = %r
       
   482         del _sys
       
   483         \n""" % (sys.path,))
       
   484 
       
   485     active_seq = None
       
   486 
       
   487     def poll_subprocess(self):
       
   488         clt = self.rpcclt
       
   489         if clt is None:
       
   490             return
       
   491         try:
       
   492             response = clt.pollresponse(self.active_seq, wait=0.05)
       
   493         except (EOFError, IOError, KeyboardInterrupt):
       
   494             # lost connection or subprocess terminated itself, restart
       
   495             # [the KBI is from rpc.SocketIO.handle_EOF()]
       
   496             if self.tkconsole.closing:
       
   497                 return
       
   498             response = None
       
   499             self.restart_subprocess()
       
   500         if response:
       
   501             self.tkconsole.resetoutput()
       
   502             self.active_seq = None
       
   503             how, what = response
       
   504             console = self.tkconsole.console
       
   505             if how == "OK":
       
   506                 if what is not None:
       
   507                     print >>console, repr(what)
       
   508             elif how == "EXCEPTION":
       
   509                 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
       
   510                     self.remote_stack_viewer()
       
   511             elif how == "ERROR":
       
   512                 errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
       
   513                 print >>sys.__stderr__, errmsg, what
       
   514                 print >>console, errmsg, what
       
   515             # we received a response to the currently active seq number:
       
   516             try:
       
   517                 self.tkconsole.endexecuting()
       
   518             except AttributeError:  # shell may have closed
       
   519                 pass
       
   520         # Reschedule myself
       
   521         if not self.tkconsole.closing:
       
   522             self.tkconsole.text.after(self.tkconsole.pollinterval,
       
   523                                       self.poll_subprocess)
       
   524 
       
   525     debugger = None
       
   526 
       
   527     def setdebugger(self, debugger):
       
   528         self.debugger = debugger
       
   529 
       
   530     def getdebugger(self):
       
   531         return self.debugger
       
   532 
       
   533     def open_remote_stack_viewer(self):
       
   534         """Initiate the remote stack viewer from a separate thread.
       
   535 
       
   536         This method is called from the subprocess, and by returning from this
       
   537         method we allow the subprocess to unblock.  After a bit the shell
       
   538         requests the subprocess to open the remote stack viewer which returns a
       
   539         static object looking at the last exceptiopn.  It is queried through
       
   540         the RPC mechanism.
       
   541 
       
   542         """
       
   543         self.tkconsole.text.after(300, self.remote_stack_viewer)
       
   544         return
       
   545 
       
   546     def remote_stack_viewer(self):
       
   547         import RemoteObjectBrowser
       
   548         oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
       
   549         if oid is None:
       
   550             self.tkconsole.root.bell()
       
   551             return
       
   552         item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
       
   553         from TreeWidget import ScrolledCanvas, TreeNode
       
   554         top = Toplevel(self.tkconsole.root)
       
   555         theme = idleConf.GetOption('main','Theme','name')
       
   556         background = idleConf.GetHighlight(theme, 'normal')['background']
       
   557         sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
       
   558         sc.frame.pack(expand=1, fill="both")
       
   559         node = TreeNode(sc.canvas, None, item)
       
   560         node.expand()
       
   561         # XXX Should GC the remote tree when closing the window
       
   562 
       
   563     gid = 0
       
   564 
       
   565     def execsource(self, source):
       
   566         "Like runsource() but assumes complete exec source"
       
   567         filename = self.stuffsource(source)
       
   568         self.execfile(filename, source)
       
   569 
       
   570     def execfile(self, filename, source=None):
       
   571         "Execute an existing file"
       
   572         if source is None:
       
   573             source = open(filename, "r").read()
       
   574         try:
       
   575             code = compile(source, filename, "exec")
       
   576         except (OverflowError, SyntaxError):
       
   577             self.tkconsole.resetoutput()
       
   578             tkerr = self.tkconsole.stderr
       
   579             print>>tkerr, '*** Error in script or command!\n'
       
   580             print>>tkerr, 'Traceback (most recent call last):'
       
   581             InteractiveInterpreter.showsyntaxerror(self, filename)
       
   582             self.tkconsole.showprompt()
       
   583         else:
       
   584             self.runcode(code)
       
   585 
       
   586     def runsource(self, source):
       
   587         "Extend base class method: Stuff the source in the line cache first"
       
   588         filename = self.stuffsource(source)
       
   589         self.more = 0
       
   590         self.save_warnings_filters = warnings.filters[:]
       
   591         warnings.filterwarnings(action="error", category=SyntaxWarning)
       
   592         if isinstance(source, types.UnicodeType):
       
   593             import IOBinding
       
   594             try:
       
   595                 source = source.encode(IOBinding.encoding)
       
   596             except UnicodeError:
       
   597                 self.tkconsole.resetoutput()
       
   598                 self.write("Unsupported characters in input\n")
       
   599                 return
       
   600         try:
       
   601             # InteractiveInterpreter.runsource() calls its runcode() method,
       
   602             # which is overridden (see below)
       
   603             return InteractiveInterpreter.runsource(self, source, filename)
       
   604         finally:
       
   605             if self.save_warnings_filters is not None:
       
   606                 warnings.filters[:] = self.save_warnings_filters
       
   607                 self.save_warnings_filters = None
       
   608 
       
   609     def stuffsource(self, source):
       
   610         "Stuff source in the filename cache"
       
   611         filename = "<pyshell#%d>" % self.gid
       
   612         self.gid = self.gid + 1
       
   613         lines = source.split("\n")
       
   614         linecache.cache[filename] = len(source)+1, 0, lines, filename
       
   615         return filename
       
   616 
       
   617     def prepend_syspath(self, filename):
       
   618         "Prepend sys.path with file's directory if not already included"
       
   619         self.runcommand("""if 1:
       
   620             _filename = %r
       
   621             import sys as _sys
       
   622             from os.path import dirname as _dirname
       
   623             _dir = _dirname(_filename)
       
   624             if not _dir in _sys.path:
       
   625                 _sys.path.insert(0, _dir)
       
   626             del _filename, _sys, _dirname, _dir
       
   627             \n""" % (filename,))
       
   628 
       
   629     def showsyntaxerror(self, filename=None):
       
   630         """Extend base class method: Add Colorizing
       
   631 
       
   632         Color the offending position instead of printing it and pointing at it
       
   633         with a caret.
       
   634 
       
   635         """
       
   636         text = self.tkconsole.text
       
   637         stuff = self.unpackerror()
       
   638         if stuff:
       
   639             msg, lineno, offset, line = stuff
       
   640             if lineno == 1:
       
   641                 pos = "iomark + %d chars" % (offset-1)
       
   642             else:
       
   643                 pos = "iomark linestart + %d lines + %d chars" % \
       
   644                       (lineno-1, offset-1)
       
   645             text.tag_add("ERROR", pos)
       
   646             text.see(pos)
       
   647             char = text.get(pos)
       
   648             if char and char in IDENTCHARS:
       
   649                 text.tag_add("ERROR", pos + " wordstart", pos)
       
   650             self.tkconsole.resetoutput()
       
   651             self.write("SyntaxError: %s\n" % str(msg))
       
   652         else:
       
   653             self.tkconsole.resetoutput()
       
   654             InteractiveInterpreter.showsyntaxerror(self, filename)
       
   655         self.tkconsole.showprompt()
       
   656 
       
   657     def unpackerror(self):
       
   658         type, value, tb = sys.exc_info()
       
   659         ok = type is SyntaxError
       
   660         if ok:
       
   661             try:
       
   662                 msg, (dummy_filename, lineno, offset, line) = value
       
   663                 if not offset:
       
   664                     offset = 0
       
   665             except:
       
   666                 ok = 0
       
   667         if ok:
       
   668             return msg, lineno, offset, line
       
   669         else:
       
   670             return None
       
   671 
       
   672     def showtraceback(self):
       
   673         "Extend base class method to reset output properly"
       
   674         self.tkconsole.resetoutput()
       
   675         self.checklinecache()
       
   676         InteractiveInterpreter.showtraceback(self)
       
   677         if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
       
   678             self.tkconsole.open_stack_viewer()
       
   679 
       
   680     def checklinecache(self):
       
   681         c = linecache.cache
       
   682         for key in c.keys():
       
   683             if key[:1] + key[-1:] != "<>":
       
   684                 del c[key]
       
   685 
       
   686     def runcommand(self, code):
       
   687         "Run the code without invoking the debugger"
       
   688         # The code better not raise an exception!
       
   689         if self.tkconsole.executing:
       
   690             self.display_executing_dialog()
       
   691             return 0
       
   692         if self.rpcclt:
       
   693             self.rpcclt.remotequeue("exec", "runcode", (code,), {})
       
   694         else:
       
   695             exec code in self.locals
       
   696         return 1
       
   697 
       
   698     def runcode(self, code):
       
   699         "Override base class method"
       
   700         if self.tkconsole.executing:
       
   701             self.interp.restart_subprocess()
       
   702         self.checklinecache()
       
   703         if self.save_warnings_filters is not None:
       
   704             warnings.filters[:] = self.save_warnings_filters
       
   705             self.save_warnings_filters = None
       
   706         debugger = self.debugger
       
   707         try:
       
   708             self.tkconsole.beginexecuting()
       
   709             try:
       
   710                 if not debugger and self.rpcclt is not None:
       
   711                     self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
       
   712                                                             (code,), {})
       
   713                 elif debugger:
       
   714                     debugger.run(code, self.locals)
       
   715                 else:
       
   716                     exec code in self.locals
       
   717             except SystemExit:
       
   718                 if not self.tkconsole.closing:
       
   719                     if tkMessageBox.askyesno(
       
   720                         "Exit?",
       
   721                         "Do you want to exit altogether?",
       
   722                         default="yes",
       
   723                         master=self.tkconsole.text):
       
   724                         raise
       
   725                     else:
       
   726                         self.showtraceback()
       
   727                 else:
       
   728                     raise
       
   729             except:
       
   730                 if use_subprocess:
       
   731                     print >> self.tkconsole.stderr, \
       
   732                              "IDLE internal error in runcode()"
       
   733                 self.showtraceback()
       
   734                 if use_subprocess:
       
   735                     self.tkconsole.endexecuting()
       
   736         finally:
       
   737             if not use_subprocess:
       
   738                 try:
       
   739                     self.tkconsole.endexecuting()
       
   740                 except AttributeError:  # shell may have closed
       
   741                     pass
       
   742 
       
   743     def write(self, s):
       
   744         "Override base class method"
       
   745         self.tkconsole.stderr.write(s)
       
   746 
       
   747     def display_port_binding_error(self):
       
   748         tkMessageBox.showerror(
       
   749             "Port Binding Error",
       
   750             "IDLE can't bind TCP/IP port 8833, which is necessary to "
       
   751             "communicate with its Python execution server.  Either "
       
   752             "no networking is installed on this computer or another "
       
   753             "process (another IDLE?) is using the port.  Run IDLE with the -n "
       
   754             "command line switch to start without a subprocess and refer to "
       
   755             "Help/IDLE Help 'Running without a subprocess' for further "
       
   756             "details.",
       
   757             master=self.tkconsole.text)
       
   758 
       
   759     def display_no_subprocess_error(self):
       
   760         tkMessageBox.showerror(
       
   761             "Subprocess Startup Error",
       
   762             "IDLE's subprocess didn't make connection.  Either IDLE can't "
       
   763             "start a subprocess or personal firewall software is blocking "
       
   764             "the connection.",
       
   765             master=self.tkconsole.text)
       
   766 
       
   767     def display_executing_dialog(self):
       
   768         tkMessageBox.showerror(
       
   769             "Already executing",
       
   770             "The Python Shell window is already executing a command; "
       
   771             "please wait until it is finished.",
       
   772             master=self.tkconsole.text)
       
   773 
       
   774 
       
   775 class PyShell(OutputWindow):
       
   776 
       
   777     shell_title = "Python Shell"
       
   778 
       
   779     # Override classes
       
   780     ColorDelegator = ModifiedColorDelegator
       
   781     UndoDelegator = ModifiedUndoDelegator
       
   782 
       
   783     # Override menus
       
   784     menu_specs = [
       
   785         ("file", "_File"),
       
   786         ("edit", "_Edit"),
       
   787         ("debug", "_Debug"),
       
   788         ("options", "_Options"),
       
   789         ("windows", "_Windows"),
       
   790         ("help", "_Help"),
       
   791     ]
       
   792 
       
   793     if macosxSupport.runningAsOSXApp():
       
   794         del menu_specs[-3]
       
   795         menu_specs[-2] = ("windows", "_Window")
       
   796 
       
   797 
       
   798     # New classes
       
   799     from IdleHistory import History
       
   800 
       
   801     def __init__(self, flist=None):
       
   802         if use_subprocess:
       
   803             ms = self.menu_specs
       
   804             if ms[2][0] != "shell":
       
   805                 ms.insert(2, ("shell", "She_ll"))
       
   806         self.interp = ModifiedInterpreter(self)
       
   807         if flist is None:
       
   808             root = Tk()
       
   809             fixwordbreaks(root)
       
   810             root.withdraw()
       
   811             flist = PyShellFileList(root)
       
   812         #
       
   813         OutputWindow.__init__(self, flist, None, None)
       
   814         #
       
   815 ##        self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
       
   816         self.usetabs = True
       
   817         # indentwidth must be 8 when using tabs.  See note in EditorWindow:
       
   818         self.indentwidth = 8
       
   819         self.context_use_ps1 = True
       
   820         #
       
   821         text = self.text
       
   822         text.configure(wrap="char")
       
   823         text.bind("<<newline-and-indent>>", self.enter_callback)
       
   824         text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
       
   825         text.bind("<<interrupt-execution>>", self.cancel_callback)
       
   826         text.bind("<<beginning-of-line>>", self.home_callback)
       
   827         text.bind("<<end-of-file>>", self.eof_callback)
       
   828         text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
       
   829         text.bind("<<toggle-debugger>>", self.toggle_debugger)
       
   830         text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
       
   831         if use_subprocess:
       
   832             text.bind("<<view-restart>>", self.view_restart_mark)
       
   833             text.bind("<<restart-shell>>", self.restart_shell)
       
   834         #
       
   835         self.save_stdout = sys.stdout
       
   836         self.save_stderr = sys.stderr
       
   837         self.save_stdin = sys.stdin
       
   838         import IOBinding
       
   839         self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
       
   840         self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
       
   841         self.console = PseudoFile(self, "console", IOBinding.encoding)
       
   842         if not use_subprocess:
       
   843             sys.stdout = self.stdout
       
   844             sys.stderr = self.stderr
       
   845             sys.stdin = self
       
   846         #
       
   847         self.history = self.History(self.text)
       
   848         #
       
   849         self.pollinterval = 50  # millisec
       
   850 
       
   851     def get_standard_extension_names(self):
       
   852         return idleConf.GetExtensions(shell_only=True)
       
   853 
       
   854     reading = False
       
   855     executing = False
       
   856     canceled = False
       
   857     endoffile = False
       
   858     closing = False
       
   859 
       
   860     def set_warning_stream(self, stream):
       
   861         global warning_stream
       
   862         warning_stream = stream
       
   863 
       
   864     def get_warning_stream(self):
       
   865         return warning_stream
       
   866 
       
   867     def toggle_debugger(self, event=None):
       
   868         if self.executing:
       
   869             tkMessageBox.showerror("Don't debug now",
       
   870                 "You can only toggle the debugger when idle",
       
   871                 master=self.text)
       
   872             self.set_debugger_indicator()
       
   873             return "break"
       
   874         else:
       
   875             db = self.interp.getdebugger()
       
   876             if db:
       
   877                 self.close_debugger()
       
   878             else:
       
   879                 self.open_debugger()
       
   880 
       
   881     def set_debugger_indicator(self):
       
   882         db = self.interp.getdebugger()
       
   883         self.setvar("<<toggle-debugger>>", not not db)
       
   884 
       
   885     def toggle_jit_stack_viewer(self, event=None):
       
   886         pass # All we need is the variable
       
   887 
       
   888     def close_debugger(self):
       
   889         db = self.interp.getdebugger()
       
   890         if db:
       
   891             self.interp.setdebugger(None)
       
   892             db.close()
       
   893             if self.interp.rpcclt:
       
   894                 RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
       
   895             self.resetoutput()
       
   896             self.console.write("[DEBUG OFF]\n")
       
   897             sys.ps1 = ">>> "
       
   898             self.showprompt()
       
   899         self.set_debugger_indicator()
       
   900 
       
   901     def open_debugger(self):
       
   902         if self.interp.rpcclt:
       
   903             dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
       
   904                                                            self)
       
   905         else:
       
   906             dbg_gui = Debugger.Debugger(self)
       
   907         self.interp.setdebugger(dbg_gui)
       
   908         dbg_gui.load_breakpoints()
       
   909         sys.ps1 = "[DEBUG ON]\n>>> "
       
   910         self.showprompt()
       
   911         self.set_debugger_indicator()
       
   912 
       
   913     def beginexecuting(self):
       
   914         "Helper for ModifiedInterpreter"
       
   915         self.resetoutput()
       
   916         self.executing = 1
       
   917 
       
   918     def endexecuting(self):
       
   919         "Helper for ModifiedInterpreter"
       
   920         self.executing = 0
       
   921         self.canceled = 0
       
   922         self.showprompt()
       
   923 
       
   924     def close(self):
       
   925         "Extend EditorWindow.close()"
       
   926         if self.executing:
       
   927             response = tkMessageBox.askokcancel(
       
   928                 "Kill?",
       
   929                 "The program is still running!\n Do you want to kill it?",
       
   930                 default="ok",
       
   931                 parent=self.text)
       
   932             if response == False:
       
   933                 return "cancel"
       
   934         if self.reading:
       
   935             self.top.quit()
       
   936         self.canceled = True
       
   937         self.closing = True
       
   938         # Wait for poll_subprocess() rescheduling to stop
       
   939         self.text.after(2 * self.pollinterval, self.close2)
       
   940 
       
   941     def close2(self):
       
   942         return EditorWindow.close(self)
       
   943 
       
   944     def _close(self):
       
   945         "Extend EditorWindow._close(), shut down debugger and execution server"
       
   946         self.close_debugger()
       
   947         if use_subprocess:
       
   948             self.interp.kill_subprocess()
       
   949         # Restore std streams
       
   950         sys.stdout = self.save_stdout
       
   951         sys.stderr = self.save_stderr
       
   952         sys.stdin = self.save_stdin
       
   953         # Break cycles
       
   954         self.interp = None
       
   955         self.console = None
       
   956         self.flist.pyshell = None
       
   957         self.history = None
       
   958         EditorWindow._close(self)
       
   959 
       
   960     def ispythonsource(self, filename):
       
   961         "Override EditorWindow method: never remove the colorizer"
       
   962         return True
       
   963 
       
   964     def short_title(self):
       
   965         return self.shell_title
       
   966 
       
   967     COPYRIGHT = \
       
   968           'Type "copyright", "credits" or "license()" for more information.'
       
   969 
       
   970     firewallmessage = """
       
   971     ****************************************************************
       
   972     Personal firewall software may warn about the connection IDLE
       
   973     makes to its subprocess using this computer's internal loopback
       
   974     interface.  This connection is not visible on any external
       
   975     interface and no data is sent to or received from the Internet.
       
   976     ****************************************************************
       
   977     """
       
   978 
       
   979     def begin(self):
       
   980         self.resetoutput()
       
   981         if use_subprocess:
       
   982             nosub = ''
       
   983             client = self.interp.start_subprocess()
       
   984             if not client:
       
   985                 self.close()
       
   986                 return False
       
   987         else:
       
   988             nosub = "==== No Subprocess ===="
       
   989         self.write("Python %s on %s\n%s\n%s\nIDLE %s      %s\n" %
       
   990                    (sys.version, sys.platform, self.COPYRIGHT,
       
   991                     self.firewallmessage, idlever.IDLE_VERSION, nosub))
       
   992         self.showprompt()
       
   993         import Tkinter
       
   994         Tkinter._default_root = None # 03Jan04 KBK What's this?
       
   995         return True
       
   996 
       
   997     def readline(self):
       
   998         save = self.reading
       
   999         try:
       
  1000             self.reading = 1
       
  1001             self.top.mainloop()  # nested mainloop()
       
  1002         finally:
       
  1003             self.reading = save
       
  1004         line = self.text.get("iomark", "end-1c")
       
  1005         if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
       
  1006             line = "\n"
       
  1007         if isinstance(line, unicode):
       
  1008             import IOBinding
       
  1009             try:
       
  1010                 line = line.encode(IOBinding.encoding)
       
  1011             except UnicodeError:
       
  1012                 pass
       
  1013         self.resetoutput()
       
  1014         if self.canceled:
       
  1015             self.canceled = 0
       
  1016             if not use_subprocess:
       
  1017                 raise KeyboardInterrupt
       
  1018         if self.endoffile:
       
  1019             self.endoffile = 0
       
  1020             line = ""
       
  1021         return line
       
  1022 
       
  1023     def isatty(self):
       
  1024         return True
       
  1025 
       
  1026     def cancel_callback(self, event=None):
       
  1027         try:
       
  1028             if self.text.compare("sel.first", "!=", "sel.last"):
       
  1029                 return # Active selection -- always use default binding
       
  1030         except:
       
  1031             pass
       
  1032         if not (self.executing or self.reading):
       
  1033             self.resetoutput()
       
  1034             self.interp.write("KeyboardInterrupt\n")
       
  1035             self.showprompt()
       
  1036             return "break"
       
  1037         self.endoffile = 0
       
  1038         self.canceled = 1
       
  1039         if (self.executing and self.interp.rpcclt):
       
  1040             if self.interp.getdebugger():
       
  1041                 self.interp.restart_subprocess()
       
  1042             else:
       
  1043                 self.interp.interrupt_subprocess()
       
  1044         if self.reading:
       
  1045             self.top.quit()  # exit the nested mainloop() in readline()
       
  1046         return "break"
       
  1047 
       
  1048     def eof_callback(self, event):
       
  1049         if self.executing and not self.reading:
       
  1050             return # Let the default binding (delete next char) take over
       
  1051         if not (self.text.compare("iomark", "==", "insert") and
       
  1052                 self.text.compare("insert", "==", "end-1c")):
       
  1053             return # Let the default binding (delete next char) take over
       
  1054         if not self.executing:
       
  1055             self.resetoutput()
       
  1056             self.close()
       
  1057         else:
       
  1058             self.canceled = 0
       
  1059             self.endoffile = 1
       
  1060             self.top.quit()
       
  1061         return "break"
       
  1062 
       
  1063     def home_callback(self, event):
       
  1064         if event.state != 0 and event.keysym == "Home":
       
  1065             return # <Modifier-Home>; fall back to class binding
       
  1066         if self.text.compare("iomark", "<=", "insert") and \
       
  1067            self.text.compare("insert linestart", "<=", "iomark"):
       
  1068             self.text.mark_set("insert", "iomark")
       
  1069             self.text.tag_remove("sel", "1.0", "end")
       
  1070             self.text.see("insert")
       
  1071             return "break"
       
  1072 
       
  1073     def linefeed_callback(self, event):
       
  1074         # Insert a linefeed without entering anything (still autoindented)
       
  1075         if self.reading:
       
  1076             self.text.insert("insert", "\n")
       
  1077             self.text.see("insert")
       
  1078         else:
       
  1079             self.newline_and_indent_event(event)
       
  1080         return "break"
       
  1081 
       
  1082     def enter_callback(self, event):
       
  1083         if self.executing and not self.reading:
       
  1084             return # Let the default binding (insert '\n') take over
       
  1085         # If some text is selected, recall the selection
       
  1086         # (but only if this before the I/O mark)
       
  1087         try:
       
  1088             sel = self.text.get("sel.first", "sel.last")
       
  1089             if sel:
       
  1090                 if self.text.compare("sel.last", "<=", "iomark"):
       
  1091                     self.recall(sel, event)
       
  1092                     return "break"
       
  1093         except:
       
  1094             pass
       
  1095         # If we're strictly before the line containing iomark, recall
       
  1096         # the current line, less a leading prompt, less leading or
       
  1097         # trailing whitespace
       
  1098         if self.text.compare("insert", "<", "iomark linestart"):
       
  1099             # Check if there's a relevant stdin range -- if so, use it
       
  1100             prev = self.text.tag_prevrange("stdin", "insert")
       
  1101             if prev and self.text.compare("insert", "<", prev[1]):
       
  1102                 self.recall(self.text.get(prev[0], prev[1]), event)
       
  1103                 return "break"
       
  1104             next = self.text.tag_nextrange("stdin", "insert")
       
  1105             if next and self.text.compare("insert lineend", ">=", next[0]):
       
  1106                 self.recall(self.text.get(next[0], next[1]), event)
       
  1107                 return "break"
       
  1108             # No stdin mark -- just get the current line, less any prompt
       
  1109             indices = self.text.tag_nextrange("console", "insert linestart")
       
  1110             if indices and \
       
  1111                self.text.compare(indices[0], "<=", "insert linestart"):
       
  1112                 self.recall(self.text.get(indices[1], "insert lineend"), event)
       
  1113             else:
       
  1114                 self.recall(self.text.get("insert linestart", "insert lineend"), event)
       
  1115             return "break"
       
  1116         # If we're between the beginning of the line and the iomark, i.e.
       
  1117         # in the prompt area, move to the end of the prompt
       
  1118         if self.text.compare("insert", "<", "iomark"):
       
  1119             self.text.mark_set("insert", "iomark")
       
  1120         # If we're in the current input and there's only whitespace
       
  1121         # beyond the cursor, erase that whitespace first
       
  1122         s = self.text.get("insert", "end-1c")
       
  1123         if s and not s.strip():
       
  1124             self.text.delete("insert", "end-1c")
       
  1125         # If we're in the current input before its last line,
       
  1126         # insert a newline right at the insert point
       
  1127         if self.text.compare("insert", "<", "end-1c linestart"):
       
  1128             self.newline_and_indent_event(event)
       
  1129             return "break"
       
  1130         # We're in the last line; append a newline and submit it
       
  1131         self.text.mark_set("insert", "end-1c")
       
  1132         if self.reading:
       
  1133             self.text.insert("insert", "\n")
       
  1134             self.text.see("insert")
       
  1135         else:
       
  1136             self.newline_and_indent_event(event)
       
  1137         self.text.tag_add("stdin", "iomark", "end-1c")
       
  1138         self.text.update_idletasks()
       
  1139         if self.reading:
       
  1140             self.top.quit() # Break out of recursive mainloop() in raw_input()
       
  1141         else:
       
  1142             self.runit()
       
  1143         return "break"
       
  1144 
       
  1145     def recall(self, s, event):
       
  1146         # remove leading and trailing empty or whitespace lines
       
  1147         s = re.sub(r'^\s*\n', '' , s)
       
  1148         s = re.sub(r'\n\s*$', '', s)
       
  1149         lines = s.split('\n')
       
  1150         self.text.undo_block_start()
       
  1151         try:
       
  1152             self.text.tag_remove("sel", "1.0", "end")
       
  1153             self.text.mark_set("insert", "end-1c")
       
  1154             prefix = self.text.get("insert linestart", "insert")
       
  1155             if prefix.rstrip().endswith(':'):
       
  1156                 self.newline_and_indent_event(event)
       
  1157                 prefix = self.text.get("insert linestart", "insert")
       
  1158             self.text.insert("insert", lines[0].strip())
       
  1159             if len(lines) > 1:
       
  1160                 orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
       
  1161                 new_base_indent  = re.search(r'^([ \t]*)', prefix).group(0)
       
  1162                 for line in lines[1:]:
       
  1163                     if line.startswith(orig_base_indent):
       
  1164                         # replace orig base indentation with new indentation
       
  1165                         line = new_base_indent + line[len(orig_base_indent):]
       
  1166                     self.text.insert('insert', '\n'+line.rstrip())
       
  1167         finally:
       
  1168             self.text.see("insert")
       
  1169             self.text.undo_block_stop()
       
  1170 
       
  1171     def runit(self):
       
  1172         line = self.text.get("iomark", "end-1c")
       
  1173         # Strip off last newline and surrounding whitespace.
       
  1174         # (To allow you to hit return twice to end a statement.)
       
  1175         i = len(line)
       
  1176         while i > 0 and line[i-1] in " \t":
       
  1177             i = i-1
       
  1178         if i > 0 and line[i-1] == "\n":
       
  1179             i = i-1
       
  1180         while i > 0 and line[i-1] in " \t":
       
  1181             i = i-1
       
  1182         line = line[:i]
       
  1183         more = self.interp.runsource(line)
       
  1184 
       
  1185     def open_stack_viewer(self, event=None):
       
  1186         if self.interp.rpcclt:
       
  1187             return self.interp.remote_stack_viewer()
       
  1188         try:
       
  1189             sys.last_traceback
       
  1190         except:
       
  1191             tkMessageBox.showerror("No stack trace",
       
  1192                 "There is no stack trace yet.\n"
       
  1193                 "(sys.last_traceback is not defined)",
       
  1194                 master=self.text)
       
  1195             return
       
  1196         from StackViewer import StackBrowser
       
  1197         sv = StackBrowser(self.root, self.flist)
       
  1198 
       
  1199     def view_restart_mark(self, event=None):
       
  1200         self.text.see("iomark")
       
  1201         self.text.see("restart")
       
  1202 
       
  1203     def restart_shell(self, event=None):
       
  1204         self.interp.restart_subprocess()
       
  1205 
       
  1206     def showprompt(self):
       
  1207         self.resetoutput()
       
  1208         try:
       
  1209             s = str(sys.ps1)
       
  1210         except:
       
  1211             s = ""
       
  1212         self.console.write(s)
       
  1213         self.text.mark_set("insert", "end-1c")
       
  1214         self.set_line_and_column()
       
  1215         self.io.reset_undo()
       
  1216 
       
  1217     def resetoutput(self):
       
  1218         source = self.text.get("iomark", "end-1c")
       
  1219         if self.history:
       
  1220             self.history.history_store(source)
       
  1221         if self.text.get("end-2c") != "\n":
       
  1222             self.text.insert("end-1c", "\n")
       
  1223         self.text.mark_set("iomark", "end-1c")
       
  1224         self.set_line_and_column()
       
  1225         sys.stdout.softspace = 0
       
  1226 
       
  1227     def write(self, s, tags=()):
       
  1228         try:
       
  1229             self.text.mark_gravity("iomark", "right")
       
  1230             OutputWindow.write(self, s, tags, "iomark")
       
  1231             self.text.mark_gravity("iomark", "left")
       
  1232         except:
       
  1233             pass
       
  1234         if self.canceled:
       
  1235             self.canceled = 0
       
  1236             if not use_subprocess:
       
  1237                 raise KeyboardInterrupt
       
  1238 
       
  1239 class PseudoFile(object):
       
  1240 
       
  1241     def __init__(self, shell, tags, encoding=None):
       
  1242         self.shell = shell
       
  1243         self.tags = tags
       
  1244         self.softspace = 0
       
  1245         self.encoding = encoding
       
  1246 
       
  1247     def write(self, s):
       
  1248         self.shell.write(s, self.tags)
       
  1249 
       
  1250     def writelines(self, l):
       
  1251         map(self.write, l)
       
  1252 
       
  1253     def flush(self):
       
  1254         pass
       
  1255 
       
  1256     def isatty(self):
       
  1257         return True
       
  1258 
       
  1259 
       
  1260 usage_msg = """\
       
  1261 
       
  1262 USAGE: idle  [-deins] [-t title] [file]*
       
  1263        idle  [-dns] [-t title] (-c cmd | -r file) [arg]*
       
  1264        idle  [-dns] [-t title] - [arg]*
       
  1265 
       
  1266   -h         print this help message and exit
       
  1267   -n         run IDLE without a subprocess (see Help/IDLE Help for details)
       
  1268 
       
  1269 The following options will override the IDLE 'settings' configuration:
       
  1270 
       
  1271   -e         open an edit window
       
  1272   -i         open a shell window
       
  1273 
       
  1274 The following options imply -i and will open a shell:
       
  1275 
       
  1276   -c cmd     run the command in a shell, or
       
  1277   -r file    run script from file
       
  1278 
       
  1279   -d         enable the debugger
       
  1280   -s         run $IDLESTARTUP or $PYTHONSTARTUP before anything else
       
  1281   -t title   set title of shell window
       
  1282 
       
  1283 A default edit window will be bypassed when -c, -r, or - are used.
       
  1284 
       
  1285 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
       
  1286 
       
  1287 Examples:
       
  1288 
       
  1289 idle
       
  1290         Open an edit window or shell depending on IDLE's configuration.
       
  1291 
       
  1292 idle foo.py foobar.py
       
  1293         Edit the files, also open a shell if configured to start with shell.
       
  1294 
       
  1295 idle -est "Baz" foo.py
       
  1296         Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
       
  1297         window with the title "Baz".
       
  1298 
       
  1299 idle -c "import sys; print sys.argv" "foo"
       
  1300         Open a shell window and run the command, passing "-c" in sys.argv[0]
       
  1301         and "foo" in sys.argv[1].
       
  1302 
       
  1303 idle -d -s -r foo.py "Hello World"
       
  1304         Open a shell window, run a startup script, enable the debugger, and
       
  1305         run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
       
  1306         sys.argv[1].
       
  1307 
       
  1308 echo "import sys; print sys.argv" | idle - "foobar"
       
  1309         Open a shell window, run the script piped in, passing '' in sys.argv[0]
       
  1310         and "foobar" in sys.argv[1].
       
  1311 """
       
  1312 
       
  1313 def main():
       
  1314     global flist, root, use_subprocess
       
  1315 
       
  1316     use_subprocess = True
       
  1317     enable_shell = False
       
  1318     enable_edit = False
       
  1319     debug = False
       
  1320     cmd = None
       
  1321     script = None
       
  1322     startup = False
       
  1323     try:
       
  1324         opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
       
  1325     except getopt.error, msg:
       
  1326         sys.stderr.write("Error: %s\n" % str(msg))
       
  1327         sys.stderr.write(usage_msg)
       
  1328         sys.exit(2)
       
  1329     for o, a in opts:
       
  1330         if o == '-c':
       
  1331             cmd = a
       
  1332             enable_shell = True
       
  1333         if o == '-d':
       
  1334             debug = True
       
  1335             enable_shell = True
       
  1336         if o == '-e':
       
  1337             enable_edit = True
       
  1338         if o == '-h':
       
  1339             sys.stdout.write(usage_msg)
       
  1340             sys.exit()
       
  1341         if o == '-i':
       
  1342             enable_shell = True
       
  1343         if o == '-n':
       
  1344             use_subprocess = False
       
  1345         if o == '-r':
       
  1346             script = a
       
  1347             if os.path.isfile(script):
       
  1348                 pass
       
  1349             else:
       
  1350                 print "No script file: ", script
       
  1351                 sys.exit()
       
  1352             enable_shell = True
       
  1353         if o == '-s':
       
  1354             startup = True
       
  1355             enable_shell = True
       
  1356         if o == '-t':
       
  1357             PyShell.shell_title = a
       
  1358             enable_shell = True
       
  1359     if args and args[0] == '-':
       
  1360         cmd = sys.stdin.read()
       
  1361         enable_shell = True
       
  1362     # process sys.argv and sys.path:
       
  1363     for i in range(len(sys.path)):
       
  1364         sys.path[i] = os.path.abspath(sys.path[i])
       
  1365     if args and args[0] == '-':
       
  1366         sys.argv = [''] + args[1:]
       
  1367     elif cmd:
       
  1368         sys.argv = ['-c'] + args
       
  1369     elif script:
       
  1370         sys.argv = [script] + args
       
  1371     elif args:
       
  1372         enable_edit = True
       
  1373         pathx = []
       
  1374         for filename in args:
       
  1375             pathx.append(os.path.dirname(filename))
       
  1376         for dir in pathx:
       
  1377             dir = os.path.abspath(dir)
       
  1378             if not dir in sys.path:
       
  1379                 sys.path.insert(0, dir)
       
  1380     else:
       
  1381         dir = os.getcwd()
       
  1382         if not dir in sys.path:
       
  1383             sys.path.insert(0, dir)
       
  1384     # check the IDLE settings configuration (but command line overrides)
       
  1385     edit_start = idleConf.GetOption('main', 'General',
       
  1386                                     'editor-on-startup', type='bool')
       
  1387     enable_edit = enable_edit or edit_start
       
  1388     enable_shell = enable_shell or not edit_start
       
  1389     # start editor and/or shell windows:
       
  1390     root = Tk(className="Idle")
       
  1391 
       
  1392     fixwordbreaks(root)
       
  1393     root.withdraw()
       
  1394     flist = PyShellFileList(root)
       
  1395     macosxSupport.setupApp(root, flist)
       
  1396 
       
  1397     if enable_edit:
       
  1398         if not (cmd or script):
       
  1399             for filename in args:
       
  1400                 flist.open(filename)
       
  1401             if not args:
       
  1402                 flist.new()
       
  1403     if enable_shell:
       
  1404         shell = flist.open_shell()
       
  1405         if not shell:
       
  1406             return # couldn't open shell
       
  1407 
       
  1408         if macosxSupport.runningAsOSXApp() and flist.dict:
       
  1409             # On OSX: when the user has double-clicked on a file that causes
       
  1410             # IDLE to be launched the shell window will open just in front of
       
  1411             # the file she wants to see. Lower the interpreter window when
       
  1412             # there are open files.
       
  1413             shell.top.lower()
       
  1414 
       
  1415     shell = flist.pyshell
       
  1416     # handle remaining options:
       
  1417     if debug:
       
  1418         shell.open_debugger()
       
  1419     if startup:
       
  1420         filename = os.environ.get("IDLESTARTUP") or \
       
  1421                    os.environ.get("PYTHONSTARTUP")
       
  1422         if filename and os.path.isfile(filename):
       
  1423             shell.interp.execfile(filename)
       
  1424     if shell and cmd or script:
       
  1425         shell.interp.runcommand("""if 1:
       
  1426             import sys as _sys
       
  1427             _sys.argv = %r
       
  1428             del _sys
       
  1429             \n""" % (sys.argv,))
       
  1430         if cmd:
       
  1431             shell.interp.execsource(cmd)
       
  1432         elif script:
       
  1433             shell.interp.prepend_syspath(script)
       
  1434             shell.interp.execfile(script)
       
  1435 
       
  1436     root.mainloop()
       
  1437     root.destroy()
       
  1438 
       
  1439 if __name__ == "__main__":
       
  1440     sys.modules['PyShell'] = sys.modules['__main__']
       
  1441     main()