PyShell.py revision 94bd77415fc44ada4ceba856a81fe029c12bf313
1#! /usr/bin/env python
2
3# changes by dscherer@cmu.edu
4
5#   the main() function has been replaced by a whole class, in order to
6#     address the constraint that only one process can sit on the port
7#     hard-coded into the loader.
8
9#     It attempts to load the RPC protocol server and publish itself.  If
10#     that fails, it assumes that some other copy of IDLE is already running
11#     on the port and attempts to contact it.  It then uses the RPC mechanism
12#     to ask that copy to do whatever it was instructed (via the command
13#     line) to do.  (Think netscape -remote).  The handling of command line
14#     arguments for remotes is still very incomplete.
15
16#   default behavior (no command line options) is to NOT start the Python
17#     Shell.  If files are specified, they are opened, otherwise a single
18#     blank editor window opens.
19
20#   If any command line -options are specified, a shell does appear.  This
21#     is necessary to make the current semantics of the options make sense.
22
23import os
24import spawn
25import sys
26import string
27import getopt
28import re
29import protocol
30import warnings
31
32import linecache
33from code import InteractiveInterpreter
34
35from Tkinter import *
36import tkMessageBox
37
38from EditorWindow import EditorWindow, fixwordbreaks
39from FileList import FileList
40from ColorDelegator import ColorDelegator
41from UndoDelegator import UndoDelegator
42from OutputWindow import OutputWindow, OnDemandOutputWindow
43from IdleConf import idleconf
44import idlever
45
46# We need to patch linecache.checkcache, because we don't want it
47# to throw away our <pyshell#...> entries.
48# Rather than repeating its code here, we save those entries,
49# then call the original function, and then restore the saved entries.
50def linecache_checkcache(orig_checkcache=linecache.checkcache):
51    cache = linecache.cache
52    save = {}
53    for filename in cache.keys():
54        if filename[:1] + filename[-1:] == '<>':
55            save[filename] = cache[filename]
56    orig_checkcache()
57    cache.update(save)
58linecache.checkcache = linecache_checkcache
59
60
61# Note: <<newline-and-indent>> event is defined in AutoIndent.py
62
63#$ event <<plain-newline-and-indent>>
64#$ win <Control-j>
65#$ unix <Control-j>
66
67#$ event <<beginning-of-line>>
68#$ win <Control-a>
69#$ win <Home>
70#$ unix <Control-a>
71#$ unix <Home>
72
73#$ event <<history-next>>
74#$ win <Alt-n>
75#$ unix <Alt-n>
76
77#$ event <<history-previous>>
78#$ win <Alt-p>
79#$ unix <Alt-p>
80
81#$ event <<interrupt-execution>>
82#$ win <Control-c>
83#$ unix <Control-c>
84
85#$ event <<end-of-file>>
86#$ win <Control-d>
87#$ unix <Control-d>
88
89#$ event <<open-stack-viewer>>
90
91#$ event <<toggle-debugger>>
92
93
94class PyShellEditorWindow(EditorWindow):
95
96    # Regular text edit window when a shell is present
97    # XXX ought to merge with regular editor window
98
99    def __init__(self, *args):
100        apply(EditorWindow.__init__, (self,) + args)
101        self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
102        self.text.bind("<<open-python-shell>>", self.flist.open_shell)
103
104    rmenu_specs = [
105        ("Set breakpoint here", "<<set-breakpoint-here>>"),
106    ]
107
108    def set_breakpoint_here(self, event=None):
109        if not self.flist.pyshell or not self.flist.pyshell.interp.debugger:
110            self.text.bell()
111            return
112        self.flist.pyshell.interp.debugger.set_breakpoint_here(self)
113
114
115class PyShellFileList(FileList):
116
117    # File list when a shell is present
118
119    EditorWindow = PyShellEditorWindow
120
121    pyshell = None
122
123    def open_shell(self, event=None):
124        if self.pyshell:
125            self.pyshell.wakeup()
126        else:
127            self.pyshell = PyShell(self)
128            self.pyshell.begin()
129        return self.pyshell
130
131
132class ModifiedColorDelegator(ColorDelegator):
133
134    # Colorizer for the shell window itself
135
136    def recolorize_main(self):
137        self.tag_remove("TODO", "1.0", "iomark")
138        self.tag_add("SYNC", "1.0", "iomark")
139        ColorDelegator.recolorize_main(self)
140
141    tagdefs = ColorDelegator.tagdefs.copy()
142    cconf = idleconf.getsection('Colors')
143
144    tagdefs.update({
145        "stdin": cconf.getcolor("stdin"),
146        "stdout": cconf.getcolor("stdout"),
147        "stderr": cconf.getcolor("stderr"),
148        "console": cconf.getcolor("console"),
149        "ERROR": cconf.getcolor("ERROR"),
150        None: cconf.getcolor("normal"),
151    })
152
153
154class ModifiedUndoDelegator(UndoDelegator):
155
156    # Forbid insert/delete before the I/O mark
157
158    def insert(self, index, chars, tags=None):
159        try:
160            if self.delegate.compare(index, "<", "iomark"):
161                self.delegate.bell()
162                return
163        except TclError:
164            pass
165        UndoDelegator.insert(self, index, chars, tags)
166
167    def delete(self, index1, index2=None):
168        try:
169            if self.delegate.compare(index1, "<", "iomark"):
170                self.delegate.bell()
171                return
172        except TclError:
173            pass
174        UndoDelegator.delete(self, index1, index2)
175
176class ModifiedInterpreter(InteractiveInterpreter):
177
178    def __init__(self, tkconsole):
179        self.tkconsole = tkconsole
180        locals = sys.modules['__main__'].__dict__
181        InteractiveInterpreter.__init__(self, locals=locals)
182        self.save_warnings_filters = None
183
184    gid = 0
185
186    def execsource(self, source):
187        # Like runsource() but assumes complete exec source
188        filename = self.stuffsource(source)
189        self.execfile(filename, source)
190
191    def execfile(self, filename, source=None):
192        # Execute an existing file
193        if source is None:
194            source = open(filename, "r").read()
195        try:
196            code = compile(source, filename, "exec")
197        except (OverflowError, SyntaxError):
198            self.tkconsole.resetoutput()
199            InteractiveInterpreter.showsyntaxerror(self, filename)
200        else:
201            self.runcode(code)
202
203    def runsource(self, source):
204        # Extend base class to stuff the source in the line cache first
205        filename = self.stuffsource(source)
206        self.more = 0
207        self.save_warnings_filters = warnings.filters[:]
208        warnings.filterwarnings(action="error", category=SyntaxWarning)
209        try:
210            return InteractiveInterpreter.runsource(self, source, filename)
211        finally:
212            if self.save_warnings_filters is not None:
213                warnings.filters[:] = self.save_warnings_filters
214                self.save_warnings_filters = None
215
216    def stuffsource(self, source):
217        # Stuff source in the filename cache
218        filename = "<pyshell#%d>" % self.gid
219        self.gid = self.gid + 1
220        lines = string.split(source, "\n")
221        linecache.cache[filename] = len(source)+1, 0, lines, filename
222        return filename
223
224    def showsyntaxerror(self, filename=None):
225        # Extend base class to color the offending position
226        # (instead of printing it and pointing at it with a caret)
227        text = self.tkconsole.text
228        stuff = self.unpackerror()
229        if not stuff:
230            self.tkconsole.resetoutput()
231            InteractiveInterpreter.showsyntaxerror(self, filename)
232            return
233        msg, lineno, offset, line = stuff
234        if lineno == 1:
235            pos = "iomark + %d chars" % (offset-1)
236        else:
237            pos = "iomark linestart + %d lines + %d chars" % (lineno-1,
238                                                              offset-1)
239        text.tag_add("ERROR", pos)
240        text.see(pos)
241        char = text.get(pos)
242        if char and char in string.letters + string.digits + "_":
243            text.tag_add("ERROR", pos + " wordstart", pos)
244        self.tkconsole.resetoutput()
245        self.write("SyntaxError: %s\n" % str(msg))
246
247    def unpackerror(self):
248        type, value, tb = sys.exc_info()
249        ok = type is SyntaxError
250        if ok:
251            try:
252                msg, (dummy_filename, lineno, offset, line) = value
253            except:
254                ok = 0
255        if ok:
256            return msg, lineno, offset, line
257        else:
258            return None
259
260    def showtraceback(self):
261        # Extend base class method to reset output properly
262        text = self.tkconsole.text
263        self.tkconsole.resetoutput()
264        self.checklinecache()
265        InteractiveInterpreter.showtraceback(self)
266
267    def checklinecache(self):
268        c = linecache.cache
269        for key in c.keys():
270            if key[:1] + key[-1:] != "<>":
271                del c[key]
272
273    debugger = None
274
275    def setdebugger(self, debugger):
276        self.debugger = debugger
277
278    def getdebugger(self):
279        return self.debugger
280
281    def runcode(self, code):
282        # Override base class method
283        if self.save_warnings_filters is not None:
284            warnings.filters[:] = self.save_warnings_filters
285            self.save_warnings_filters = None
286        debugger = self.debugger
287        try:
288            self.tkconsole.beginexecuting()
289            try:
290                if debugger:
291                    debugger.run(code, self.locals)
292                else:
293                    exec code in self.locals
294            except SystemExit:
295                if tkMessageBox.askyesno(
296                    "Exit?",
297                    "Do you want to exit altogether?",
298                    default="yes",
299                    master=self.tkconsole.text):
300                    raise
301                else:
302                    self.showtraceback()
303                    if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
304                        self.tkconsole.open_stack_viewer()
305            except:
306                self.showtraceback()
307                if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
308                    self.tkconsole.open_stack_viewer()
309
310        finally:
311            self.tkconsole.endexecuting()
312
313    def write(self, s):
314        # Override base class write
315        self.tkconsole.console.write(s)
316
317
318class PyShell(OutputWindow):
319
320    shell_title = "Python Shell"
321
322    # Override classes
323    ColorDelegator = ModifiedColorDelegator
324    UndoDelegator = ModifiedUndoDelegator
325
326    # Override menu bar specs
327    menu_specs = PyShellEditorWindow.menu_specs[:]
328    menu_specs.insert(len(menu_specs)-2, ("debug", "_Debug"))
329
330    # New classes
331    from IdleHistory import History
332
333    def __init__(self, flist=None):
334        self.interp = ModifiedInterpreter(self)
335        if flist is None:
336            root = Tk()
337            fixwordbreaks(root)
338            root.withdraw()
339            flist = PyShellFileList(root)
340
341        OutputWindow.__init__(self, flist, None, None)
342
343        import __builtin__
344        __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
345
346        self.auto = self.extensions["AutoIndent"] # Required extension
347        self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1)
348
349        text = self.text
350        text.configure(wrap="char")
351        text.bind("<<newline-and-indent>>", self.enter_callback)
352        text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
353        text.bind("<<interrupt-execution>>", self.cancel_callback)
354        text.bind("<<beginning-of-line>>", self.home_callback)
355        text.bind("<<end-of-file>>", self.eof_callback)
356        text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
357        text.bind("<<toggle-debugger>>", self.toggle_debugger)
358        text.bind("<<open-python-shell>>", self.flist.open_shell)
359        text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
360
361        self.save_stdout = sys.stdout
362        self.save_stderr = sys.stderr
363        self.save_stdin = sys.stdin
364        sys.stdout = PseudoFile(self, "stdout")
365        sys.stderr = PseudoFile(self, "stderr")
366        sys.stdin = self
367        self.console = PseudoFile(self, "console")
368
369        self.history = self.History(self.text)
370
371    reading = 0
372    executing = 0
373    canceled = 0
374    endoffile = 0
375
376    def toggle_debugger(self, event=None):
377        if self.executing:
378            tkMessageBox.showerror("Don't debug now",
379                "You can only toggle the debugger when idle",
380                master=self.text)
381            self.set_debugger_indicator()
382            return "break"
383        else:
384            db = self.interp.getdebugger()
385            if db:
386                self.close_debugger()
387            else:
388                self.open_debugger()
389
390    def set_debugger_indicator(self):
391        db = self.interp.getdebugger()
392        self.setvar("<<toggle-debugger>>", not not db)
393
394    def toggle_jit_stack_viewer( self, event=None):
395        pass # All we need is the variable
396
397    def close_debugger(self):
398        db = self.interp.getdebugger()
399        if db:
400            self.interp.setdebugger(None)
401            db.close()
402            self.resetoutput()
403            self.console.write("[DEBUG OFF]\n")
404            sys.ps1 = ">>> "
405            self.showprompt()
406        self.set_debugger_indicator()
407
408    def open_debugger(self):
409        import Debugger
410        self.interp.setdebugger(Debugger.Debugger(self))
411        sys.ps1 = "[DEBUG ON]\n>>> "
412        self.showprompt()
413        self.set_debugger_indicator()
414
415    def beginexecuting(self):
416        # Helper for ModifiedInterpreter
417        self.resetoutput()
418        self.executing = 1
419        ##self._cancel_check = self.cancel_check
420        ##sys.settrace(self._cancel_check)
421
422    def endexecuting(self):
423        # Helper for ModifiedInterpreter
424        ##sys.settrace(None)
425        ##self._cancel_check = None
426        self.executing = 0
427        self.canceled = 0
428
429    def close(self):
430        # Extend base class method
431        if self.executing:
432            # XXX Need to ask a question here
433            if not tkMessageBox.askokcancel(
434                "Kill?",
435                "The program is still running; do you want to kill it?",
436                default="ok",
437                master=self.text):
438                return "cancel"
439            self.canceled = 1
440            if self.reading:
441                self.top.quit()
442            return "cancel"
443        return PyShellEditorWindow.close(self)
444
445    def _close(self):
446        self.close_debugger()
447        # Restore std streams
448        sys.stdout = self.save_stdout
449        sys.stderr = self.save_stderr
450        sys.stdin = self.save_stdin
451        # Break cycles
452        self.interp = None
453        self.console = None
454        self.auto = None
455        self.flist.pyshell = None
456        self.history = None
457        OutputWindow._close(self) # Really EditorWindow._close
458
459    def ispythonsource(self, filename):
460        # Override this so EditorWindow never removes the colorizer
461        return 1
462
463    def short_title(self):
464        return self.shell_title
465
466    COPYRIGHT = \
467              'Type "copyright", "credits" or "license" for more information.'
468
469    def begin(self):
470        self.resetoutput()
471        self.write("Python %s on %s\n%s\nIDLE %s -- press F1 for help\n" %
472                   (sys.version, sys.platform, self.COPYRIGHT,
473                    idlever.IDLE_VERSION))
474        try:
475            sys.ps1
476        except AttributeError:
477            sys.ps1 = ">>> "
478        self.showprompt()
479        import Tkinter
480        Tkinter._default_root = None
481
482    def interact(self):
483        self.begin()
484        self.top.mainloop()
485
486    def readline(self):
487        save = self.reading
488        try:
489            self.reading = 1
490            self.top.mainloop()
491        finally:
492            self.reading = save
493        line = self.text.get("iomark", "end-1c")
494        self.resetoutput()
495        if self.canceled:
496            self.canceled = 0
497            raise KeyboardInterrupt
498        if self.endoffile:
499            self.endoffile = 0
500            return ""
501        return line
502
503    def isatty(self):
504        return 1
505
506    def cancel_callback(self, event):
507        try:
508            if self.text.compare("sel.first", "!=", "sel.last"):
509                return # Active selection -- always use default binding
510        except:
511            pass
512        if not (self.executing or self.reading):
513            self.resetoutput()
514            self.write("KeyboardInterrupt\n")
515            self.showprompt()
516            return "break"
517        self.endoffile = 0
518        self.canceled = 1
519        if self.reading:
520            self.top.quit()
521        return "break"
522
523    def eof_callback(self, event):
524        if self.executing and not self.reading:
525            return # Let the default binding (delete next char) take over
526        if not (self.text.compare("iomark", "==", "insert") and
527                self.text.compare("insert", "==", "end-1c")):
528            return # Let the default binding (delete next char) take over
529        if not self.executing:
530##             if not tkMessageBox.askokcancel(
531##                 "Exit?",
532##                 "Are you sure you want to exit?",
533##                 default="ok", master=self.text):
534##                 return "break"
535            self.resetoutput()
536            self.close()
537        else:
538            self.canceled = 0
539            self.endoffile = 1
540            self.top.quit()
541        return "break"
542
543    def home_callback(self, event):
544        if event.state != 0 and event.keysym == "Home":
545            return # <Modifier-Home>; fall back to class binding
546        if self.text.compare("iomark", "<=", "insert") and \
547           self.text.compare("insert linestart", "<=", "iomark"):
548            self.text.mark_set("insert", "iomark")
549            self.text.tag_remove("sel", "1.0", "end")
550            self.text.see("insert")
551            return "break"
552
553    def linefeed_callback(self, event):
554        # Insert a linefeed without entering anything (still autoindented)
555        if self.reading:
556            self.text.insert("insert", "\n")
557            self.text.see("insert")
558        else:
559            self.auto.auto_indent(event)
560        return "break"
561
562    def enter_callback(self, event):
563        if self.executing and not self.reading:
564            return # Let the default binding (insert '\n') take over
565        # If some text is selected, recall the selection
566        # (but only if this before the I/O mark)
567        try:
568            sel = self.text.get("sel.first", "sel.last")
569            if sel:
570                if self.text.compare("sel.last", "<=", "iomark"):
571                    self.recall(sel)
572                    return "break"
573        except:
574            pass
575        # If we're strictly before the line containing iomark, recall
576        # the current line, less a leading prompt, less leading or
577        # trailing whitespace
578        if self.text.compare("insert", "<", "iomark linestart"):
579            # Check if there's a relevant stdin range -- if so, use it
580            prev = self.text.tag_prevrange("stdin", "insert")
581            if prev and self.text.compare("insert", "<", prev[1]):
582                self.recall(self.text.get(prev[0], prev[1]))
583                return "break"
584            next = self.text.tag_nextrange("stdin", "insert")
585            if next and self.text.compare("insert lineend", ">=", next[0]):
586                self.recall(self.text.get(next[0], next[1]))
587                return "break"
588            # No stdin mark -- just get the current line
589            self.recall(self.text.get("insert linestart", "insert lineend"))
590            return "break"
591        # If we're in the current input and there's only whitespace
592        # beyond the cursor, erase that whitespace first
593        s = self.text.get("insert", "end-1c")
594        if s and not string.strip(s):
595            self.text.delete("insert", "end-1c")
596        # If we're in the current input before its last line,
597        # insert a newline right at the insert point
598        if self.text.compare("insert", "<", "end-1c linestart"):
599            self.auto.auto_indent(event)
600            return "break"
601        # We're in the last line; append a newline and submit it
602        self.text.mark_set("insert", "end-1c")
603        if self.reading:
604            self.text.insert("insert", "\n")
605            self.text.see("insert")
606        else:
607            self.auto.auto_indent(event)
608        self.text.tag_add("stdin", "iomark", "end-1c")
609        self.text.update_idletasks()
610        if self.reading:
611            self.top.quit() # Break out of recursive mainloop() in raw_input()
612        else:
613            self.runit()
614        return "break"
615
616    def recall(self, s):
617        if self.history:
618            self.history.recall(s)
619
620    def runit(self):
621        line = self.text.get("iomark", "end-1c")
622        # Strip off last newline and surrounding whitespace.
623        # (To allow you to hit return twice to end a statement.)
624        i = len(line)
625        while i > 0 and line[i-1] in " \t":
626            i = i-1
627        if i > 0 and line[i-1] == "\n":
628            i = i-1
629        while i > 0 and line[i-1] in " \t":
630            i = i-1
631        line = line[:i]
632        more = self.interp.runsource(line)
633        if not more:
634            self.showprompt()
635
636    def cancel_check(self, frame, what, args,
637                     dooneevent=tkinter.dooneevent,
638                     dontwait=tkinter.DONT_WAIT):
639        # Hack -- use the debugger hooks to be able to handle events
640        # and interrupt execution at any time.
641        # This slows execution down quite a bit, so you may want to
642        # disable this (by not calling settrace() in runcode() above)
643        # for full-bore (uninterruptable) speed.
644        # XXX This should become a user option.
645        if self.canceled:
646            return
647        dooneevent(dontwait)
648        if self.canceled:
649            self.canceled = 0
650            raise KeyboardInterrupt
651        return self._cancel_check
652
653    def open_stack_viewer(self, event=None):
654        try:
655            sys.last_traceback
656        except:
657            tkMessageBox.showerror("No stack trace",
658                "There is no stack trace yet.\n"
659                "(sys.last_traceback is not defined)",
660                master=self.text)
661            return
662        from StackViewer import StackBrowser
663        sv = StackBrowser(self.root, self.flist)
664
665    def showprompt(self):
666        self.resetoutput()
667        try:
668            s = str(sys.ps1)
669        except:
670            s = ""
671        self.console.write(s)
672        self.text.mark_set("insert", "end-1c")
673
674    def resetoutput(self):
675        source = self.text.get("iomark", "end-1c")
676        if self.history:
677            self.history.history_store(source)
678        if self.text.get("end-2c") != "\n":
679            self.text.insert("end-1c", "\n")
680        self.text.mark_set("iomark", "end-1c")
681        sys.stdout.softspace = 0
682
683    def write(self, s, tags=()):
684        self.text.mark_gravity("iomark", "right")
685        OutputWindow.write(self, s, tags, "iomark")
686        self.text.mark_gravity("iomark", "left")
687        if self.canceled:
688            self.canceled = 0
689            raise KeyboardInterrupt
690
691class PseudoFile:
692
693    def __init__(self, shell, tags):
694        self.shell = shell
695        self.tags = tags
696
697    def write(self, s):
698        self.shell.write(s, self.tags)
699
700    def writelines(self, l):
701        map(self.write, l)
702
703    def flush(self):
704        pass
705
706    def isatty(self):
707        return 1
708
709usage_msg = """\
710usage: idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
711
712-c command  run this command
713-d          enable debugger
714-e          edit mode; arguments are files to be edited
715-s          run $IDLESTARTUP or $PYTHONSTARTUP before anything else
716-t title    set title of shell window
717
718When neither -c nor -e is used, and there are arguments, and the first
719argument is not '-', the first argument is run as a script.  Remaining
720arguments are arguments to the script or to the command run by -c.
721"""
722
723class usageError:
724    def __init__(self, string): self.string = string
725    def __repr__(self): return self.string
726
727class main:
728    def __init__(self):
729        try:
730            self.server = protocol.Server(connection_hook = self.address_ok)
731            protocol.publish( 'IDLE', self.connect )
732            self.main( sys.argv[1:] )
733            return
734        except protocol.connectionLost:
735            try:
736                client = protocol.Client()
737                IDLE = client.getobject('IDLE')
738                if IDLE:
739                    try:
740                        IDLE.remote( sys.argv[1:] )
741                    except usageError, msg:
742                        sys.stderr.write("Error: %s\n" % str(msg))
743                        sys.stderr.write(usage_msg)
744                    return
745            except protocol.connectionLost:
746                pass
747
748        # xxx Should scream via Tk()
749        print "Something already has our socket, but it won't open a window for me!"
750        print "Unable to proceed."
751
752    def idle(self):
753        spawn.kill_zombies()
754        self.server.rpc_loop()
755        root.after(25, self.idle)
756
757    # We permit connections from localhost only
758    def address_ok(self, addr):
759        return addr[0] == '127.0.0.1'
760
761    def connect(self, client, addr):
762        return self
763
764    def remote( self, argv ):
765        # xxx Should make this behavior match the behavior in main, or redo
766        #     command line options entirely.
767
768        try:
769            opts, args = getopt.getopt(argv, "c:deist:")
770        except getopt.error, msg:
771            raise usageError(msg)
772
773        for filename in args:
774            flist.open(filename)
775        if not args:
776            flist.new()
777
778    def main( self, argv ):
779        cmd = None
780        edit = 0
781        noshell = 1
782
783        debug = 0
784        startup = 0
785
786        try:
787            opts, args = getopt.getopt(argv, "c:deist:")
788        except getopt.error, msg:
789            sys.stderr.write("Error: %s\n" % str(msg))
790            sys.stderr.write(usage_msg)
791            sys.exit(2)
792
793        for o, a in opts:
794            noshell = 0
795            if o == '-c':
796                cmd = a
797            if o == '-d':
798                debug = 1
799            if o == '-e':
800                edit = 1
801            if o == '-s':
802                startup = 1
803            if o == '-t':
804                PyShell.shell_title = a
805
806        if noshell: edit=1
807
808        for i in range(len(sys.path)):
809            sys.path[i] = os.path.abspath(sys.path[i])
810
811        pathx = []
812        if edit:
813            for filename in args:
814                pathx.append(os.path.dirname(filename))
815        elif args and args[0] != "-":
816            pathx.append(os.path.dirname(args[0]))
817        else:
818            pathx.append(os.curdir)
819        for dir in pathx:
820            dir = os.path.abspath(dir)
821            if not dir in sys.path:
822                sys.path.insert(0, dir)
823
824        global flist, root
825        root = Tk(className="Idle")
826        fixwordbreaks(root)
827        root.withdraw()
828        flist = PyShellFileList(root)
829
830        if edit:
831            for filename in args:
832                flist.open(filename)
833            if not args:
834                flist.new()
835        else:
836            if cmd:
837                sys.argv = ["-c"] + args
838            else:
839                sys.argv = args or [""]
840
841        #dbg=OnDemandOutputWindow(flist)
842        #dbg.set_title('Internal IDLE Problem')
843        #sys.stdout = PseudoFile(dbg,['stdout'])
844        #sys.stderr = PseudoFile(dbg,['stderr'])
845
846        if noshell:
847          flist.pyshell = None
848        else:
849          shell = PyShell(flist)
850          interp = shell.interp
851          flist.pyshell = shell
852
853          if startup:
854              filename = os.environ.get("IDLESTARTUP") or \
855                         os.environ.get("PYTHONSTARTUP")
856              if filename and os.path.isfile(filename):
857                  interp.execfile(filename)
858
859          if debug:
860              shell.open_debugger()
861          if cmd:
862              interp.execsource(cmd)
863          elif not edit and args and args[0] != "-":
864              interp.execfile(args[0])
865
866          shell.begin()
867
868        self.idle()
869        root.mainloop()
870        root.destroy()
871
872
873if __name__ == "__main__":
874    main()
875