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