home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activepython / ActivePython-2.1.1.msi / Python21_Tools_idle_PyShell.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  24.6 KB  |  783 lines

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