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