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