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