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