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