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