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