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