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