PyShell.py revision 969de458aa12e831942637bbcd9994b29dc86252
1#! /usr/bin/env python 2 3import os 4import sys 5import string 6import getopt 7import re 8import socket 9import time 10import warnings 11import traceback 12 13import linecache 14from code import InteractiveInterpreter 15 16from Tkinter import * 17import tkMessageBox 18 19from EditorWindow import EditorWindow, fixwordbreaks 20from FileList import FileList 21from ColorDelegator import ColorDelegator 22from UndoDelegator import UndoDelegator 23from OutputWindow import OutputWindow 24from configHandler import idleConf 25import idlever 26 27import rpc 28 29# XX hardwire this for now, remove later KBK 09Jun02 30use_subprocess = 1 # Set to 1 to spawn subprocess for command execution 31 32# Change warnings module to write to sys.__stderr__ 33try: 34 import warnings 35except ImportError: 36 pass 37else: 38 def idle_showwarning(message, category, filename, lineno): 39 file = sys.__stderr__ 40 file.write(warnings.formatwarning(message, category, filename, lineno)) 41 warnings.showwarning = idle_showwarning 42 43# We need to patch linecache.checkcache, because we don't want it 44# to throw away our <pyshell#...> entries. 45# Rather than repeating its code here, we save those entries, 46# then call the original function, and then restore the saved entries. 47def linecache_checkcache(orig_checkcache=linecache.checkcache): 48 cache = linecache.cache 49 save = {} 50 for filename in cache.keys(): 51 if filename[:1] + filename[-1:] == '<>': 52 save[filename] = cache[filename] 53 orig_checkcache() 54 cache.update(save) 55linecache.checkcache = linecache_checkcache 56 57 58# Note: <<newline-and-indent>> event is defined in AutoIndent.py 59 60#$ event <<plain-newline-and-indent>> 61#$ win <Control-j> 62#$ unix <Control-j> 63 64#$ event <<beginning-of-line>> 65#$ win <Control-a> 66#$ win <Home> 67#$ unix <Control-a> 68#$ unix <Home> 69 70#$ event <<history-next>> 71#$ win <Alt-n> 72#$ unix <Alt-n> 73 74#$ event <<history-previous>> 75#$ win <Alt-p> 76#$ unix <Alt-p> 77 78#$ event <<interrupt-execution>> 79#$ win <Control-c> 80#$ unix <Control-c> 81 82#$ event <<end-of-file>> 83#$ win <Control-d> 84#$ unix <Control-d> 85 86#$ event <<open-stack-viewer>> 87 88#$ event <<toggle-debugger>> 89 90 91class PyShellEditorWindow(EditorWindow): 92 93 # Regular text edit window when a shell is present 94 # XXX ought to merge with regular editor window 95 96 def __init__(self, *args): 97 apply(EditorWindow.__init__, (self,) + args) 98 self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here) 99 self.text.bind("<<open-python-shell>>", self.flist.open_shell) 100 101 rmenu_specs = [ 102 ("Set breakpoint here", "<<set-breakpoint-here>>"), 103 ] 104 105 def set_breakpoint_here(self, event=None): 106 if not self.flist.pyshell or not self.flist.pyshell.interp.debugger: 107 self.text.bell() 108 return 109 self.flist.pyshell.interp.debugger.set_breakpoint_here(self) 110 111 112class PyShellFileList(FileList): 113 114 # File list when a shell is present 115 116 EditorWindow = PyShellEditorWindow 117 118 pyshell = None 119 120 def open_shell(self, event=None): 121 if self.pyshell: 122 self.pyshell.wakeup() 123 else: 124 self.pyshell = PyShell(self) 125 self.pyshell.begin() 126 return self.pyshell 127 128 129class ModifiedColorDelegator(ColorDelegator): 130 131 # Colorizer for the shell window itself 132 133 def __init__(self): 134 ColorDelegator.__init__(self) 135 self.LoadTagDefs() 136 137 def recolorize_main(self): 138 self.tag_remove("TODO", "1.0", "iomark") 139 self.tag_add("SYNC", "1.0", "iomark") 140 ColorDelegator.recolorize_main(self) 141 142 def LoadTagDefs(self): 143 ColorDelegator.LoadTagDefs(self) 144 theme = idleConf.GetOption('main','Theme','name') 145 self.tagdefs.update({ 146 "stdin": {'background':None,'foreground':None}, 147 "stdout": idleConf.GetHighlight(theme, "stdout"), 148 "stderr": idleConf.GetHighlight(theme, "stderr"), 149 "console": idleConf.GetHighlight(theme, "console"), 150 "ERROR": idleConf.GetHighlight(theme, "error"), 151 None: idleConf.GetHighlight(theme, "normal"), 152 }) 153 154class ModifiedUndoDelegator(UndoDelegator): 155 156 # Forbid insert/delete before the I/O mark 157 158 def insert(self, index, chars, tags=None): 159 try: 160 if self.delegate.compare(index, "<", "iomark"): 161 self.delegate.bell() 162 return 163 except TclError: 164 pass 165 UndoDelegator.insert(self, index, chars, tags) 166 167 def delete(self, index1, index2=None): 168 try: 169 if self.delegate.compare(index1, "<", "iomark"): 170 self.delegate.bell() 171 return 172 except TclError: 173 pass 174 UndoDelegator.delete(self, index1, index2) 175 176class ModifiedInterpreter(InteractiveInterpreter): 177 178 def __init__(self, tkconsole): 179 self.tkconsole = tkconsole 180 locals = sys.modules['__main__'].__dict__ 181 InteractiveInterpreter.__init__(self, locals=locals) 182 self.save_warnings_filters = None 183 184 rpcclt = None 185 rpcpid = None 186 187 def spawn_subprocess(self): 188 port = 8833 189 addr = ("localhost", port) 190 w = ['-W' + s for s in sys.warnoptions] 191 args = [sys.executable] + w + ["-c", "__import__('run').main()", 192 str(port)] 193 self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args) 194 for i in range(5): 195 time.sleep(i) 196 try: 197 self.rpcclt = rpc.RPCClient(addr) 198 break 199 except socket.error, err: 200 if i > 3: 201 print >>sys.__stderr__, "Socket error:", err, "; retry..." 202 else: 203 # XXX Make this a dialog? #GvR 204 print >>sys.__stderr__, "Can't spawn subprocess!" 205 # XXX Add Stephen's error msg, resolve the two later... KBK 09Jun02 206 display_port_binding_error() 207 return 208 self.rpcclt.register("stdin", self.tkconsole) 209 self.rpcclt.register("stdout", self.tkconsole.stdout) 210 self.rpcclt.register("stderr", self.tkconsole.stderr) 211 self.rpcclt.register("flist", self.tkconsole.flist) 212 self.poll_subprocess() 213 214 active_seq = None 215 216 def poll_subprocess(self): 217 clt = self.rpcclt 218 if clt is None: 219 return 220 response = clt.pollresponse(self.active_seq) 221 self.tkconsole.text.after(50, self.poll_subprocess) 222 if response: 223 self.tkconsole.resetoutput() 224 self.active_seq = None 225 how, what = response 226 file = self.tkconsole.console 227 if how == "OK": 228 if what is not None: 229 print >>file, `what` 230 elif how == "EXCEPTION": 231 mod, name, args, tb = what 232 print >>file, 'Traceback (most recent call last):' 233 while tb and tb[0][0] in ("run.py", "rpc.py"): 234 del tb[0] 235 while tb and tb[-1][0] in ("run.py", "rpc.py"): 236 del tb[-1] 237 for i in range(len(tb)): 238 fn, ln, nm, line = tb[i] 239 if not line and fn.startswith("<pyshell#"): 240 line = linecache.getline(fn, ln) 241 tb[i] = fn, ln, nm, line 242 traceback.print_list(tb, file=file) 243 if mod and mod != "exceptions": 244 name = mod + "." + name 245 print >>file, name + ":", " ".join(map(str, args)) 246 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 247 self.remote_stack_viewer() 248 elif how == "ERROR": 249 print >>sys.__stderr__, "Oops:", how, what 250 print >>file, "Oops:", how, what 251 self.tkconsole.endexecuting() 252 253 def kill_subprocess(self): 254 clt = self.rpcclt 255 self.rpcclt = None 256 if clt is not None: 257 clt.close() 258 259 def remote_stack_viewer(self): 260 import RemoteObjectBrowser 261 oid = self.rpcclt.remotecall("exec", "stackviewer", ("flist",), {}) 262 if oid is None: 263 self.tkconsole.root.bell() 264 return 265 item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid) 266 from TreeWidget import ScrolledCanvas, TreeNode 267 top = Toplevel(self.tkconsole.root) 268 sc = ScrolledCanvas(top, bg="white", highlightthickness=0) 269 sc.frame.pack(expand=1, fill="both") 270 node = TreeNode(sc.canvas, None, item) 271 node.expand() 272 # XXX Should GC the remote tree when closing the window 273 274 gid = 0 275 276 def execsource(self, source): 277 # Like runsource() but assumes complete exec source 278 filename = self.stuffsource(source) 279 self.execfile(filename, source) 280 281 def execfile(self, filename, source=None): 282 # Execute an existing file 283 if source is None: 284 source = open(filename, "r").read() 285 try: 286 code = compile(source, filename, "exec") 287 except (OverflowError, SyntaxError): 288 self.tkconsole.resetoutput() 289 InteractiveInterpreter.showsyntaxerror(self, filename) 290 else: 291 self.runcode(code) 292 293 def runsource(self, source): 294 # Extend base class to stuff the source in the line cache first 295 filename = self.stuffsource(source) 296 self.more = 0 297 self.save_warnings_filters = warnings.filters[:] 298 warnings.filterwarnings(action="error", category=SyntaxWarning) 299 try: 300 return InteractiveInterpreter.runsource(self, source, filename) 301 finally: 302 if self.save_warnings_filters is not None: 303 warnings.filters[:] = self.save_warnings_filters 304 self.save_warnings_filters = None 305 306 def stuffsource(self, source): 307 # Stuff source in the filename cache 308 filename = "<pyshell#%d>" % self.gid 309 self.gid = self.gid + 1 310 lines = string.split(source, "\n") 311 linecache.cache[filename] = len(source)+1, 0, lines, filename 312 return filename 313 314 def showsyntaxerror(self, filename=None): 315 # Extend base class to color the offending position 316 # (instead of printing it and pointing at it with a caret) 317 text = self.tkconsole.text 318 stuff = self.unpackerror() 319 if not stuff: 320 self.tkconsole.resetoutput() 321 InteractiveInterpreter.showsyntaxerror(self, filename) 322 return 323 msg, lineno, offset, line = stuff 324 if lineno == 1: 325 pos = "iomark + %d chars" % (offset-1) 326 else: 327 pos = "iomark linestart + %d lines + %d chars" % (lineno-1, 328 offset-1) 329 text.tag_add("ERROR", pos) 330 text.see(pos) 331 char = text.get(pos) 332 if char and char in string.letters + string.digits + "_": 333 text.tag_add("ERROR", pos + " wordstart", pos) 334 self.tkconsole.resetoutput() 335 self.write("SyntaxError: %s\n" % str(msg)) 336 337 def unpackerror(self): 338 type, value, tb = sys.exc_info() 339 ok = type is SyntaxError 340 if ok: 341 try: 342 msg, (dummy_filename, lineno, offset, line) = value 343 except: 344 ok = 0 345 if ok: 346 return msg, lineno, offset, line 347 else: 348 return None 349 350 def showtraceback(self): 351 # Extend base class method to reset output properly 352 self.tkconsole.resetoutput() 353 self.checklinecache() 354 InteractiveInterpreter.showtraceback(self) 355 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 356 self.tkconsole.open_stack_viewer() 357 358 def checklinecache(self): 359 c = linecache.cache 360 for key in c.keys(): 361 if key[:1] + key[-1:] != "<>": 362 del c[key] 363 364 debugger = None 365 366 def setdebugger(self, debugger): 367 self.debugger = debugger 368 369 def getdebugger(self): 370 return self.debugger 371 372 def runcommand(self, code): 373 # This runs the code without invoking the debugger. 374 # The code better not raise an exception! 375 if self.tkconsole.executing: 376 tkMessageBox.showerror( 377 "Already executing", 378 "The Python Shell window is already executing a command; " 379 "please wait until it is finished.", 380 master=self.tkconsole.text) 381 return 0 382 if self.rpcclt: 383 self.rpcclt.remotecall("exec", "runcode", (code,), {}) 384 else: 385 exec code in self.locals 386 return 1 387 388 def runcode(self, code): 389 # Override base class method 390 if self.tkconsole.executing: 391 tkMessageBox.showerror( 392 "Already executing", 393 "The Python Shell window is already executing a command; " 394 "please wait until it is finished.", 395 master=self.tkconsole.text) 396 return 397 398 self.checklinecache() 399 if self.save_warnings_filters is not None: 400 warnings.filters[:] = self.save_warnings_filters 401 self.save_warnings_filters = None 402 debugger = self.debugger 403 if not debugger and self.rpcclt is not None: 404 self.tkconsole.beginexecuting() 405 self.active_seq = self.rpcclt.asynccall("exec", "runcode", 406 (code,), {}) 407 return 408 409 try: 410 self.tkconsole.beginexecuting() 411 try: 412 if debugger: 413 debugger.run(code, self.locals) 414 else: 415 exec code in self.locals 416 except SystemExit: 417 if tkMessageBox.askyesno( 418 "Exit?", 419 "Do you want to exit altogether?", 420 default="yes", 421 master=self.tkconsole.text): 422 raise 423 else: 424 self.showtraceback() 425 except: 426 self.showtraceback() 427 428 finally: 429 self.tkconsole.endexecuting() 430 431 def write(self, s): 432 # Override base class write 433 self.tkconsole.console.write(s) 434 435class PyShell(OutputWindow): 436 437 shell_title = "Python Shell" 438 439 # Override classes 440 ColorDelegator = ModifiedColorDelegator 441 UndoDelegator = ModifiedUndoDelegator 442 443 # Override menu bar specs 444 menu_specs = PyShellEditorWindow.menu_specs[:] 445 menu_specs.insert(len(menu_specs)-3, ("debug", "_Debug")) 446 447 # New classes 448 from IdleHistory import History 449 450 def __init__(self, flist=None): 451 self.interp = ModifiedInterpreter(self) 452 if flist is None: 453 root = Tk() 454 fixwordbreaks(root) 455 root.withdraw() 456 flist = PyShellFileList(root) 457 458 OutputWindow.__init__(self, flist, None, None) 459 460 import __builtin__ 461 __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D." 462 463 self.auto = self.extensions["AutoIndent"] # Required extension 464 self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1) 465 466 text = self.text 467 text.configure(wrap="char") 468 text.bind("<<newline-and-indent>>", self.enter_callback) 469 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback) 470 text.bind("<<interrupt-execution>>", self.cancel_callback) 471 text.bind("<<beginning-of-line>>", self.home_callback) 472 text.bind("<<end-of-file>>", self.eof_callback) 473 text.bind("<<open-stack-viewer>>", self.open_stack_viewer) 474 text.bind("<<toggle-debugger>>", self.toggle_debugger) 475 text.bind("<<open-python-shell>>", self.flist.open_shell) 476 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer) 477 478 self.save_stdout = sys.stdout 479 self.save_stderr = sys.stderr 480 self.save_stdin = sys.stdin 481 self.stdout = PseudoFile(self, "stdout") 482 self.stderr = PseudoFile(self, "stderr") 483 self.console = PseudoFile(self, "console") 484 if not use_subprocess: 485 sys.stdout = self.stdout 486 sys.stderr = self.stderr 487 sys.stdin = self 488 489 self.history = self.History(self.text) 490 491 if use_subprocess: 492 self.interp.spawn_subprocess() 493 494 reading = 0 495 executing = 0 496 canceled = 0 497 endoffile = 0 498 499 def toggle_debugger(self, event=None): 500 if self.executing: 501 tkMessageBox.showerror("Don't debug now", 502 "You can only toggle the debugger when idle", 503 master=self.text) 504 self.set_debugger_indicator() 505 return "break" 506 else: 507 db = self.interp.getdebugger() 508 if db: 509 self.close_debugger() 510 else: 511 self.open_debugger() 512 513 def set_debugger_indicator(self): 514 db = self.interp.getdebugger() 515 self.setvar("<<toggle-debugger>>", not not db) 516 517 def toggle_jit_stack_viewer( self, event=None): 518 pass # All we need is the variable 519 520 def close_debugger(self): 521 db = self.interp.getdebugger() 522 if db: 523 self.interp.setdebugger(None) 524 db.close() 525 self.resetoutput() 526 self.console.write("[DEBUG OFF]\n") 527 sys.ps1 = ">>> " 528 self.showprompt() 529 self.set_debugger_indicator() 530 531 def open_debugger(self): 532 if self.interp.rpcclt: 533 return self.open_remote_debugger() 534 import Debugger 535 self.interp.setdebugger(Debugger.Debugger(self)) 536 sys.ps1 = "[DEBUG ON]\n>>> " 537 self.showprompt() 538 self.set_debugger_indicator() 539 540 def open_remote_debugger(self): 541 import RemoteDebugger 542 gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt, self) 543 self.interp.setdebugger(gui) 544 sys.ps1 = "[DEBUG ON]\n>>> " 545 self.showprompt() 546 self.set_debugger_indicator() 547 548 def beginexecuting(self): 549 # Helper for ModifiedInterpreter 550 self.resetoutput() 551 self.executing = 1 552 ##self._cancel_check = self.cancel_check 553 ##sys.settrace(self._cancel_check) 554 555 def endexecuting(self): 556 # Helper for ModifiedInterpreter 557 ##sys.settrace(None) 558 ##self._cancel_check = None 559 self.executing = 0 560 self.canceled = 0 561 self.showprompt() 562 563 def close(self): 564 # Extend base class method 565 if self.executing: 566 # XXX Need to ask a question here 567 if not tkMessageBox.askokcancel( 568 "Kill?", 569 "The program is still running; do you want to kill it?", 570 default="ok", 571 master=self.text): 572 return "cancel" 573 self.canceled = 1 574 if self.reading: 575 self.top.quit() 576 return "cancel" 577 return OutputWindow.close(self) 578 579 def _close(self): 580 self.close_debugger() 581 self.interp.kill_subprocess() 582 # Restore std streams 583 sys.stdout = self.save_stdout 584 sys.stderr = self.save_stderr 585 sys.stdin = self.save_stdin 586 # Break cycles 587 self.interp = None 588 self.console = None 589 self.auto = None 590 self.flist.pyshell = None 591 self.history = None 592 OutputWindow._close(self) # Really EditorWindow._close 593 594 def ispythonsource(self, filename): 595 # Override this so EditorWindow never removes the colorizer 596 return 1 597 598 def short_title(self): 599 return self.shell_title 600 601 COPYRIGHT = \ 602 'Type "copyright", "credits" or "license" for more information.' 603 604 def begin(self): 605 self.resetoutput() 606 self.write("Python %s on %s\n%s\nGRPC IDLE Fork %s\n" % 607 (sys.version, sys.platform, self.COPYRIGHT, 608 idlever.IDLE_VERSION)) 609 try: 610 sys.ps1 611 except AttributeError: 612 sys.ps1 = ">>> " 613 self.showprompt() 614 import Tkinter 615 Tkinter._default_root = None 616 617 def interact(self): 618 self.begin() 619 self.top.mainloop() 620 621 def readline(self): 622 save = self.reading 623 try: 624 self.reading = 1 625 self.top.mainloop() 626 finally: 627 self.reading = save 628 line = self.text.get("iomark", "end-1c") 629 self.resetoutput() 630 if self.canceled: 631 self.canceled = 0 632 raise KeyboardInterrupt 633 if self.endoffile: 634 self.endoffile = 0 635 return "" 636 return line 637 638 def isatty(self): 639 return 1 640 641 def cancel_callback(self, event): 642 try: 643 if self.text.compare("sel.first", "!=", "sel.last"): 644 return # Active selection -- always use default binding 645 except: 646 pass 647 if not (self.executing or self.reading): 648 self.resetoutput() 649 self.write("KeyboardInterrupt\n") 650 self.showprompt() 651 return "break" 652 self.endoffile = 0 653 if self.reading: 654 self.canceled = 1 655 self.top.quit() 656 elif (self.executing and self.interp.rpcclt and 657 self.interp.rpcpid and hasattr(os, "kill")): 658 try: 659 from signal import SIGINT 660 except ImportError: 661 SIGINT = 2 662 os.kill(self.interp.rpcpid, SIGINT) 663 else: 664 self.canceled = 1 665 return "break" 666 667 def eof_callback(self, event): 668 if self.executing and not self.reading: 669 return # Let the default binding (delete next char) take over 670 if not (self.text.compare("iomark", "==", "insert") and 671 self.text.compare("insert", "==", "end-1c")): 672 return # Let the default binding (delete next char) take over 673 if not self.executing: 674 self.resetoutput() 675 self.close() 676 else: 677 self.canceled = 0 678 self.endoffile = 1 679 self.top.quit() 680 return "break" 681 682 def home_callback(self, event): 683 if event.state != 0 and event.keysym == "Home": 684 return # <Modifier-Home>; fall back to class binding 685 if self.text.compare("iomark", "<=", "insert") and \ 686 self.text.compare("insert linestart", "<=", "iomark"): 687 self.text.mark_set("insert", "iomark") 688 self.text.tag_remove("sel", "1.0", "end") 689 self.text.see("insert") 690 return "break" 691 692 def linefeed_callback(self, event): 693 # Insert a linefeed without entering anything (still autoindented) 694 if self.reading: 695 self.text.insert("insert", "\n") 696 self.text.see("insert") 697 else: 698 self.auto.auto_indent(event) 699 return "break" 700 701 def enter_callback(self, event): 702 if self.executing and not self.reading: 703 return # Let the default binding (insert '\n') take over 704 # If some text is selected, recall the selection 705 # (but only if this before the I/O mark) 706 try: 707 sel = self.text.get("sel.first", "sel.last") 708 if sel: 709 if self.text.compare("sel.last", "<=", "iomark"): 710 self.recall(sel) 711 return "break" 712 except: 713 pass 714 # If we're strictly before the line containing iomark, recall 715 # the current line, less a leading prompt, less leading or 716 # trailing whitespace 717 if self.text.compare("insert", "<", "iomark linestart"): 718 # Check if there's a relevant stdin range -- if so, use it 719 prev = self.text.tag_prevrange("stdin", "insert") 720 if prev and self.text.compare("insert", "<", prev[1]): 721 self.recall(self.text.get(prev[0], prev[1])) 722 return "break" 723 next = self.text.tag_nextrange("stdin", "insert") 724 if next and self.text.compare("insert lineend", ">=", next[0]): 725 self.recall(self.text.get(next[0], next[1])) 726 return "break" 727 # No stdin mark -- just get the current line 728 self.recall(self.text.get("insert linestart", "insert lineend")) 729 return "break" 730 # If we're in the current input and there's only whitespace 731 # beyond the cursor, erase that whitespace first 732 s = self.text.get("insert", "end-1c") 733 if s and not string.strip(s): 734 self.text.delete("insert", "end-1c") 735 # If we're in the current input before its last line, 736 # insert a newline right at the insert point 737 if self.text.compare("insert", "<", "end-1c linestart"): 738 self.auto.auto_indent(event) 739 return "break" 740 # We're in the last line; append a newline and submit it 741 self.text.mark_set("insert", "end-1c") 742 if self.reading: 743 self.text.insert("insert", "\n") 744 self.text.see("insert") 745 else: 746 self.auto.auto_indent(event) 747 self.text.tag_add("stdin", "iomark", "end-1c") 748 self.text.update_idletasks() 749 if self.reading: 750 self.top.quit() # Break out of recursive mainloop() in raw_input() 751 else: 752 self.runit() 753 return "break" 754 755 def recall(self, s): 756 if self.history: 757 self.history.recall(s) 758 759 def runit(self): 760 line = self.text.get("iomark", "end-1c") 761 # Strip off last newline and surrounding whitespace. 762 # (To allow you to hit return twice to end a statement.) 763 i = len(line) 764 while i > 0 and line[i-1] in " \t": 765 i = i-1 766 if i > 0 and line[i-1] == "\n": 767 i = i-1 768 while i > 0 and line[i-1] in " \t": 769 i = i-1 770 line = line[:i] 771 more = self.interp.runsource(line) 772 # XXX This was causing extra prompt with shell KBK 773# if not more: 774# self.showprompt() 775 776 def cancel_check(self, frame, what, args, 777 dooneevent=tkinter.dooneevent, 778 dontwait=tkinter.DONT_WAIT): 779 # Hack -- use the debugger hooks to be able to handle events 780 # and interrupt execution at any time. 781 # This slows execution down quite a bit, so you may want to 782 # disable this (by not calling settrace() in runcode() above) 783 # for full-bore (uninterruptable) speed. 784 # XXX This should become a user option. 785 if self.canceled: 786 return 787 dooneevent(dontwait) 788 if self.canceled: 789 self.canceled = 0 790 raise KeyboardInterrupt 791 return self._cancel_check 792 793 def open_stack_viewer(self, event=None): 794 if self.interp.rpcclt: 795 return self.interp.remote_stack_viewer() 796 try: 797 sys.last_traceback 798 except: 799 tkMessageBox.showerror("No stack trace", 800 "There is no stack trace yet.\n" 801 "(sys.last_traceback is not defined)", 802 master=self.text) 803 return 804 from StackViewer import StackBrowser 805 sv = StackBrowser(self.root, self.flist) 806 807 def showprompt(self): 808 self.resetoutput() 809 try: 810 s = str(sys.ps1) 811 except: 812 s = "" 813 self.console.write(s) 814 self.text.mark_set("insert", "end-1c") 815 self.set_line_and_column() 816 817 def resetoutput(self): 818 source = self.text.get("iomark", "end-1c") 819 if self.history: 820 self.history.history_store(source) 821 if self.text.get("end-2c") != "\n": 822 self.text.insert("end-1c", "\n") 823 self.text.mark_set("iomark", "end-1c") 824 self.set_line_and_column() 825 sys.stdout.softspace = 0 826 827 def write(self, s, tags=()): 828 self.text.mark_gravity("iomark", "right") 829 OutputWindow.write(self, s, tags, "iomark") 830 self.text.mark_gravity("iomark", "left") 831 if self.canceled: 832 self.canceled = 0 833 raise KeyboardInterrupt 834 835class PseudoFile: 836 837 def __init__(self, shell, tags): 838 self.shell = shell 839 self.tags = tags 840 self.softspace = 0 841 842 def write(self, s): 843 self.shell.write(s, self.tags) 844 845 def writelines(self, l): 846 map(self.write, l) 847 848 def flush(self): 849 pass 850 851 def isatty(self): 852 return 1 853 854 855usage_msg = """\ 856usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ... 857 858idle file(s) (without options) edit the file(s) 859 860-c cmd run the command in a shell 861-d enable the debugger 862-e edit mode; arguments are files to be edited 863-i open an interactive shell 864-i file(s) open a shell and also an editor window for each file 865-r 866-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else 867-t title set title of shell window 868 869Remaining arguments are applied to the command (-c) or script (-r). 870""" 871 872def main(): 873 cmd = None 874 edit = 0 875 debug = 0 876 script = None 877 startup = 0 878 879 try: 880 opts, args = getopt.getopt(sys.argv[1:], "c:deir:st:") 881 except getopt.error, msg: 882 sys.stderr.write("Error: %s\n" % str(msg)) 883 sys.stderr.write(usage_msg) 884 sys.exit(2) 885 886 for o, a in opts: 887 if o == '-c': 888 cmd = a 889 if o == '-d': 890 debug = 1 891 if o == '-e': 892 edit = 1 893 if o == '-r': 894 script = a 895 if o == '-s': 896 startup = 1 897 if o == '-t': 898 PyShell.shell_title = a 899 900 if args and args[0] != "-": edit = 1 901 902 for i in range(len(sys.path)): 903 sys.path[i] = os.path.abspath(sys.path[i]) 904 905 pathx = [] 906 if edit: 907 for filename in args: 908 pathx.append(os.path.dirname(filename)) 909 elif args and args[0] != "-": 910 pathx.append(os.path.dirname(args[0])) 911 else: 912 pathx.append(os.curdir) 913 for dir in pathx: 914 dir = os.path.abspath(dir) 915 if not dir in sys.path: 916 sys.path.insert(0, dir) 917 918 global flist, root 919 root = Tk(className="Idle") 920 fixwordbreaks(root) 921 root.withdraw() 922 flist = PyShellFileList(root) 923 924 if edit: 925 for filename in args: 926 flist.open(filename) 927 if not args: 928 flist.new() 929 else: 930 if cmd: 931 sys.argv = ["-c"] + args 932 else: 933 sys.argv = args or [""] 934 935 shell = PyShell(flist) 936 interp = shell.interp 937 flist.pyshell = shell 938 939 if startup: 940 filename = os.environ.get("IDLESTARTUP") or \ 941 os.environ.get("PYTHONSTARTUP") 942 if filename and os.path.isfile(filename): 943 interp.execfile(filename) 944 945 if debug: 946 shell.open_debugger() 947 if cmd: 948 interp.execsource(cmd) 949 elif script: 950 if os.path.isfile(script): 951 interp.execfile(script) 952 else: 953 print "No script file: ", script 954 shell.begin() 955 root.mainloop() 956 root.destroy() 957 958def display_port_binding_error(): 959 print """\ 960IDLE cannot run. 961 962IDLE needs to use a specific TCP/IP port (8833) in order to execute and 963debug programs. IDLE is unable to bind to this port, and so cannot 964start. Here are some possible causes of this problem: 965 966 1. TCP/IP networking is not installed or not working on this computer 967 2. Another program is running that uses this port 968 3. Personal firewall software is preventing IDLE from using this port 969 970IDLE makes and accepts connections only with this computer, and does not 971communicate over the internet in any way. Its use of port 8833 should not 972be a security risk on a single-user machine. 973""" 974 975if __name__ == "__main__": 976 main() 977