PyShell.py revision 931237e2e66975c54e2ac6c5e503ee2992a22bcf
1#! /usr/bin/env python 2 3import os 4import os.path 5import sys 6import string 7import getopt 8import re 9import socket 10import time 11import threading 12import traceback 13import types 14import exceptions 15 16import linecache 17from code import InteractiveInterpreter 18 19from Tkinter import * 20import tkMessageBox 21 22from EditorWindow import EditorWindow, fixwordbreaks 23from FileList import FileList 24from ColorDelegator import ColorDelegator 25from UndoDelegator import UndoDelegator 26from OutputWindow import OutputWindow 27from configHandler import idleConf 28import idlever 29 30import rpc 31import Debugger 32import RemoteDebugger 33 34IDENTCHARS = string.ascii_letters + string.digits + "_" 35LOCALHOST = '127.0.0.1' 36 37try: 38 from signal import SIGTERM 39except ImportError: 40 SIGTERM = 15 41 42# Change warnings module to write to sys.__stderr__ 43try: 44 import warnings 45except ImportError: 46 pass 47else: 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 52 53def extended_linecache_checkcache(orig_checkcache=linecache.checkcache): 54 """Extend linecache.checkcache to preserve the <pyshell#...> entries 55 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. 61 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) 70 71# Patch linecache.checkcache(): 72linecache.checkcache = extended_linecache_checkcache 73 74 75class PyShellEditorWindow(EditorWindow): 76 "Regular text edit window when a shell is present" 77 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) 84 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) 94 95 rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"), 96 ("Clear Breakpoint", "<<clear-breakpoint-here>>")] 97 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 111 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) 120 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 139 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 154 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. 166 # 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() 193 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) 206 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 213 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 223 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) 230 231 def _close(self): 232 "Extend base method - clear breaks when module is closed" 233 self.clear_file_breaks() 234 EditorWindow._close(self) 235 236 237class PyShellFileList(FileList): 238 "Extend base class: file list when a shell is present" 239 240 EditorWindow = PyShellEditorWindow 241 242 pyshell = None 243 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 251 252 253class ModifiedColorDelegator(ColorDelegator): 254 "Extend base class: colorizer for the shell window itself" 255 256 def __init__(self): 257 ColorDelegator.__init__(self) 258 self.LoadTagDefs() 259 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) 264 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"), 274 }) 275 276class ModifiedUndoDelegator(UndoDelegator): 277 "Extend base class: forbid insert/delete before the I/O mark" 278 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) 287 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) 296 297 298class MyRPCClient(rpc.RPCClient): 299 300 def handle_EOF(self): 301 "Override the base class - just re-raise EOFError" 302 raise EOFError 303 304 305class ModifiedInterpreter(InteractiveInterpreter): 306 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() 314 315 port = 8833 316 rpcclt = None 317 rpcpid = None 318 319 def spawn_subprocess(self): 320 args = self.subprocess_arglist 321 self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args) 322 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 return [sys.executable] + w + ["-c", command, str(self.port)] 335 336 def start_subprocess(self): 337 addr = (LOCALHOST, self.port) 338 # Idle starts listening for connection on localhost 339 for i in range(3): 340 time.sleep(i) 341 try: 342 self.rpcclt = MyRPCClient(addr) 343 break 344 except socket.error, err: 345 print>>sys.__stderr__,"IDLE socket error: " + err[1]\ 346 + ", retrying..." 347 else: 348 display_port_binding_error() 349 sys.exit() 350 self.spawn_subprocess() 351 # Accept the connection from the Python execution server 352 self.rpcclt.accept() 353 self.rpcclt.register("stdin", self.tkconsole) 354 self.rpcclt.register("stdout", self.tkconsole.stdout) 355 self.rpcclt.register("stderr", self.tkconsole.stderr) 356 self.rpcclt.register("flist", self.tkconsole.flist) 357 self.rpcclt.register("linecache", linecache) 358 self.rpcclt.register("interp", self) 359 self.transfer_path() 360 self.poll_subprocess() 361 362 def restart_subprocess(self): 363 if self.restarting: 364 return 365 self.restarting = True 366 # close only the subprocess debugger 367 debug = self.getdebugger() 368 if debug: 369 try: 370 # Only close subprocess debugger, don't unregister gui_adap! 371 RemoteDebugger.close_subprocess_debugger(self.rpcclt) 372 except: 373 pass 374 # Kill subprocess, spawn a new one, accept connection. 375 self.rpcclt.close() 376 self.unix_terminate() 377 console = self.tkconsole 378 was_executing = console.executing 379 console.executing = False 380 self.spawn_subprocess() 381 self.rpcclt.accept() 382 self.transfer_path() 383 # annotate restart in shell window and mark it 384 console.text.delete("iomark", "end-1c") 385 if was_executing: 386 console.write('\n') 387 console.showprompt() 388 halfbar = ((int(console.width) - 16) // 2) * '=' 389 console.write(halfbar + ' RESTART ' + halfbar) 390 console.text.mark_set("restart", "end-1c") 391 console.text.mark_gravity("restart", "left") 392 console.showprompt() 393 # restart subprocess debugger 394 if debug: 395 # Restarted debugger connects to current instance of debug GUI 396 gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt) 397 # reload remote debugger breakpoints for all PyShellEditWindows 398 debug.load_breakpoints() 399 self.restarting = False 400 401 def __request_interrupt(self): 402 self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) 403 404 def interrupt_subprocess(self): 405 threading.Thread(target=self.__request_interrupt).start() 406 407 def kill_subprocess(self): 408 self.rpcclt.close() 409 self.unix_terminate() 410 self.tkconsole.executing = False 411 self.rpcclt = None 412 413 def unix_terminate(self): 414 "UNIX: make sure subprocess is terminated and collect status" 415 if hasattr(os, 'kill'): 416 try: 417 os.kill(self.rpcpid, SIGTERM) 418 except OSError: 419 # process already terminated: 420 return 421 else: 422 try: 423 os.waitpid(self.rpcpid, 0) 424 except OSError: 425 return 426 427 def transfer_path(self): 428 self.runcommand("""if 1: 429 import sys as _sys 430 _sys.path = %s 431 del _sys 432 _msg = 'Use File/Exit or your end-of-file key to quit IDLE' 433 __builtins__.quit = __builtins__.exit = _msg 434 del _msg 435 \n""" % `sys.path`) 436 437 active_seq = None 438 439 def poll_subprocess(self): 440 clt = self.rpcclt 441 if clt is None: 442 return 443 try: 444 response = clt.pollresponse(self.active_seq, wait=0.05) 445 except (EOFError, IOError, KeyboardInterrupt): 446 # lost connection or subprocess terminated itself, restart 447 # [the KBI is from rpc.SocketIO.handle_EOF()] 448 if self.tkconsole.closing: 449 return 450 response = None 451 self.restart_subprocess() 452 if response: 453 self.tkconsole.resetoutput() 454 self.active_seq = None 455 how, what = response 456 console = self.tkconsole.console 457 if how == "OK": 458 if what is not None: 459 print >>console, `what` 460 elif how == "EXCEPTION": 461 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 462 self.remote_stack_viewer() 463 elif how == "ERROR": 464 errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n" 465 print >>sys.__stderr__, errmsg, what 466 print >>console, errmsg, what 467 # we received a response to the currently active seq number: 468 self.tkconsole.endexecuting() 469 # Reschedule myself 470 if not self.tkconsole.closing: 471 self.tkconsole.text.after(self.tkconsole.pollinterval, 472 self.poll_subprocess) 473 474 debugger = None 475 476 def setdebugger(self, debugger): 477 self.debugger = debugger 478 479 def getdebugger(self): 480 return self.debugger 481 482 def open_remote_stack_viewer(self): 483 """Initiate the remote stack viewer from a separate thread. 484 485 This method is called from the subprocess, and by returning from this 486 method we allow the subprocess to unblock. After a bit the shell 487 requests the subprocess to open the remote stack viewer which returns a 488 static object looking at the last exceptiopn. It is queried through 489 the RPC mechanism. 490 491 """ 492 self.tkconsole.text.after(300, self.remote_stack_viewer) 493 return 494 495 def remote_stack_viewer(self): 496 import RemoteObjectBrowser 497 oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) 498 if oid is None: 499 self.tkconsole.root.bell() 500 return 501 item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid) 502 from TreeWidget import ScrolledCanvas, TreeNode 503 top = Toplevel(self.tkconsole.root) 504 sc = ScrolledCanvas(top, bg="white", highlightthickness=0) 505 sc.frame.pack(expand=1, fill="both") 506 node = TreeNode(sc.canvas, None, item) 507 node.expand() 508 # XXX Should GC the remote tree when closing the window 509 510 gid = 0 511 512 def execsource(self, source): 513 "Like runsource() but assumes complete exec source" 514 filename = self.stuffsource(source) 515 self.execfile(filename, source) 516 517 def execfile(self, filename, source=None): 518 "Execute an existing file" 519 if source is None: 520 source = open(filename, "r").read() 521 try: 522 code = compile(source, filename, "exec") 523 except (OverflowError, SyntaxError): 524 self.tkconsole.resetoutput() 525 tkerr = self.tkconsole.stderr 526 print>>tkerr, '*** Error in script or command!\n' 527 print>>tkerr, 'Traceback (most recent call last):' 528 InteractiveInterpreter.showsyntaxerror(self, filename) 529 self.tkconsole.showprompt() 530 else: 531 self.runcode(code) 532 533 def runsource(self, source): 534 "Extend base class method: Stuff the source in the line cache first" 535 filename = self.stuffsource(source) 536 self.more = 0 537 self.save_warnings_filters = warnings.filters[:] 538 warnings.filterwarnings(action="error", category=SyntaxWarning) 539 if isinstance(source, types.UnicodeType): 540 import IOBinding 541 try: 542 source = source.encode(IOBinding.encoding) 543 except UnicodeError: 544 self.tkconsole.resetoutput() 545 self.write("Unsupported characters in input") 546 return 547 try: 548 return InteractiveInterpreter.runsource(self, source, filename) 549 finally: 550 if self.save_warnings_filters is not None: 551 warnings.filters[:] = self.save_warnings_filters 552 self.save_warnings_filters = None 553 554 def stuffsource(self, source): 555 "Stuff source in the filename cache" 556 filename = "<pyshell#%d>" % self.gid 557 self.gid = self.gid + 1 558 lines = source.split("\n") 559 linecache.cache[filename] = len(source)+1, 0, lines, filename 560 return filename 561 562 def prepend_syspath(self, filename): 563 "Prepend sys.path with file's directory if not already included" 564 self.runcommand("""if 1: 565 _filename = %s 566 import sys as _sys 567 from os.path import dirname as _dirname 568 _dir = _dirname(_filename) 569 if not _dir in _sys.path: 570 _sys.path.insert(0, _dir) 571 del _filename, _sys, _dirname, _dir 572 \n""" % `filename`) 573 574 def showsyntaxerror(self, filename=None): 575 """Extend base class method: Add Colorizing 576 577 Color the offending position instead of printing it and pointing at it 578 with a caret. 579 580 """ 581 text = self.tkconsole.text 582 stuff = self.unpackerror() 583 if stuff: 584 msg, lineno, offset, line = stuff 585 if lineno == 1: 586 pos = "iomark + %d chars" % (offset-1) 587 else: 588 pos = "iomark linestart + %d lines + %d chars" % \ 589 (lineno-1, offset-1) 590 text.tag_add("ERROR", pos) 591 text.see(pos) 592 char = text.get(pos) 593 if char and char in IDENTCHARS: 594 text.tag_add("ERROR", pos + " wordstart", pos) 595 self.tkconsole.resetoutput() 596 self.write("SyntaxError: %s\n" % str(msg)) 597 else: 598 self.tkconsole.resetoutput() 599 InteractiveInterpreter.showsyntaxerror(self, filename) 600 self.tkconsole.showprompt() 601 602 def unpackerror(self): 603 type, value, tb = sys.exc_info() 604 ok = type is SyntaxError 605 if ok: 606 try: 607 msg, (dummy_filename, lineno, offset, line) = value 608 if not offset: 609 offset = 0 610 except: 611 ok = 0 612 if ok: 613 return msg, lineno, offset, line 614 else: 615 return None 616 617 def showtraceback(self): 618 "Extend base class method to reset output properly" 619 self.tkconsole.resetoutput() 620 self.checklinecache() 621 InteractiveInterpreter.showtraceback(self) 622 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 623 self.tkconsole.open_stack_viewer() 624 625 def checklinecache(self): 626 c = linecache.cache 627 for key in c.keys(): 628 if key[:1] + key[-1:] != "<>": 629 del c[key] 630 631 def display_executing_dialog(self): 632 tkMessageBox.showerror( 633 "Already executing", 634 "The Python Shell window is already executing a command; " 635 "please wait until it is finished.", 636 master=self.tkconsole.text) 637 638 def runcommand(self, code): 639 "Run the code without invoking the debugger" 640 # The code better not raise an exception! 641 if self.tkconsole.executing: 642 self.display_executing_dialog() 643 return 0 644 if self.rpcclt: 645 self.rpcclt.remotequeue("exec", "runcode", (code,), {}) 646 else: 647 exec code in self.locals 648 return 1 649 650 def runcode(self, code): 651 "Override base class method" 652 if self.tkconsole.executing: 653 self.interp.restart_subprocess() 654 self.checklinecache() 655 if self.save_warnings_filters is not None: 656 warnings.filters[:] = self.save_warnings_filters 657 self.save_warnings_filters = None 658 debugger = self.debugger 659 try: 660 self.tkconsole.beginexecuting() 661 try: 662 if not debugger and self.rpcclt is not None: 663 self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", 664 (code,), {}) 665 elif debugger: 666 debugger.run(code, self.locals) 667 else: 668 exec code in self.locals 669 except SystemExit: 670 if tkMessageBox.askyesno( 671 "Exit?", 672 "Do you want to exit altogether?", 673 default="yes", 674 master=self.tkconsole.text): 675 raise 676 else: 677 self.showtraceback() 678 except: 679 self.showtraceback() 680 finally: 681 if not use_subprocess: 682 self.tkconsole.endexecuting() 683 684 def write(self, s): 685 "Override base class method" 686 self.tkconsole.stderr.write(s) 687 688class PyShell(OutputWindow): 689 690 shell_title = "Python Shell" 691 692 # Override classes 693 ColorDelegator = ModifiedColorDelegator 694 UndoDelegator = ModifiedUndoDelegator 695 696 # Override menus 697 menu_specs = [ 698 ("file", "_File"), 699 ("edit", "_Edit"), 700 ("debug", "_Debug"), 701 ("options", "_Options"), 702 ("windows", "_Windows"), 703 ("help", "_Help"), 704 ] 705 706 # New classes 707 from IdleHistory import History 708 709 def __init__(self, flist=None): 710 if use_subprocess: 711 ms = self.menu_specs 712 if ms[2][0] != "shell": 713 ms.insert(2, ("shell", "_Shell")) 714 self.interp = ModifiedInterpreter(self) 715 if flist is None: 716 root = Tk() 717 fixwordbreaks(root) 718 root.withdraw() 719 flist = PyShellFileList(root) 720 # 721 OutputWindow.__init__(self, flist, None, None) 722 # 723 import __builtin__ 724 __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D." 725 # 726 self.config(usetabs=1, indentwidth=8, context_use_ps1=1) 727 # 728 text = self.text 729 text.configure(wrap="char") 730 text.bind("<<newline-and-indent>>", self.enter_callback) 731 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback) 732 text.bind("<<interrupt-execution>>", self.cancel_callback) 733 text.bind("<<beginning-of-line>>", self.home_callback) 734 text.bind("<<end-of-file>>", self.eof_callback) 735 text.bind("<<open-stack-viewer>>", self.open_stack_viewer) 736 text.bind("<<toggle-debugger>>", self.toggle_debugger) 737 text.bind("<<open-python-shell>>", self.flist.open_shell) 738 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer) 739 if use_subprocess: 740 text.bind("<<view-restart>>", self.view_restart_mark) 741 text.bind("<<restart-shell>>", self.restart_shell) 742 # 743 self.save_stdout = sys.stdout 744 self.save_stderr = sys.stderr 745 self.save_stdin = sys.stdin 746 import IOBinding 747 self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) 748 self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) 749 self.console = PseudoFile(self, "console", IOBinding.encoding) 750 if not use_subprocess: 751 sys.stdout = self.stdout 752 sys.stderr = self.stderr 753 sys.stdin = self 754 # 755 self.history = self.History(self.text) 756 # 757 self.pollinterval = 50 # millisec 758 if use_subprocess: 759 self.interp.start_subprocess() 760 761 reading = False 762 executing = False 763 canceled = False 764 endoffile = False 765 closing = False 766 767 def toggle_debugger(self, event=None): 768 if self.executing: 769 tkMessageBox.showerror("Don't debug now", 770 "You can only toggle the debugger when idle", 771 master=self.text) 772 self.set_debugger_indicator() 773 return "break" 774 else: 775 db = self.interp.getdebugger() 776 if db: 777 self.close_debugger() 778 else: 779 self.open_debugger() 780 781 def set_debugger_indicator(self): 782 db = self.interp.getdebugger() 783 self.setvar("<<toggle-debugger>>", not not db) 784 785 def toggle_jit_stack_viewer(self, event=None): 786 pass # All we need is the variable 787 788 def close_debugger(self): 789 db = self.interp.getdebugger() 790 if db: 791 self.interp.setdebugger(None) 792 db.close() 793 if self.interp.rpcclt: 794 RemoteDebugger.close_remote_debugger(self.interp.rpcclt) 795 self.resetoutput() 796 self.console.write("[DEBUG OFF]\n") 797 sys.ps1 = ">>> " 798 self.showprompt() 799 self.set_debugger_indicator() 800 801 def open_debugger(self): 802 if self.interp.rpcclt: 803 dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, 804 self) 805 else: 806 dbg_gui = Debugger.Debugger(self) 807 self.interp.setdebugger(dbg_gui) 808 dbg_gui.load_breakpoints() 809 sys.ps1 = "[DEBUG ON]\n>>> " 810 self.showprompt() 811 self.set_debugger_indicator() 812 813 def beginexecuting(self): 814 "Helper for ModifiedInterpreter" 815 self.resetoutput() 816 self.executing = 1 817 818 def endexecuting(self): 819 "Helper for ModifiedInterpreter" 820 self.executing = 0 821 self.canceled = 0 822 self.showprompt() 823 824 def close(self): 825 "Extend EditorWindow.close()" 826 if self.executing: 827 response = tkMessageBox.askokcancel( 828 "Kill?", 829 "The program is still running!\n Do you want to kill it?", 830 default="ok", 831 parent=self.text) 832 if response == False: 833 return "cancel" 834 self.closing = True 835 # Wait for poll_subprocess() rescheduling to stop 836 self.text.after(2 * self.pollinterval, self.close2) 837 838 def close2(self): 839 return EditorWindow.close(self) 840 841 def _close(self): 842 "Extend EditorWindow._close(), shut down debugger and execution server" 843 self.close_debugger() 844 if use_subprocess: 845 self.interp.kill_subprocess() 846 # Restore std streams 847 sys.stdout = self.save_stdout 848 sys.stderr = self.save_stderr 849 sys.stdin = self.save_stdin 850 # Break cycles 851 self.interp = None 852 self.console = None 853 self.flist.pyshell = None 854 self.history = None 855 EditorWindow._close(self) 856 857 def ispythonsource(self, filename): 858 "Override EditorWindow method: never remove the colorizer" 859 return True 860 861 def short_title(self): 862 return self.shell_title 863 864 COPYRIGHT = \ 865 'Type "copyright", "credits" or "license()" for more information.' 866 867 def begin(self): 868 self.resetoutput() 869 if use_subprocess: 870 nosub = '' 871 else: 872 nosub = "==== No Subprocess ====" 873 self.write("Python %s on %s\n%s\nIDLE %s %s\n" % 874 (sys.version, sys.platform, self.COPYRIGHT, 875 idlever.IDLE_VERSION, nosub)) 876 self.showprompt() 877 import Tkinter 878 Tkinter._default_root = None 879 880 def interact(self): 881 self.begin() 882 self.top.mainloop() 883 884 def readline(self): 885 save = self.reading 886 try: 887 self.reading = 1 888 self.top.mainloop() 889 finally: 890 self.reading = save 891 line = self.text.get("iomark", "end-1c") 892 if isinstance(line, unicode): 893 import IOBinding 894 try: 895 line = line.encode(IOBinding.encoding) 896 except UnicodeError: 897 pass 898 self.resetoutput() 899 if self.canceled: 900 self.canceled = 0 901 raise KeyboardInterrupt 902 if self.endoffile: 903 self.endoffile = 0 904 return "" 905 return line 906 907 def isatty(self): 908 return True 909 910 def cancel_callback(self, event=None): 911 try: 912 if self.text.compare("sel.first", "!=", "sel.last"): 913 return # Active selection -- always use default binding 914 except: 915 pass 916 if not (self.executing or self.reading): 917 self.resetoutput() 918 self.interp.write("KeyboardInterrupt\n") 919 self.showprompt() 920 return "break" 921 self.endoffile = 0 922 self.canceled = 1 923 if self.reading: 924 self.top.quit() 925 elif (self.executing and self.interp.rpcclt): 926 if self.interp.getdebugger(): 927 self.interp.restart_subprocess() 928 else: 929 self.interp.interrupt_subprocess() 930 return "break" 931 932 def eof_callback(self, event): 933 if self.executing and not self.reading: 934 return # Let the default binding (delete next char) take over 935 if not (self.text.compare("iomark", "==", "insert") and 936 self.text.compare("insert", "==", "end-1c")): 937 return # Let the default binding (delete next char) take over 938 if not self.executing: 939 self.resetoutput() 940 self.close() 941 else: 942 self.canceled = 0 943 self.endoffile = 1 944 self.top.quit() 945 return "break" 946 947 def home_callback(self, event): 948 if event.state != 0 and event.keysym == "Home": 949 return # <Modifier-Home>; fall back to class binding 950 if self.text.compare("iomark", "<=", "insert") and \ 951 self.text.compare("insert linestart", "<=", "iomark"): 952 self.text.mark_set("insert", "iomark") 953 self.text.tag_remove("sel", "1.0", "end") 954 self.text.see("insert") 955 return "break" 956 957 def linefeed_callback(self, event): 958 # Insert a linefeed without entering anything (still autoindented) 959 if self.reading: 960 self.text.insert("insert", "\n") 961 self.text.see("insert") 962 else: 963 self.newline_and_indent_event(event) 964 return "break" 965 966 def enter_callback(self, event): 967 if self.executing and not self.reading: 968 return # Let the default binding (insert '\n') take over 969 # If some text is selected, recall the selection 970 # (but only if this before the I/O mark) 971 try: 972 sel = self.text.get("sel.first", "sel.last") 973 if sel: 974 if self.text.compare("sel.last", "<=", "iomark"): 975 self.recall(sel) 976 return "break" 977 except: 978 pass 979 # If we're strictly before the line containing iomark, recall 980 # the current line, less a leading prompt, less leading or 981 # trailing whitespace 982 if self.text.compare("insert", "<", "iomark linestart"): 983 # Check if there's a relevant stdin range -- if so, use it 984 prev = self.text.tag_prevrange("stdin", "insert") 985 if prev and self.text.compare("insert", "<", prev[1]): 986 self.recall(self.text.get(prev[0], prev[1])) 987 return "break" 988 next = self.text.tag_nextrange("stdin", "insert") 989 if next and self.text.compare("insert lineend", ">=", next[0]): 990 self.recall(self.text.get(next[0], next[1])) 991 return "break" 992 # No stdin mark -- just get the current line, less any prompt 993 line = self.text.get("insert linestart", "insert lineend") 994 last_line_of_prompt = sys.ps1.split('\n')[-1] 995 if line.startswith(last_line_of_prompt): 996 line = line[len(last_line_of_prompt):] 997 self.recall(line) 998 return "break" 999 # If we're between the beginning of the line and the iomark, i.e. 1000 # in the prompt area, move to the end of the prompt 1001 if self.text.compare("insert", "<", "iomark"): 1002 self.text.mark_set("insert", "iomark") 1003 # If we're in the current input and there's only whitespace 1004 # beyond the cursor, erase that whitespace first 1005 s = self.text.get("insert", "end-1c") 1006 if s and not s.strip(): 1007 self.text.delete("insert", "end-1c") 1008 # If we're in the current input before its last line, 1009 # insert a newline right at the insert point 1010 if self.text.compare("insert", "<", "end-1c linestart"): 1011 self.newline_and_indent_event(event) 1012 return "break" 1013 # We're in the last line; append a newline and submit it 1014 self.text.mark_set("insert", "end-1c") 1015 if self.reading: 1016 self.text.insert("insert", "\n") 1017 self.text.see("insert") 1018 else: 1019 self.newline_and_indent_event(event) 1020 self.text.tag_add("stdin", "iomark", "end-1c") 1021 self.text.update_idletasks() 1022 if self.reading: 1023 self.top.quit() # Break out of recursive mainloop() in raw_input() 1024 else: 1025 self.runit() 1026 return "break" 1027 1028 def recall(self, s): 1029 if self.history: 1030 self.history.recall(s) 1031 1032 def runit(self): 1033 line = self.text.get("iomark", "end-1c") 1034 # Strip off last newline and surrounding whitespace. 1035 # (To allow you to hit return twice to end a statement.) 1036 i = len(line) 1037 while i > 0 and line[i-1] in " \t": 1038 i = i-1 1039 if i > 0 and line[i-1] == "\n": 1040 i = i-1 1041 while i > 0 and line[i-1] in " \t": 1042 i = i-1 1043 line = line[:i] 1044 more = self.interp.runsource(line) 1045 1046 def open_stack_viewer(self, event=None): 1047 if self.interp.rpcclt: 1048 return self.interp.remote_stack_viewer() 1049 try: 1050 sys.last_traceback 1051 except: 1052 tkMessageBox.showerror("No stack trace", 1053 "There is no stack trace yet.\n" 1054 "(sys.last_traceback is not defined)", 1055 master=self.text) 1056 return 1057 from StackViewer import StackBrowser 1058 sv = StackBrowser(self.root, self.flist) 1059 1060 def view_restart_mark(self, event=None): 1061 self.text.see("iomark") 1062 self.text.see("restart") 1063 1064 def restart_shell(self, event=None): 1065 self.interp.restart_subprocess() 1066 1067 def showprompt(self): 1068 self.resetoutput() 1069 try: 1070 s = str(sys.ps1) 1071 except: 1072 s = "" 1073 self.console.write(s) 1074 self.text.mark_set("insert", "end-1c") 1075 self.set_line_and_column() 1076 self.io.reset_undo() 1077 1078 def resetoutput(self): 1079 source = self.text.get("iomark", "end-1c") 1080 if self.history: 1081 self.history.history_store(source) 1082 if self.text.get("end-2c") != "\n": 1083 self.text.insert("end-1c", "\n") 1084 self.text.mark_set("iomark", "end-1c") 1085 self.set_line_and_column() 1086 sys.stdout.softspace = 0 1087 1088 def write(self, s, tags=()): 1089 try: 1090 self.text.mark_gravity("iomark", "right") 1091 OutputWindow.write(self, s, tags, "iomark") 1092 self.text.mark_gravity("iomark", "left") 1093 except: 1094 pass 1095 if self.canceled: 1096 self.canceled = 0 1097 if not use_subprocess: 1098 raise KeyboardInterrupt 1099 1100class PseudoFile: 1101 1102 def __init__(self, shell, tags, encoding=None): 1103 self.shell = shell 1104 self.tags = tags 1105 self.softspace = 0 1106 self.encoding = encoding 1107 1108 def write(self, s): 1109 self.shell.write(s, self.tags) 1110 1111 def writelines(self, l): 1112 map(self.write, l) 1113 1114 def flush(self): 1115 pass 1116 1117 def isatty(self): 1118 return True 1119 1120 1121usage_msg = """\ 1122 1123USAGE: idle [-deins] [-t title] [file]* 1124 idle [-dns] [-t title] (-c cmd | -r file) [arg]* 1125 idle [-dns] [-t title] - [arg]* 1126 1127 -h print this help message and exit 1128 -n run IDLE without a subprocess (see Help/IDLE Help for details) 1129 1130The following options will override the IDLE 'settings' configuration: 1131 1132 -e open an edit window 1133 -i open a shell window 1134 1135The following options imply -i and will open a shell: 1136 1137 -c cmd run the command in a shell, or 1138 -r file run script from file 1139 1140 -d enable the debugger 1141 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else 1142 -t title set title of shell window 1143 1144A default edit window will be bypassed when -c, -r, or - are used. 1145 1146[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. 1147 1148Examples: 1149 1150idle 1151 Open an edit window or shell depending on IDLE's configuration. 1152 1153idle foo.py foobar.py 1154 Edit the files, also open a shell if configured to start with shell. 1155 1156idle -est "Baz" foo.py 1157 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell 1158 window with the title "Baz". 1159 1160idle -c "import sys; print sys.argv" "foo" 1161 Open a shell window and run the command, passing "-c" in sys.argv[0] 1162 and "foo" in sys.argv[1]. 1163 1164idle -d -s -r foo.py "Hello World" 1165 Open a shell window, run a startup script, enable the debugger, and 1166 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in 1167 sys.argv[1]. 1168 1169echo "import sys; print sys.argv" | idle - "foobar" 1170 Open a shell window, run the script piped in, passing '' in sys.argv[0] 1171 and "foobar" in sys.argv[1]. 1172""" 1173 1174def main(): 1175 global flist, root, use_subprocess 1176 1177 use_subprocess = True 1178 enable_shell = False 1179 enable_edit = False 1180 debug = False 1181 cmd = None 1182 script = None 1183 startup = False 1184 try: 1185 sys.ps1 1186 except AttributeError: 1187 sys.ps1 = '>>> ' 1188 try: 1189 opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") 1190 except getopt.error, msg: 1191 sys.stderr.write("Error: %s\n" % str(msg)) 1192 sys.stderr.write(usage_msg) 1193 sys.exit(2) 1194 for o, a in opts: 1195 if o == '-c': 1196 cmd = a 1197 enable_shell = True 1198 if o == '-d': 1199 debug = True 1200 enable_shell = True 1201 if o == '-e': 1202 enable_edit = True 1203 if o == '-h': 1204 sys.stdout.write(usage_msg) 1205 sys.exit() 1206 if o == '-i': 1207 enable_shell = True 1208 if o == '-n': 1209 use_subprocess = False 1210 if o == '-r': 1211 script = a 1212 if os.path.isfile(script): 1213 pass 1214 else: 1215 print "No script file: ", script 1216 sys.exit() 1217 enable_shell = True 1218 if o == '-s': 1219 startup = True 1220 enable_shell = True 1221 if o == '-t': 1222 PyShell.shell_title = a 1223 enable_shell = True 1224 if args and args[0] == '-': 1225 cmd = sys.stdin.read() 1226 enable_shell = True 1227 # process sys.argv and sys.path: 1228 for i in range(len(sys.path)): 1229 sys.path[i] = os.path.abspath(sys.path[i]) 1230 if args and args[0] == '-': 1231 sys.argv = [''] + args[1:] 1232 elif cmd: 1233 sys.argv = ['-c'] + args 1234 elif script: 1235 sys.argv = [script] + args 1236 elif args: 1237 enable_edit = True 1238 pathx = [] 1239 for filename in args: 1240 pathx.append(os.path.dirname(filename)) 1241 for dir in pathx: 1242 dir = os.path.abspath(dir) 1243 if not dir in sys.path: 1244 sys.path.insert(0, dir) 1245 else: 1246 dir = os.getcwd() 1247 if not dir in sys.path: 1248 sys.path.insert(0, dir) 1249 # check the IDLE settings configuration (but command line overrides) 1250 edit_start = idleConf.GetOption('main', 'General', 1251 'editor-on-startup', type='bool') 1252 enable_edit = enable_edit or edit_start 1253 enable_shell = enable_shell or not edit_start 1254 # start editor and/or shell windows: 1255 root = Tk(className="Idle") 1256 fixwordbreaks(root) 1257 root.withdraw() 1258 flist = PyShellFileList(root) 1259 if enable_edit: 1260 if not (cmd or script): 1261 for filename in args: 1262 flist.open(filename) 1263 if not args: 1264 flist.new() 1265 if enable_shell: 1266 flist.open_shell() 1267 elif enable_shell: 1268 flist.pyshell = PyShell(flist) 1269 flist.pyshell.begin() 1270 shell = flist.pyshell 1271 # handle remaining options: 1272 if debug: 1273 shell.open_debugger() 1274 if startup: 1275 filename = os.environ.get("IDLESTARTUP") or \ 1276 os.environ.get("PYTHONSTARTUP") 1277 if filename and os.path.isfile(filename): 1278 shell.interp.execfile(filename) 1279 if cmd or script: 1280 shell.interp.runcommand("""if 1: 1281 import sys as _sys 1282 _sys.argv = %s 1283 del _sys 1284 \n""" % `sys.argv`) 1285 if cmd: 1286 shell.interp.execsource(cmd) 1287 elif script: 1288 shell.interp.prepend_syspath(script) 1289 shell.interp.execfile(script) 1290 root.mainloop() 1291 root.destroy() 1292 1293 1294def display_port_binding_error(): 1295 print """\ 1296\nIDLE cannot run. 1297 1298IDLE needs to use a specific TCP/IP port (8833) in order to communicate with 1299its Python execution server. IDLE is unable to bind to this port, and so 1300cannot start. Here are some possible causes of this problem: 1301 1302 1. TCP/IP networking is not installed or not working on this computer 1303 2. Another program (another IDLE?) is running that uses this port 1304 3. Personal firewall software is preventing IDLE from using this port 1305 1306Run IDLE with the -n command line switch to start without a subprocess 1307and refer to Help/IDLE Help "Running without a subprocess" for further 1308details. 1309""" 1310 1311if __name__ == "__main__": 1312 sys.modules['PyShell'] = sys.modules['__main__'] 1313 main() 1314