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