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