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