PyShell.py revision ad8cad3c6d18f1455de9b4d3d370702eb3a1ee9f
1#! /usr/bin/env python
2
3import os
4import os.path
5import sys
6import string
7import getopt
8import re
9import socket
10import time
11import threading
12import traceback
13import types
14import io
15
16import linecache
17from code import InteractiveInterpreter
18from platform import python_version
19
20try:
21    from Tkinter import *
22except ImportError:
23    print>>sys.__stderr__, "** IDLE can't import Tkinter.  " \
24                           "Your Python may not be configured for Tk. **"
25    sys.exit(1)
26import tkMessageBox
27
28from idlelib.EditorWindow import EditorWindow, fixwordbreaks
29from idlelib.FileList import FileList
30from idlelib.ColorDelegator import ColorDelegator
31from idlelib.UndoDelegator import UndoDelegator
32from idlelib.OutputWindow import OutputWindow
33from idlelib.configHandler import idleConf
34from idlelib import idlever
35from idlelib import rpc
36from idlelib import Debugger
37from idlelib import RemoteDebugger
38from idlelib import macosxSupport
39
40IDENTCHARS = string.ascii_letters + string.digits + "_"
41HOST = '127.0.0.1' # python execution server on localhost loopback
42PORT = 0  # someday pass in host, port for remote debug capability
43
44try:
45    from signal import SIGTERM
46except 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.
53global warning_stream
54warning_stream = sys.__stderr__
55try:
56    import warnings
57except ImportError:
58    pass
59else:
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
83def 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():
103linecache.checkcache = extended_linecache_checkcache
104
105
106class 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
285class 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
305class 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
332class 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
354class MyRPCClient(rpc.RPCClient):
355
356    def handle_EOF(self):
357        "Override the base class - just re-raise EOFError"
358        raise EOFError
359
360
361class 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
828class 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
1295class 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
1315class 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
1328class 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
1374usage_msg = """\
1375
1376USAGE: idle  [-deins] [-t title] [file]*
1377       idle  [-dns] [-t title] (-c cmd | -r file) [arg]*
1378       idle  [-dns] [-t title] - [arg]*
1379
1380  -h         print this help message and exit
1381  -n         run IDLE without a subprocess (see Help/IDLE Help for details)
1382
1383The following options will override the IDLE 'settings' configuration:
1384
1385  -e         open an edit window
1386  -i         open a shell window
1387
1388The following options imply -i and will open a shell:
1389
1390  -c cmd     run the command in a shell, or
1391  -r file    run script from file
1392
1393  -d         enable the debugger
1394  -s         run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1395  -t title   set title of shell window
1396
1397A default edit window will be bypassed when -c, -r, or - are used.
1398
1399[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1400
1401Examples:
1402
1403idle
1404        Open an edit window or shell depending on IDLE's configuration.
1405
1406idle foo.py foobar.py
1407        Edit the files, also open a shell if configured to start with shell.
1408
1409idle -est "Baz" foo.py
1410        Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1411        window with the title "Baz".
1412
1413idle -c "import sys; print sys.argv" "foo"
1414        Open a shell window and run the command, passing "-c" in sys.argv[0]
1415        and "foo" in sys.argv[1].
1416
1417idle -d -s -r foo.py "Hello World"
1418        Open a shell window, run a startup script, enable the debugger, and
1419        run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1420        sys.argv[1].
1421
1422echo "import sys; print sys.argv" | idle - "foobar"
1423        Open a shell window, run the script piped in, passing '' in sys.argv[0]
1424        and "foobar" in sys.argv[1].
1425"""
1426
1427def main():
1428    global flist, root, use_subprocess
1429
1430    use_subprocess = True
1431    enable_shell = False
1432    enable_edit = False
1433    debug = False
1434    cmd = None
1435    script = None
1436    startup = False
1437    try:
1438        opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
1439    except getopt.error, msg:
1440        sys.stderr.write("Error: %s\n" % str(msg))
1441        sys.stderr.write(usage_msg)
1442        sys.exit(2)
1443    for o, a in opts:
1444        if o == '-c':
1445            cmd = a
1446            enable_shell = True
1447        if o == '-d':
1448            debug = True
1449            enable_shell = True
1450        if o == '-e':
1451            enable_edit = True
1452        if o == '-h':
1453            sys.stdout.write(usage_msg)
1454            sys.exit()
1455        if o == '-i':
1456            enable_shell = True
1457        if o == '-n':
1458            use_subprocess = False
1459        if o == '-r':
1460            script = a
1461            if os.path.isfile(script):
1462                pass
1463            else:
1464                print "No script file: ", script
1465                sys.exit()
1466            enable_shell = True
1467        if o == '-s':
1468            startup = True
1469            enable_shell = True
1470        if o == '-t':
1471            PyShell.shell_title = a
1472            enable_shell = True
1473    if args and args[0] == '-':
1474        cmd = sys.stdin.read()
1475        enable_shell = True
1476    # process sys.argv and sys.path:
1477    for i in range(len(sys.path)):
1478        sys.path[i] = os.path.abspath(sys.path[i])
1479    if args and args[0] == '-':
1480        sys.argv = [''] + args[1:]
1481    elif cmd:
1482        sys.argv = ['-c'] + args
1483    elif script:
1484        sys.argv = [script] + args
1485    elif args:
1486        enable_edit = True
1487        pathx = []
1488        for filename in args:
1489            pathx.append(os.path.dirname(filename))
1490        for dir in pathx:
1491            dir = os.path.abspath(dir)
1492            if dir not in sys.path:
1493                sys.path.insert(0, dir)
1494    else:
1495        dir = os.getcwd()
1496        if not dir in sys.path:
1497            sys.path.insert(0, dir)
1498    # check the IDLE settings configuration (but command line overrides)
1499    edit_start = idleConf.GetOption('main', 'General',
1500                                    'editor-on-startup', type='bool')
1501    enable_edit = enable_edit or edit_start
1502    enable_shell = enable_shell or not enable_edit
1503    # start editor and/or shell windows:
1504    root = Tk(className="Idle")
1505
1506    fixwordbreaks(root)
1507    root.withdraw()
1508    flist = PyShellFileList(root)
1509    macosxSupport.setupApp(root, flist)
1510
1511    if enable_edit:
1512        if not (cmd or script):
1513            for filename in args[:]:
1514                if flist.open(filename) is None:
1515                    # filename is a directory actually, disconsider it
1516                    args.remove(filename)
1517            if not args:
1518                flist.new()
1519    if enable_shell:
1520        shell = flist.open_shell()
1521        if not shell:
1522            return # couldn't open shell
1523
1524        if macosxSupport.runningAsOSXApp() and flist.dict:
1525            # On OSX: when the user has double-clicked on a file that causes
1526            # IDLE to be launched the shell window will open just in front of
1527            # the file she wants to see. Lower the interpreter window when
1528            # there are open files.
1529            shell.top.lower()
1530
1531    shell = flist.pyshell
1532    # handle remaining options:
1533    if debug:
1534        shell.open_debugger()
1535    if startup:
1536        filename = os.environ.get("IDLESTARTUP") or \
1537                   os.environ.get("PYTHONSTARTUP")
1538        if filename and os.path.isfile(filename):
1539            shell.interp.execfile(filename)
1540    if shell and cmd or script:
1541        shell.interp.runcommand("""if 1:
1542            import sys as _sys
1543            _sys.argv = %r
1544            del _sys
1545            \n""" % (sys.argv,))
1546        if cmd:
1547            shell.interp.execsource(cmd)
1548        elif script:
1549            shell.interp.prepend_syspath(script)
1550            shell.interp.execfile(script)
1551
1552    # Check for problematic OS X Tk versions and print a warning message
1553    # in the IDLE shell window; this is less intrusive than always opening
1554    # a separate window.
1555    tkversionwarning = macosxSupport.tkVersionWarning(root)
1556    if tkversionwarning:
1557        shell.interp.runcommand(''.join(("print('", tkversionwarning, "')")))
1558
1559    while flist.inversedict:  # keep IDLE running while files are open.
1560        root.mainloop()
1561    root.destroy()
1562
1563if __name__ == "__main__":
1564    sys.modules['PyShell'] = sys.modules['__main__']
1565    main()
1566