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