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