PyXR

c:\python24\lib \ idlelib \ PyShell.py



0001 #! /usr/bin/env python
0002 
0003 import os
0004 import os.path
0005 import sys
0006 import string
0007 import getopt
0008 import re
0009 import socket
0010 import time
0011 import threading
0012 import traceback
0013 import types
0014 import exceptions
0015 
0016 import linecache
0017 from code import InteractiveInterpreter
0018 
0019 try:
0020     from Tkinter import *
0021 except ImportError:
0022     print>>sys.__stderr__, "** IDLE can't import Tkinter.  " \
0023                            "Your Python may not be configured for Tk. **"
0024     sys.exit(1)
0025 import tkMessageBox
0026 
0027 from EditorWindow import EditorWindow, fixwordbreaks
0028 from FileList import FileList
0029 from ColorDelegator import ColorDelegator
0030 from UndoDelegator import UndoDelegator
0031 from OutputWindow import OutputWindow
0032 from configHandler import idleConf
0033 import idlever
0034 
0035 import rpc
0036 import Debugger
0037 import RemoteDebugger
0038 
0039 IDENTCHARS = string.ascii_letters + string.digits + "_"
0040 LOCALHOST = '127.0.0.1'
0041 
0042 try:
0043     from signal import SIGTERM
0044 except ImportError:
0045     SIGTERM = 15
0046 
0047 # Override warnings module to write to warning_stream.  Initialize to send IDLE
0048 # internal warnings to the console.  ScriptBinding.check_syntax() will
0049 # temporarily redirect the stream to the shell window to display warnings when
0050 # checking user's code.
0051 global warning_stream
0052 warning_stream = sys.__stderr__
0053 try:
0054     import warnings
0055 except ImportError:
0056     pass
0057 else:
0058     def idle_showwarning(message, category, filename, lineno):
0059         file = warning_stream
0060         try:
0061             file.write(warnings.formatwarning(message, category, filename, lineno))
0062         except IOError:
0063             pass  ## file (probably __stderr__) is invalid, warning dropped.
0064     warnings.showwarning = idle_showwarning
0065     def idle_formatwarning(message, category, filename, lineno):
0066         """Format warnings the IDLE way"""
0067         s = "\nWarning (from warnings module):\n"
0068         s += '  File \"%s\", line %s\n' % (filename, lineno)
0069         line = linecache.getline(filename, lineno).strip()
0070         if line:
0071             s += "    %s\n" % line
0072         s += "%s: %s\n>>> " % (category.__name__, message)
0073         return s
0074     warnings.formatwarning = idle_formatwarning
0075 
0076 def extended_linecache_checkcache(orig_checkcache=linecache.checkcache):
0077     """Extend linecache.checkcache to preserve the <pyshell#...> entries
0078 
0079     Rather than repeating the linecache code, patch it to save the pyshell#
0080     entries, call the original linecache.checkcache(), and then restore the
0081     saved entries.  Assigning the orig_checkcache keyword arg freezes its value
0082     at definition time to the (original) method linecache.checkcache(), i.e.
0083     makes orig_checkcache lexical.
0084 
0085     """
0086     cache = linecache.cache
0087     save = {}
0088     for filename in cache.keys():
0089         if filename[:1] + filename[-1:] == '<>':
0090             save[filename] = cache[filename]
0091     orig_checkcache()
0092     cache.update(save)
0093 
0094 # Patch linecache.checkcache():
0095 linecache.checkcache = extended_linecache_checkcache
0096 
0097 
0098 class PyShellEditorWindow(EditorWindow):
0099     "Regular text edit window in IDLE, supports breakpoints"
0100 
0101     def __init__(self, *args):
0102         self.breakpoints = []
0103         EditorWindow.__init__(self, *args)
0104         self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
0105         self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
0106         self.text.bind("<<open-python-shell>>", self.flist.open_shell)
0107 
0108         self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
0109                                            'breakpoints.lst')
0110         # whenever a file is changed, restore breakpoints
0111         if self.io.filename: self.restore_file_breaks()
0112         def filename_changed_hook(old_hook=self.io.filename_change_hook,
0113                                   self=self):
0114             self.restore_file_breaks()
0115             old_hook()
0116         self.io.set_filename_change_hook(filename_changed_hook)
0117 
0118     rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
0119                    ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
0120 
0121     def set_breakpoint(self, lineno):
0122         text = self.text
0123         filename = self.io.filename
0124         text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
0125         try:
0126             i = self.breakpoints.index(lineno)
0127         except ValueError:  # only add if missing, i.e. do once
0128             self.breakpoints.append(lineno)
0129         try:    # update the subprocess debugger
0130             debug = self.flist.pyshell.interp.debugger
0131             debug.set_breakpoint_here(filename, lineno)
0132         except: # but debugger may not be active right now....
0133             pass
0134 
0135     def set_breakpoint_here(self, event=None):
0136         text = self.text
0137         filename = self.io.filename
0138         if not filename:
0139             text.bell()
0140             return
0141         lineno = int(float(text.index("insert")))
0142         self.set_breakpoint(lineno)
0143 
0144     def clear_breakpoint_here(self, event=None):
0145         text = self.text
0146         filename = self.io.filename
0147         if not filename:
0148             text.bell()
0149             return
0150         lineno = int(float(text.index("insert")))
0151         try:
0152             self.breakpoints.remove(lineno)
0153         except:
0154             pass
0155         text.tag_remove("BREAK", "insert linestart",\
0156                         "insert lineend +1char")
0157         try:
0158             debug = self.flist.pyshell.interp.debugger
0159             debug.clear_breakpoint_here(filename, lineno)
0160         except:
0161             pass
0162 
0163     def clear_file_breaks(self):
0164         if self.breakpoints:
0165             text = self.text
0166             filename = self.io.filename
0167             if not filename:
0168                 text.bell()
0169                 return
0170             self.breakpoints = []
0171             text.tag_remove("BREAK", "1.0", END)
0172             try:
0173                 debug = self.flist.pyshell.interp.debugger
0174                 debug.clear_file_breaks(filename)
0175             except:
0176                 pass
0177 
0178     def store_file_breaks(self):
0179         "Save breakpoints when file is saved"
0180         # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
0181         #     be run.  The breaks are saved at that time.  If we introduce
0182         #     a temporary file save feature the save breaks functionality
0183         #     needs to be re-verified, since the breaks at the time the
0184         #     temp file is created may differ from the breaks at the last
0185         #     permanent save of the file.  Currently, a break introduced
0186         #     after a save will be effective, but not persistent.
0187         #     This is necessary to keep the saved breaks synched with the
0188         #     saved file.
0189         #
0190         #     Breakpoints are set as tagged ranges in the text.  Certain
0191         #     kinds of edits cause these ranges to be deleted: Inserting
0192         #     or deleting a line just before a breakpoint, and certain
0193         #     deletions prior to a breakpoint.  These issues need to be
0194         #     investigated and understood.  It's not clear if they are
0195         #     Tk issues or IDLE issues, or whether they can actually
0196         #     be fixed.  Since a modified file has to be saved before it is
0197         #     run, and since self.breakpoints (from which the subprocess
0198         #     debugger is loaded) is updated during the save, the visible
0199         #     breaks stay synched with the subprocess even if one of these
0200         #     unexpected breakpoint deletions occurs.
0201         breaks = self.breakpoints
0202         filename = self.io.filename
0203         try:
0204             lines = open(self.breakpointPath,"r").readlines()
0205         except IOError:
0206             lines = []
0207         new_file = open(self.breakpointPath,"w")
0208         for line in lines:
0209             if not line.startswith(filename + '='):
0210                 new_file.write(line)
0211         self.update_breakpoints()
0212         breaks = self.breakpoints
0213         if breaks:
0214             new_file.write(filename + '=' + str(breaks) + '\n')
0215         new_file.close()
0216 
0217     def restore_file_breaks(self):
0218         self.text.update()   # this enables setting "BREAK" tags to be visible
0219         filename = self.io.filename
0220         if filename is None:
0221             return
0222         if os.path.isfile(self.breakpointPath):
0223             lines = open(self.breakpointPath,"r").readlines()
0224             for line in lines:
0225                 if line.startswith(filename + '='):
0226                     breakpoint_linenumbers = eval(line[len(filename)+1:])
0227                     for breakpoint_linenumber in breakpoint_linenumbers:
0228                         self.set_breakpoint(breakpoint_linenumber)
0229 
0230     def update_breakpoints(self):
0231         "Retrieves all the breakpoints in the current window"
0232         text = self.text
0233         ranges = text.tag_ranges("BREAK")
0234         linenumber_list = self.ranges_to_linenumbers(ranges)
0235         self.breakpoints = linenumber_list
0236 
0237     def ranges_to_linenumbers(self, ranges):
0238         lines = []
0239         for index in range(0, len(ranges), 2):
0240             lineno = int(float(ranges[index]))
0241             end = int(float(ranges[index+1]))
0242             while lineno < end:
0243                 lines.append(lineno)
0244                 lineno += 1
0245         return lines
0246 
0247 # XXX 13 Dec 2002 KBK Not used currently
0248 #    def saved_change_hook(self):
0249 #        "Extend base method - clear breaks if module is modified"
0250 #        if not self.get_saved():
0251 #            self.clear_file_breaks()
0252 #        EditorWindow.saved_change_hook(self)
0253 
0254     def _close(self):
0255         "Extend base method - clear breaks when module is closed"
0256         self.clear_file_breaks()
0257         EditorWindow._close(self)
0258 
0259 
0260 class PyShellFileList(FileList):
0261     "Extend base class: IDLE supports a shell and breakpoints"
0262 
0263     # override FileList's class variable, instances return PyShellEditorWindow
0264     # instead of EditorWindow when new edit windows are created.
0265     EditorWindow = PyShellEditorWindow
0266 
0267     pyshell = None
0268 
0269     def open_shell(self, event=None):
0270         if self.pyshell:
0271             self.pyshell.top.wakeup()
0272         else:
0273             self.pyshell = PyShell(self)
0274             if self.pyshell:
0275                 if not self.pyshell.begin():
0276                     return None
0277         return self.pyshell
0278 
0279 
0280 class ModifiedColorDelegator(ColorDelegator):
0281     "Extend base class: colorizer for the shell window itself"
0282 
0283     def __init__(self):
0284         ColorDelegator.__init__(self)
0285         self.LoadTagDefs()
0286 
0287     def recolorize_main(self):
0288         self.tag_remove("TODO", "1.0", "iomark")
0289         self.tag_add("SYNC", "1.0", "iomark")
0290         ColorDelegator.recolorize_main(self)
0291 
0292     def LoadTagDefs(self):
0293         ColorDelegator.LoadTagDefs(self)
0294         theme = idleConf.GetOption('main','Theme','name')
0295         self.tagdefs.update({
0296             "stdin": {'background':None,'foreground':None},
0297             "stdout": idleConf.GetHighlight(theme, "stdout"),
0298             "stderr": idleConf.GetHighlight(theme, "stderr"),
0299             "console": idleConf.GetHighlight(theme, "console"),
0300             None: idleConf.GetHighlight(theme, "normal"),
0301         })
0302 
0303 class ModifiedUndoDelegator(UndoDelegator):
0304     "Extend base class: forbid insert/delete before the I/O mark"
0305 
0306     def insert(self, index, chars, tags=None):
0307         try:
0308             if self.delegate.compare(index, "<", "iomark"):
0309                 self.delegate.bell()
0310                 return
0311         except TclError:
0312             pass
0313         UndoDelegator.insert(self, index, chars, tags)
0314 
0315     def delete(self, index1, index2=None):
0316         try:
0317             if self.delegate.compare(index1, "<", "iomark"):
0318                 self.delegate.bell()
0319                 return
0320         except TclError:
0321             pass
0322         UndoDelegator.delete(self, index1, index2)
0323 
0324 
0325 class MyRPCClient(rpc.RPCClient):
0326 
0327     def handle_EOF(self):
0328         "Override the base class - just re-raise EOFError"
0329         raise EOFError
0330 
0331 
0332 class ModifiedInterpreter(InteractiveInterpreter):
0333 
0334     def __init__(self, tkconsole):
0335         self.tkconsole = tkconsole
0336         locals = sys.modules['__main__'].__dict__
0337         InteractiveInterpreter.__init__(self, locals=locals)
0338         self.save_warnings_filters = None
0339         self.restarting = False
0340         self.subprocess_arglist = self.build_subprocess_arglist()
0341 
0342     port = 8833
0343     rpcclt = None
0344     rpcpid = None
0345 
0346     def spawn_subprocess(self):
0347         args = self.subprocess_arglist
0348         self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)
0349 
0350     def build_subprocess_arglist(self):
0351         w = ['-W' + s for s in sys.warnoptions]
0352         # Maybe IDLE is installed and is being accessed via sys.path,
0353         # or maybe it's not installed and the idle.py script is being
0354         # run from the IDLE source directory.
0355         del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
0356                                        default=False, type='bool')
0357         if __name__ == 'idlelib.PyShell':
0358             command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
0359         else:
0360             command = "__import__('run').main(%r)" % (del_exitf,)
0361         if sys.platform[:3] == 'win' and ' ' in sys.executable:
0362             # handle embedded space in path by quoting the argument
0363             decorated_exec = '"%s"' % sys.executable
0364         else:
0365             decorated_exec = sys.executable
0366         return [decorated_exec] + w + ["-c", command, str(self.port)]
0367 
0368     def start_subprocess(self):
0369         # spawning first avoids passing a listening socket to the subprocess
0370         self.spawn_subprocess()
0371         #time.sleep(20) # test to simulate GUI not accepting connection
0372         addr = (LOCALHOST, self.port)
0373         # Idle starts listening for connection on localhost
0374         for i in range(3):
0375             time.sleep(i)
0376             try:
0377                 self.rpcclt = MyRPCClient(addr)
0378                 break
0379             except socket.error, err:
0380                 pass
0381         else:
0382             self.display_port_binding_error()
0383             return None
0384         # Accept the connection from the Python execution server
0385         self.rpcclt.listening_sock.settimeout(10)
0386         try:
0387             self.rpcclt.accept()
0388         except socket.timeout, err:
0389             self.display_no_subprocess_error()
0390             return None
0391         self.rpcclt.register("stdin", self.tkconsole)
0392         self.rpcclt.register("stdout", self.tkconsole.stdout)
0393         self.rpcclt.register("stderr", self.tkconsole.stderr)
0394         self.rpcclt.register("flist", self.tkconsole.flist)
0395         self.rpcclt.register("linecache", linecache)
0396         self.rpcclt.register("interp", self)
0397         self.transfer_path()
0398         self.poll_subprocess()
0399         return self.rpcclt
0400 
0401     def restart_subprocess(self):
0402         if self.restarting:
0403             return self.rpcclt
0404         self.restarting = True
0405         # close only the subprocess debugger
0406         debug = self.getdebugger()
0407         if debug:
0408             try:
0409                 # Only close subprocess debugger, don't unregister gui_adap!
0410                 RemoteDebugger.close_subprocess_debugger(self.rpcclt)
0411             except:
0412                 pass
0413         # Kill subprocess, spawn a new one, accept connection.
0414         self.rpcclt.close()
0415         self.unix_terminate()
0416         console = self.tkconsole
0417         was_executing = console.executing
0418         console.executing = False
0419         self.spawn_subprocess()
0420         try:
0421             self.rpcclt.accept()
0422         except socket.timeout, err:
0423             self.display_no_subprocess_error()
0424             return None
0425         self.transfer_path()
0426         # annotate restart in shell window and mark it
0427         console.text.delete("iomark", "end-1c")
0428         if was_executing:
0429             console.write('\n')
0430             console.showprompt()
0431         halfbar = ((int(console.width) - 16) // 2) * '='
0432         console.write(halfbar + ' RESTART ' + halfbar)
0433         console.text.mark_set("restart", "end-1c")
0434         console.text.mark_gravity("restart", "left")
0435         console.showprompt()
0436         # restart subprocess debugger
0437         if debug:
0438             # Restarted debugger connects to current instance of debug GUI
0439             gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
0440             # reload remote debugger breakpoints for all PyShellEditWindows
0441             debug.load_breakpoints()
0442         self.restarting = False
0443         return self.rpcclt
0444 
0445     def __request_interrupt(self):
0446         self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
0447 
0448     def interrupt_subprocess(self):
0449         threading.Thread(target=self.__request_interrupt).start()
0450 
0451     def kill_subprocess(self):
0452         try:
0453             self.rpcclt.close()
0454         except AttributeError:  # no socket
0455             pass
0456         self.unix_terminate()
0457         self.tkconsole.executing = False
0458         self.rpcclt = None
0459 
0460     def unix_terminate(self):
0461         "UNIX: make sure subprocess is terminated and collect status"
0462         if hasattr(os, 'kill'):
0463             try:
0464                 os.kill(self.rpcpid, SIGTERM)
0465             except OSError:
0466                 # process already terminated:
0467                 return
0468             else:
0469                 try:
0470                     os.waitpid(self.rpcpid, 0)
0471                 except OSError:
0472                     return
0473 
0474     def transfer_path(self):
0475         self.runcommand("""if 1:
0476         import sys as _sys
0477         _sys.path = %r
0478         del _sys
0479         _msg = 'Use File/Exit or your end-of-file key to quit IDLE'
0480         __builtins__.quit = __builtins__.exit = _msg
0481         del _msg
0482         \n""" % (sys.path,))
0483 
0484     active_seq = None
0485 
0486     def poll_subprocess(self):
0487         clt = self.rpcclt
0488         if clt is None:
0489             return
0490         try:
0491             response = clt.pollresponse(self.active_seq, wait=0.05)
0492         except (EOFError, IOError, KeyboardInterrupt):
0493             # lost connection or subprocess terminated itself, restart
0494             # [the KBI is from rpc.SocketIO.handle_EOF()]
0495             if self.tkconsole.closing:
0496                 return
0497             response = None
0498             self.restart_subprocess()
0499         if response:
0500             self.tkconsole.resetoutput()
0501             self.active_seq = None
0502             how, what = response
0503             console = self.tkconsole.console
0504             if how == "OK":
0505                 if what is not None:
0506                     print >>console, repr(what)
0507             elif how == "EXCEPTION":
0508                 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
0509                     self.remote_stack_viewer()
0510             elif how == "ERROR":
0511                 errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
0512                 print >>sys.__stderr__, errmsg, what
0513                 print >>console, errmsg, what
0514             # we received a response to the currently active seq number:
0515             self.tkconsole.endexecuting()
0516         # Reschedule myself
0517         if not self.tkconsole.closing:
0518             self.tkconsole.text.after(self.tkconsole.pollinterval,
0519                                       self.poll_subprocess)
0520 
0521     debugger = None
0522 
0523     def setdebugger(self, debugger):
0524         self.debugger = debugger
0525 
0526     def getdebugger(self):
0527         return self.debugger
0528 
0529     def open_remote_stack_viewer(self):
0530         """Initiate the remote stack viewer from a separate thread.
0531 
0532         This method is called from the subprocess, and by returning from this
0533         method we allow the subprocess to unblock.  After a bit the shell
0534         requests the subprocess to open the remote stack viewer which returns a
0535         static object looking at the last exceptiopn.  It is queried through
0536         the RPC mechanism.
0537 
0538         """
0539         self.tkconsole.text.after(300, self.remote_stack_viewer)
0540         return
0541 
0542     def remote_stack_viewer(self):
0543         import RemoteObjectBrowser
0544         oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
0545         if oid is None:
0546             self.tkconsole.root.bell()
0547             return
0548         item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
0549         from TreeWidget import ScrolledCanvas, TreeNode
0550         top = Toplevel(self.tkconsole.root)
0551         theme = idleConf.GetOption('main','Theme','name')
0552         background = idleConf.GetHighlight(theme, 'normal')['background']
0553         sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
0554         sc.frame.pack(expand=1, fill="both")
0555         node = TreeNode(sc.canvas, None, item)
0556         node.expand()
0557         # XXX Should GC the remote tree when closing the window
0558 
0559     gid = 0
0560 
0561     def execsource(self, source):
0562         "Like runsource() but assumes complete exec source"
0563         filename = self.stuffsource(source)
0564         self.execfile(filename, source)
0565 
0566     def execfile(self, filename, source=None):
0567         "Execute an existing file"
0568         if source is None:
0569             source = open(filename, "r").read()
0570         try:
0571             code = compile(source, filename, "exec")
0572         except (OverflowError, SyntaxError):
0573             self.tkconsole.resetoutput()
0574             tkerr = self.tkconsole.stderr
0575             print>>tkerr, '*** Error in script or command!\n'
0576             print>>tkerr, 'Traceback (most recent call last):'
0577             InteractiveInterpreter.showsyntaxerror(self, filename)
0578             self.tkconsole.showprompt()
0579         else:
0580             self.runcode(code)
0581 
0582     def runsource(self, source):
0583         "Extend base class method: Stuff the source in the line cache first"
0584         filename = self.stuffsource(source)
0585         self.more = 0
0586         self.save_warnings_filters = warnings.filters[:]
0587         warnings.filterwarnings(action="error", category=SyntaxWarning)
0588         if isinstance(source, types.UnicodeType):
0589             import IOBinding
0590             try:
0591                 source = source.encode(IOBinding.encoding)
0592             except UnicodeError:
0593                 self.tkconsole.resetoutput()
0594                 self.write("Unsupported characters in input")
0595                 return
0596         try:
0597             return InteractiveInterpreter.runsource(self, source, filename)
0598         finally:
0599             if self.save_warnings_filters is not None:
0600                 warnings.filters[:] = self.save_warnings_filters
0601                 self.save_warnings_filters = None
0602 
0603     def stuffsource(self, source):
0604         "Stuff source in the filename cache"
0605         filename = "<pyshell#%d>" % self.gid
0606         self.gid = self.gid + 1
0607         lines = source.split("\n")
0608         linecache.cache[filename] = len(source)+1, 0, lines, filename
0609         return filename
0610 
0611     def prepend_syspath(self, filename):
0612         "Prepend sys.path with file's directory if not already included"
0613         self.runcommand("""if 1:
0614             _filename = %r
0615             import sys as _sys
0616             from os.path import dirname as _dirname
0617             _dir = _dirname(_filename)
0618             if not _dir in _sys.path:
0619                 _sys.path.insert(0, _dir)
0620             del _filename, _sys, _dirname, _dir
0621             \n""" % (filename,))
0622 
0623     def showsyntaxerror(self, filename=None):
0624         """Extend base class method: Add Colorizing
0625 
0626         Color the offending position instead of printing it and pointing at it
0627         with a caret.
0628 
0629         """
0630         text = self.tkconsole.text
0631         stuff = self.unpackerror()
0632         if stuff:
0633             msg, lineno, offset, line = stuff
0634             if lineno == 1:
0635                 pos = "iomark + %d chars" % (offset-1)
0636             else:
0637                 pos = "iomark linestart + %d lines + %d chars" % \
0638                       (lineno-1, offset-1)
0639             text.tag_add("ERROR", pos)
0640             text.see(pos)
0641             char = text.get(pos)
0642             if char and char in IDENTCHARS:
0643                 text.tag_add("ERROR", pos + " wordstart", pos)
0644             self.tkconsole.resetoutput()
0645             self.write("SyntaxError: %s\n" % str(msg))
0646         else:
0647             self.tkconsole.resetoutput()
0648             InteractiveInterpreter.showsyntaxerror(self, filename)
0649         self.tkconsole.showprompt()
0650 
0651     def unpackerror(self):
0652         type, value, tb = sys.exc_info()
0653         ok = type is SyntaxError
0654         if ok:
0655             try:
0656                 msg, (dummy_filename, lineno, offset, line) = value
0657                 if not offset:
0658                     offset = 0
0659             except:
0660                 ok = 0
0661         if ok:
0662             return msg, lineno, offset, line
0663         else:
0664             return None
0665 
0666     def showtraceback(self):
0667         "Extend base class method to reset output properly"
0668         self.tkconsole.resetoutput()
0669         self.checklinecache()
0670         InteractiveInterpreter.showtraceback(self)
0671         if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
0672             self.tkconsole.open_stack_viewer()
0673 
0674     def checklinecache(self):
0675         c = linecache.cache
0676         for key in c.keys():
0677             if key[:1] + key[-1:] != "<>":
0678                 del c[key]
0679 
0680     def runcommand(self, code):
0681         "Run the code without invoking the debugger"
0682         # The code better not raise an exception!
0683         if self.tkconsole.executing:
0684             self.display_executing_dialog()
0685             return 0
0686         if self.rpcclt:
0687             self.rpcclt.remotequeue("exec", "runcode", (code,), {})
0688         else:
0689             exec code in self.locals
0690         return 1
0691 
0692     def runcode(self, code):
0693         "Override base class method"
0694         if self.tkconsole.executing:
0695             self.interp.restart_subprocess()
0696         self.checklinecache()
0697         if self.save_warnings_filters is not None:
0698             warnings.filters[:] = self.save_warnings_filters
0699             self.save_warnings_filters = None
0700         debugger = self.debugger
0701         try:
0702             self.tkconsole.beginexecuting()
0703             try:
0704                 if not debugger and self.rpcclt is not None:
0705                     self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
0706                                                             (code,), {})
0707                 elif debugger:
0708                     debugger.run(code, self.locals)
0709                 else:
0710                     exec code in self.locals
0711             except SystemExit:
0712                 if tkMessageBox.askyesno(
0713                     "Exit?",
0714                     "Do you want to exit altogether?",
0715                     default="yes",
0716                     master=self.tkconsole.text):
0717                     raise
0718                 else:
0719                     self.showtraceback()
0720             except:
0721                 self.showtraceback()
0722         finally:
0723             if not use_subprocess:
0724                 self.tkconsole.endexecuting()
0725 
0726     def write(self, s):
0727         "Override base class method"
0728         self.tkconsole.stderr.write(s)
0729 
0730     def display_port_binding_error(self):
0731         tkMessageBox.showerror(
0732             "Port Binding Error",
0733             "IDLE can't bind TCP/IP port 8833, which is necessary to "
0734             "communicate with its Python execution server.  Either "
0735             "no networking is installed on this computer or another "
0736             "process (another IDLE?) is using the port.  Run IDLE with the -n "
0737             "command line switch to start without a subprocess and refer to "
0738             "Help/IDLE Help 'Running without a subprocess' for further "
0739             "details.",
0740             master=self.tkconsole.text)
0741 
0742     def display_no_subprocess_error(self):
0743         tkMessageBox.showerror(
0744             "Subprocess Startup Error",
0745             "IDLE's subprocess didn't make connection.  Either IDLE can't "
0746             "start a subprocess or personal firewall software is blocking "
0747             "the connection.",
0748             master=self.tkconsole.text)
0749 
0750     def display_executing_dialog(self):
0751         tkMessageBox.showerror(
0752             "Already executing",
0753             "The Python Shell window is already executing a command; "
0754             "please wait until it is finished.",
0755             master=self.tkconsole.text)
0756 
0757 
0758 class PyShell(OutputWindow):
0759 
0760     shell_title = "Python Shell"
0761 
0762     # Override classes
0763     ColorDelegator = ModifiedColorDelegator
0764     UndoDelegator = ModifiedUndoDelegator
0765 
0766     # Override menus
0767     menu_specs = [
0768         ("file", "_File"),
0769         ("edit", "_Edit"),
0770         ("debug", "_Debug"),
0771         ("options", "_Options"),
0772         ("windows", "_Windows"),
0773         ("help", "_Help"),
0774     ]
0775 
0776     # New classes
0777     from IdleHistory import History
0778 
0779     def __init__(self, flist=None):
0780         if use_subprocess:
0781             ms = self.menu_specs
0782             if ms[2][0] != "shell":
0783                 ms.insert(2, ("shell", "_Shell"))
0784         self.interp = ModifiedInterpreter(self)
0785         if flist is None:
0786             root = Tk()
0787             fixwordbreaks(root)
0788             root.withdraw()
0789             flist = PyShellFileList(root)
0790         #
0791         OutputWindow.__init__(self, flist, None, None)
0792         #
0793         import __builtin__
0794         __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D."
0795         #
0796         self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
0797         #
0798         text = self.text
0799         text.configure(wrap="char")
0800         text.bind("<<newline-and-indent>>", self.enter_callback)
0801         text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
0802         text.bind("<<interrupt-execution>>", self.cancel_callback)
0803         text.bind("<<beginning-of-line>>", self.home_callback)
0804         text.bind("<<end-of-file>>", self.eof_callback)
0805         text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
0806         text.bind("<<toggle-debugger>>", self.toggle_debugger)
0807         text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
0808         if use_subprocess:
0809             text.bind("<<view-restart>>", self.view_restart_mark)
0810             text.bind("<<restart-shell>>", self.restart_shell)
0811         #
0812         self.save_stdout = sys.stdout
0813         self.save_stderr = sys.stderr
0814         self.save_stdin = sys.stdin
0815         import IOBinding
0816         self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
0817         self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
0818         self.console = PseudoFile(self, "console", IOBinding.encoding)
0819         if not use_subprocess:
0820             sys.stdout = self.stdout
0821             sys.stderr = self.stderr
0822             sys.stdin = self
0823         #
0824         self.history = self.History(self.text)
0825         #
0826         self.pollinterval = 50  # millisec
0827 
0828     def get_standard_extension_names(self):
0829         return idleConf.GetExtensions(shell_only=True)
0830 
0831     reading = False
0832     executing = False
0833     canceled = False
0834     endoffile = False
0835     closing = False
0836 
0837     def set_warning_stream(self, stream):
0838         global warning_stream
0839         warning_stream = stream
0840 
0841     def get_warning_stream(self):
0842         return warning_stream
0843 
0844     def toggle_debugger(self, event=None):
0845         if self.executing:
0846             tkMessageBox.showerror("Don't debug now",
0847                 "You can only toggle the debugger when idle",
0848                 master=self.text)
0849             self.set_debugger_indicator()
0850             return "break"
0851         else:
0852             db = self.interp.getdebugger()
0853             if db:
0854                 self.close_debugger()
0855             else:
0856                 self.open_debugger()
0857 
0858     def set_debugger_indicator(self):
0859         db = self.interp.getdebugger()
0860         self.setvar("<<toggle-debugger>>", not not db)
0861 
0862     def toggle_jit_stack_viewer(self, event=None):
0863         pass # All we need is the variable
0864 
0865     def close_debugger(self):
0866         db = self.interp.getdebugger()
0867         if db:
0868             self.interp.setdebugger(None)
0869             db.close()
0870             if self.interp.rpcclt:
0871                 RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
0872             self.resetoutput()
0873             self.console.write("[DEBUG OFF]\n")
0874             sys.ps1 = ">>> "
0875             self.showprompt()
0876         self.set_debugger_indicator()
0877 
0878     def open_debugger(self):
0879         if self.interp.rpcclt:
0880             dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
0881                                                            self)
0882         else:
0883             dbg_gui = Debugger.Debugger(self)
0884         self.interp.setdebugger(dbg_gui)
0885         dbg_gui.load_breakpoints()
0886         sys.ps1 = "[DEBUG ON]\n>>> "
0887         self.showprompt()
0888         self.set_debugger_indicator()
0889 
0890     def beginexecuting(self):
0891         "Helper for ModifiedInterpreter"
0892         self.resetoutput()
0893         self.executing = 1
0894 
0895     def endexecuting(self):
0896         "Helper for ModifiedInterpreter"
0897         self.executing = 0
0898         self.canceled = 0
0899         self.showprompt()
0900 
0901     def close(self):
0902         "Extend EditorWindow.close()"
0903         if self.executing:
0904             response = tkMessageBox.askokcancel(
0905                 "Kill?",
0906                 "The program is still running!\n Do you want to kill it?",
0907                 default="ok",
0908                 parent=self.text)
0909             if response == False:
0910                 return "cancel"
0911         self.closing = True
0912         # Wait for poll_subprocess() rescheduling to stop
0913         self.text.after(2 * self.pollinterval, self.close2)
0914 
0915     def close2(self):
0916         return EditorWindow.close(self)
0917 
0918     def _close(self):
0919         "Extend EditorWindow._close(), shut down debugger and execution server"
0920         self.close_debugger()
0921         if use_subprocess:
0922             self.interp.kill_subprocess()
0923         # Restore std streams
0924         sys.stdout = self.save_stdout
0925         sys.stderr = self.save_stderr
0926         sys.stdin = self.save_stdin
0927         # Break cycles
0928         self.interp = None
0929         self.console = None
0930         self.flist.pyshell = None
0931         self.history = None
0932         EditorWindow._close(self)
0933 
0934     def ispythonsource(self, filename):
0935         "Override EditorWindow method: never remove the colorizer"
0936         return True
0937 
0938     def short_title(self):
0939         return self.shell_title
0940 
0941     COPYRIGHT = \
0942           'Type "copyright", "credits" or "license()" for more information.'
0943 
0944     firewallmessage = """
0945     ****************************************************************
0946     Personal firewall software may warn about the connection IDLE
0947     makes to its subprocess using this computer's internal loopback
0948     interface.  This connection is not visible on any external
0949     interface and no data is sent to or received from the Internet.
0950     ****************************************************************
0951     """
0952 
0953     def begin(self):
0954         self.resetoutput()
0955         if use_subprocess:
0956             nosub = ''
0957             client = self.interp.start_subprocess()
0958             if not client:
0959                 self.close()
0960                 return False
0961         else:
0962             nosub = "==== No Subprocess ===="
0963         self.write("Python %s on %s\n%s\n%s\nIDLE %s      %s\n" %
0964                    (sys.version, sys.platform, self.COPYRIGHT,
0965                     self.firewallmessage, idlever.IDLE_VERSION, nosub))
0966         self.showprompt()
0967         import Tkinter
0968         Tkinter._default_root = None # 03Jan04 KBK What's this?
0969         return True
0970 
0971     def readline(self):
0972         save = self.reading
0973         try:
0974             self.reading = 1
0975             self.top.mainloop()
0976         finally:
0977             self.reading = save
0978         line = self.text.get("iomark", "end-1c")
0979         if isinstance(line, unicode):
0980             import IOBinding
0981             try:
0982                 line = line.encode(IOBinding.encoding)
0983             except UnicodeError:
0984                 pass
0985         self.resetoutput()
0986         if self.canceled:
0987             self.canceled = 0
0988             raise KeyboardInterrupt
0989         if self.endoffile:
0990             self.endoffile = 0
0991             return ""
0992         return line
0993 
0994     def isatty(self):
0995         return True
0996 
0997     def cancel_callback(self, event=None):
0998         try:
0999             if self.text.compare("sel.first", "!=", "sel.last"):
1000                 return # Active selection -- always use default binding
1001         except:
1002             pass
1003         if not (self.executing or self.reading):
1004             self.resetoutput()
1005             self.interp.write("KeyboardInterrupt\n")
1006             self.showprompt()
1007             return "break"
1008         self.endoffile = 0
1009         self.canceled = 1
1010         if self.reading:
1011             self.top.quit()
1012         elif (self.executing and self.interp.rpcclt):
1013             if self.interp.getdebugger():
1014                 self.interp.restart_subprocess()
1015             else:
1016                 self.interp.interrupt_subprocess()
1017         return "break"
1018 
1019     def eof_callback(self, event):
1020         if self.executing and not self.reading:
1021             return # Let the default binding (delete next char) take over
1022         if not (self.text.compare("iomark", "==", "insert") and
1023                 self.text.compare("insert", "==", "end-1c")):
1024             return # Let the default binding (delete next char) take over
1025         if not self.executing:
1026             self.resetoutput()
1027             self.close()
1028         else:
1029             self.canceled = 0
1030             self.endoffile = 1
1031             self.top.quit()
1032         return "break"
1033 
1034     def home_callback(self, event):
1035         if event.state != 0 and event.keysym == "Home":
1036             return # <Modifier-Home>; fall back to class binding
1037         if self.text.compare("iomark", "<=", "insert") and \
1038            self.text.compare("insert linestart", "<=", "iomark"):
1039             self.text.mark_set("insert", "iomark")
1040             self.text.tag_remove("sel", "1.0", "end")
1041             self.text.see("insert")
1042             return "break"
1043 
1044     def linefeed_callback(self, event):
1045         # Insert a linefeed without entering anything (still autoindented)
1046         if self.reading:
1047             self.text.insert("insert", "\n")
1048             self.text.see("insert")
1049         else:
1050             self.newline_and_indent_event(event)
1051         return "break"
1052 
1053     def enter_callback(self, event):
1054         if self.executing and not self.reading:
1055             return # Let the default binding (insert '\n') take over
1056         # If some text is selected, recall the selection
1057         # (but only if this before the I/O mark)
1058         try:
1059             sel = self.text.get("sel.first", "sel.last")
1060             if sel:
1061                 if self.text.compare("sel.last", "<=", "iomark"):
1062                     self.recall(sel)
1063                     return "break"
1064         except:
1065             pass
1066         # If we're strictly before the line containing iomark, recall
1067         # the current line, less a leading prompt, less leading or
1068         # trailing whitespace
1069         if self.text.compare("insert", "<", "iomark linestart"):
1070             # Check if there's a relevant stdin range -- if so, use it
1071             prev = self.text.tag_prevrange("stdin", "insert")
1072             if prev and self.text.compare("insert", "<", prev[1]):
1073                 self.recall(self.text.get(prev[0], prev[1]))
1074                 return "break"
1075             next = self.text.tag_nextrange("stdin", "insert")
1076             if next and self.text.compare("insert lineend", ">=", next[0]):
1077                 self.recall(self.text.get(next[0], next[1]))
1078                 return "break"
1079             # No stdin mark -- just get the current line, less any prompt
1080             line = self.text.get("insert linestart", "insert lineend")
1081             last_line_of_prompt = sys.ps1.split('\n')[-1]
1082             if line.startswith(last_line_of_prompt):
1083                 line = line[len(last_line_of_prompt):]
1084             self.recall(line)
1085             return "break"
1086         # If we're between the beginning of the line and the iomark, i.e.
1087         # in the prompt area, move to the end of the prompt
1088         if self.text.compare("insert", "<", "iomark"):
1089             self.text.mark_set("insert", "iomark")
1090         # If we're in the current input and there's only whitespace
1091         # beyond the cursor, erase that whitespace first
1092         s = self.text.get("insert", "end-1c")
1093         if s and not s.strip():
1094             self.text.delete("insert", "end-1c")
1095         # If we're in the current input before its last line,
1096         # insert a newline right at the insert point
1097         if self.text.compare("insert", "<", "end-1c linestart"):
1098             self.newline_and_indent_event(event)
1099             return "break"
1100         # We're in the last line; append a newline and submit it
1101         self.text.mark_set("insert", "end-1c")
1102         if self.reading:
1103             self.text.insert("insert", "\n")
1104             self.text.see("insert")
1105         else:
1106             self.newline_and_indent_event(event)
1107         self.text.tag_add("stdin", "iomark", "end-1c")
1108         self.text.update_idletasks()
1109         if self.reading:
1110             self.top.quit() # Break out of recursive mainloop() in raw_input()
1111         else:
1112             self.runit()
1113         return "break"
1114 
1115     def recall(self, s):
1116         if self.history:
1117             self.history.recall(s)
1118 
1119     def runit(self):
1120         line = self.text.get("iomark", "end-1c")
1121         # Strip off last newline and surrounding whitespace.
1122         # (To allow you to hit return twice to end a statement.)
1123         i = len(line)
1124         while i > 0 and line[i-1] in " \t":
1125             i = i-1
1126         if i > 0 and line[i-1] == "\n":
1127             i = i-1
1128         while i > 0 and line[i-1] in " \t":
1129             i = i-1
1130         line = line[:i]
1131         more = self.interp.runsource(line)
1132 
1133     def open_stack_viewer(self, event=None):
1134         if self.interp.rpcclt:
1135             return self.interp.remote_stack_viewer()
1136         try:
1137             sys.last_traceback
1138         except:
1139             tkMessageBox.showerror("No stack trace",
1140                 "There is no stack trace yet.\n"
1141                 "(sys.last_traceback is not defined)",
1142                 master=self.text)
1143             return
1144         from StackViewer import StackBrowser
1145         sv = StackBrowser(self.root, self.flist)
1146 
1147     def view_restart_mark(self, event=None):
1148         self.text.see("iomark")
1149         self.text.see("restart")
1150 
1151     def restart_shell(self, event=None):
1152         self.interp.restart_subprocess()
1153 
1154     def showprompt(self):
1155         self.resetoutput()
1156         try:
1157             s = str(sys.ps1)
1158         except:
1159             s = ""
1160         self.console.write(s)
1161         self.text.mark_set("insert", "end-1c")
1162         self.set_line_and_column()
1163         self.io.reset_undo()
1164 
1165     def resetoutput(self):
1166         source = self.text.get("iomark", "end-1c")
1167         if self.history:
1168             self.history.history_store(source)
1169         if self.text.get("end-2c") != "\n":
1170             self.text.insert("end-1c", "\n")
1171         self.text.mark_set("iomark", "end-1c")
1172         self.set_line_and_column()
1173         sys.stdout.softspace = 0
1174 
1175     def write(self, s, tags=()):
1176         try:
1177             self.text.mark_gravity("iomark", "right")
1178             OutputWindow.write(self, s, tags, "iomark")
1179             self.text.mark_gravity("iomark", "left")
1180         except:
1181             pass
1182         if self.canceled:
1183             self.canceled = 0
1184             if not use_subprocess:
1185                 raise KeyboardInterrupt
1186 
1187 class PseudoFile:
1188 
1189     def __init__(self, shell, tags, encoding=None):
1190         self.shell = shell
1191         self.tags = tags
1192         self.softspace = 0
1193         self.encoding = encoding
1194 
1195     def write(self, s):
1196         self.shell.write(s, self.tags)
1197 
1198     def writelines(self, l):
1199         map(self.write, l)
1200 
1201     def flush(self):
1202         pass
1203 
1204     def isatty(self):
1205         return True
1206 
1207 
1208 usage_msg = """\
1209 
1210 USAGE: idle  [-deins] [-t title] [file]*
1211        idle  [-dns] [-t title] (-c cmd | -r file) [arg]*
1212        idle  [-dns] [-t title] - [arg]*
1213 
1214   -h         print this help message and exit
1215   -n         run IDLE without a subprocess (see Help/IDLE Help for details)
1216 
1217 The following options will override the IDLE 'settings' configuration:
1218 
1219   -e         open an edit window
1220   -i         open a shell window
1221 
1222 The following options imply -i and will open a shell:
1223 
1224   -c cmd     run the command in a shell, or
1225   -r file    run script from file
1226 
1227   -d         enable the debugger
1228   -s         run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1229   -t title   set title of shell window
1230 
1231 A default edit window will be bypassed when -c, -r, or - are used.
1232 
1233 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1234 
1235 Examples:
1236 
1237 idle
1238         Open an edit window or shell depending on IDLE's configuration.
1239 
1240 idle foo.py foobar.py
1241         Edit the files, also open a shell if configured to start with shell.
1242 
1243 idle -est "Baz" foo.py
1244         Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1245         window with the title "Baz".
1246 
1247 idle -c "import sys; print sys.argv" "foo"
1248         Open a shell window and run the command, passing "-c" in sys.argv[0]
1249         and "foo" in sys.argv[1].
1250 
1251 idle -d -s -r foo.py "Hello World"
1252         Open a shell window, run a startup script, enable the debugger, and
1253         run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1254         sys.argv[1].
1255 
1256 echo "import sys; print sys.argv" | idle - "foobar"
1257         Open a shell window, run the script piped in, passing '' in sys.argv[0]
1258         and "foobar" in sys.argv[1].
1259 """
1260 
1261 def main():
1262     global flist, root, use_subprocess
1263 
1264     use_subprocess = True
1265     enable_shell = False
1266     enable_edit = False
1267     debug = False
1268     cmd = None
1269     script = None
1270     startup = False
1271     try:
1272         sys.ps1
1273     except AttributeError:
1274         sys.ps1 = '>>> '
1275     try:
1276         opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
1277     except getopt.error, msg:
1278         sys.stderr.write("Error: %s\n" % str(msg))
1279         sys.stderr.write(usage_msg)
1280         sys.exit(2)
1281     for o, a in opts:
1282         if o == '-c':
1283             cmd = a
1284             enable_shell = True
1285         if o == '-d':
1286             debug = True
1287             enable_shell = True
1288         if o == '-e':
1289             enable_edit = True
1290         if o == '-h':
1291             sys.stdout.write(usage_msg)
1292             sys.exit()
1293         if o == '-i':
1294             enable_shell = True
1295         if o == '-n':
1296             use_subprocess = False
1297         if o == '-r':
1298             script = a
1299             if os.path.isfile(script):
1300                 pass
1301             else:
1302                 print "No script file: ", script
1303                 sys.exit()
1304             enable_shell = True
1305         if o == '-s':
1306             startup = True
1307             enable_shell = True
1308         if o == '-t':
1309             PyShell.shell_title = a
1310             enable_shell = True
1311     if args and args[0] == '-':
1312         cmd = sys.stdin.read()
1313         enable_shell = True
1314     # process sys.argv and sys.path:
1315     for i in range(len(sys.path)):
1316         sys.path[i] = os.path.abspath(sys.path[i])
1317     if args and args[0] == '-':
1318         sys.argv = [''] + args[1:]
1319     elif cmd:
1320         sys.argv = ['-c'] + args
1321     elif script:
1322         sys.argv = [script] + args
1323     elif args:
1324         enable_edit = True
1325         pathx = []
1326         for filename in args:
1327             pathx.append(os.path.dirname(filename))
1328         for dir in pathx:
1329             dir = os.path.abspath(dir)
1330             if not dir in sys.path:
1331                 sys.path.insert(0, dir)
1332     else:
1333         dir = os.getcwd()
1334         if not dir in sys.path:
1335             sys.path.insert(0, dir)
1336     # check the IDLE settings configuration (but command line overrides)
1337     edit_start = idleConf.GetOption('main', 'General',
1338                                     'editor-on-startup', type='bool')
1339     enable_edit = enable_edit or edit_start
1340     enable_shell = enable_shell or not edit_start
1341     # start editor and/or shell windows:
1342     root = Tk(className="Idle")
1343     fixwordbreaks(root)
1344     root.withdraw()
1345     flist = PyShellFileList(root)
1346     if enable_edit:
1347         if not (cmd or script):
1348             for filename in args:
1349                 flist.open(filename)
1350             if not args:
1351                 flist.new()
1352     if enable_shell:
1353         if not flist.open_shell():
1354             return # couldn't open shell
1355     shell = flist.pyshell
1356     # handle remaining options:
1357     if debug:
1358         shell.open_debugger()
1359     if startup:
1360         filename = os.environ.get("IDLESTARTUP") or \
1361                    os.environ.get("PYTHONSTARTUP")
1362         if filename and os.path.isfile(filename):
1363             shell.interp.execfile(filename)
1364     if shell and cmd or script:
1365         shell.interp.runcommand("""if 1:
1366             import sys as _sys
1367             _sys.argv = %r
1368             del _sys
1369             \n""" % (sys.argv,))
1370         if cmd:
1371             shell.interp.execsource(cmd)
1372         elif script:
1373             shell.interp.prepend_syspath(script)
1374             shell.interp.execfile(script)
1375     root.mainloop()
1376     root.destroy()
1377 
1378 if __name__ == "__main__":
1379     sys.modules['PyShell'] = sys.modules['__main__']
1380     main()
1381 

Generated by PyXR 0.9.4
SourceForge.net Logo