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