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