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