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