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