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