1#! /usr/bin/env python3 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 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 self.more = 0 680 # at the moment, InteractiveInterpreter expects str 681 assert isinstance(source, str) 682 # InteractiveInterpreter.runsource() calls its runcode() method, 683 # which is overridden (see below) 684 return InteractiveInterpreter.runsource(self, source, filename) 685 686 def stuffsource(self, source): 687 "Stuff source in the filename cache" 688 filename = "<pyshell#%d>" % self.gid 689 self.gid = self.gid + 1 690 lines = source.split("\n") 691 linecache.cache[filename] = len(source)+1, 0, lines, filename 692 return filename 693 694 def prepend_syspath(self, filename): 695 "Prepend sys.path with file's directory if not already included" 696 self.runcommand("""if 1: 697 _filename = %r 698 import sys as _sys 699 from os.path import dirname as _dirname 700 _dir = _dirname(_filename) 701 if not _dir in _sys.path: 702 _sys.path.insert(0, _dir) 703 del _filename, _sys, _dirname, _dir 704 \n""" % (filename,)) 705 706 def showsyntaxerror(self, filename=None): 707 """Override Interactive Interpreter method: Use Colorizing 708 709 Color the offending position instead of printing it and pointing at it 710 with a caret. 711 712 """ 713 tkconsole = self.tkconsole 714 text = tkconsole.text 715 text.tag_remove("ERROR", "1.0", "end") 716 type, value, tb = sys.exc_info() 717 msg = getattr(value, 'msg', '') or value or "<no detail available>" 718 lineno = getattr(value, 'lineno', '') or 1 719 offset = getattr(value, 'offset', '') or 0 720 if offset == 0: 721 lineno += 1 #mark end of offending line 722 if lineno == 1: 723 pos = "iomark + %d chars" % (offset-1) 724 else: 725 pos = "iomark linestart + %d lines + %d chars" % \ 726 (lineno-1, offset-1) 727 tkconsole.colorize_syntax_error(text, pos) 728 tkconsole.resetoutput() 729 self.write("SyntaxError: %s\n" % msg) 730 tkconsole.showprompt() 731 732 def showtraceback(self): 733 "Extend base class method to reset output properly" 734 self.tkconsole.resetoutput() 735 self.checklinecache() 736 InteractiveInterpreter.showtraceback(self) 737 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 738 self.tkconsole.open_stack_viewer() 739 740 def checklinecache(self): 741 c = linecache.cache 742 for key in list(c.keys()): 743 if key[:1] + key[-1:] != "<>": 744 del c[key] 745 746 def runcommand(self, code): 747 "Run the code without invoking the debugger" 748 # The code better not raise an exception! 749 if self.tkconsole.executing: 750 self.display_executing_dialog() 751 return 0 752 if self.rpcclt: 753 self.rpcclt.remotequeue("exec", "runcode", (code,), {}) 754 else: 755 exec(code, self.locals) 756 return 1 757 758 def runcode(self, code): 759 "Override base class method" 760 if self.tkconsole.executing: 761 self.interp.restart_subprocess() 762 self.checklinecache() 763 debugger = self.debugger 764 try: 765 self.tkconsole.beginexecuting() 766 if not debugger and self.rpcclt is not None: 767 self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", 768 (code,), {}) 769 elif debugger: 770 debugger.run(code, self.locals) 771 else: 772 exec(code, self.locals) 773 except SystemExit: 774 if not self.tkconsole.closing: 775 if tkMessageBox.askyesno( 776 "Exit?", 777 "Do you want to exit altogether?", 778 default="yes", 779 parent=self.tkconsole.text): 780 raise 781 else: 782 self.showtraceback() 783 else: 784 raise 785 except: 786 if use_subprocess: 787 print("IDLE internal error in runcode()", 788 file=self.tkconsole.stderr) 789 self.showtraceback() 790 self.tkconsole.endexecuting() 791 else: 792 if self.tkconsole.canceled: 793 self.tkconsole.canceled = False 794 print("KeyboardInterrupt", file=self.tkconsole.stderr) 795 else: 796 self.showtraceback() 797 finally: 798 if not use_subprocess: 799 try: 800 self.tkconsole.endexecuting() 801 except AttributeError: # shell may have closed 802 pass 803 804 def write(self, s): 805 "Override base class method" 806 return self.tkconsole.stderr.write(s) 807 808 def display_port_binding_error(self): 809 tkMessageBox.showerror( 810 "Port Binding Error", 811 "IDLE can't bind to a TCP/IP port, which is necessary to " 812 "communicate with its Python execution server. This might be " 813 "because no networking is installed on this computer. " 814 "Run IDLE with the -n command line switch to start without a " 815 "subprocess and refer to Help/IDLE Help 'Running without a " 816 "subprocess' for further details.", 817 parent=self.tkconsole.text) 818 819 def display_no_subprocess_error(self): 820 tkMessageBox.showerror( 821 "Subprocess Connection Error", 822 "IDLE's subprocess didn't make connection.\n" 823 "See the 'Startup failure' section of the IDLE doc, online at\n" 824 "https://docs.python.org/3/library/idle.html#startup-failure", 825 parent=self.tkconsole.text) 826 827 def display_executing_dialog(self): 828 tkMessageBox.showerror( 829 "Already executing", 830 "The Python Shell window is already executing a command; " 831 "please wait until it is finished.", 832 parent=self.tkconsole.text) 833 834 835class PyShell(OutputWindow): 836 837 shell_title = "Python " + python_version() + " Shell" 838 839 # Override classes 840 ColorDelegator = ModifiedColorDelegator 841 UndoDelegator = ModifiedUndoDelegator 842 843 # Override menus 844 menu_specs = [ 845 ("file", "_File"), 846 ("edit", "_Edit"), 847 ("debug", "_Debug"), 848 ("options", "_Options"), 849 ("window", "_Window"), 850 ("help", "_Help"), 851 ] 852 853 # Extend right-click context menu 854 rmenu_specs = OutputWindow.rmenu_specs + [ 855 ("Squeeze", "<<squeeze-current-text>>"), 856 ] 857 858 allow_line_numbers = False 859 860 # New classes 861 from idlelib.history import History 862 863 def __init__(self, flist=None): 864 if use_subprocess: 865 ms = self.menu_specs 866 if ms[2][0] != "shell": 867 ms.insert(2, ("shell", "She_ll")) 868 self.interp = ModifiedInterpreter(self) 869 if flist is None: 870 root = Tk() 871 fixwordbreaks(root) 872 root.withdraw() 873 flist = PyShellFileList(root) 874 875 OutputWindow.__init__(self, flist, None, None) 876 877 self.usetabs = True 878 # indentwidth must be 8 when using tabs. See note in EditorWindow: 879 self.indentwidth = 8 880 881 self.sys_ps1 = sys.ps1 if hasattr(sys, 'ps1') else '>>> ' 882 self.prompt_last_line = self.sys_ps1.split('\n')[-1] 883 self.prompt = self.sys_ps1 # Changes when debug active 884 885 text = self.text 886 text.configure(wrap="char") 887 text.bind("<<newline-and-indent>>", self.enter_callback) 888 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback) 889 text.bind("<<interrupt-execution>>", self.cancel_callback) 890 text.bind("<<end-of-file>>", self.eof_callback) 891 text.bind("<<open-stack-viewer>>", self.open_stack_viewer) 892 text.bind("<<toggle-debugger>>", self.toggle_debugger) 893 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer) 894 if use_subprocess: 895 text.bind("<<view-restart>>", self.view_restart_mark) 896 text.bind("<<restart-shell>>", self.restart_shell) 897 squeezer = self.Squeezer(self) 898 text.bind("<<squeeze-current-text>>", 899 squeezer.squeeze_current_text_event) 900 901 self.save_stdout = sys.stdout 902 self.save_stderr = sys.stderr 903 self.save_stdin = sys.stdin 904 from idlelib import iomenu 905 self.stdin = StdInputFile(self, "stdin", 906 iomenu.encoding, iomenu.errors) 907 self.stdout = StdOutputFile(self, "stdout", 908 iomenu.encoding, iomenu.errors) 909 self.stderr = StdOutputFile(self, "stderr", 910 iomenu.encoding, "backslashreplace") 911 self.console = StdOutputFile(self, "console", 912 iomenu.encoding, iomenu.errors) 913 if not use_subprocess: 914 sys.stdout = self.stdout 915 sys.stderr = self.stderr 916 sys.stdin = self.stdin 917 try: 918 # page help() text to shell. 919 import pydoc # import must be done here to capture i/o rebinding. 920 # XXX KBK 27Dec07 use text viewer someday, but must work w/o subproc 921 pydoc.pager = pydoc.plainpager 922 except: 923 sys.stderr = sys.__stderr__ 924 raise 925 # 926 self.history = self.History(self.text) 927 # 928 self.pollinterval = 50 # millisec 929 930 def get_standard_extension_names(self): 931 return idleConf.GetExtensions(shell_only=True) 932 933 reading = False 934 executing = False 935 canceled = False 936 endoffile = False 937 closing = False 938 _stop_readline_flag = False 939 940 def set_warning_stream(self, stream): 941 global warning_stream 942 warning_stream = stream 943 944 def get_warning_stream(self): 945 return warning_stream 946 947 def toggle_debugger(self, event=None): 948 if self.executing: 949 tkMessageBox.showerror("Don't debug now", 950 "You can only toggle the debugger when idle", 951 parent=self.text) 952 self.set_debugger_indicator() 953 return "break" 954 else: 955 db = self.interp.getdebugger() 956 if db: 957 self.close_debugger() 958 else: 959 self.open_debugger() 960 961 def set_debugger_indicator(self): 962 db = self.interp.getdebugger() 963 self.setvar("<<toggle-debugger>>", not not db) 964 965 def toggle_jit_stack_viewer(self, event=None): 966 pass # All we need is the variable 967 968 def close_debugger(self): 969 db = self.interp.getdebugger() 970 if db: 971 self.interp.setdebugger(None) 972 db.close() 973 if self.interp.rpcclt: 974 debugger_r.close_remote_debugger(self.interp.rpcclt) 975 self.resetoutput() 976 self.console.write("[DEBUG OFF]\n") 977 self.prompt = self.sys_ps1 978 self.showprompt() 979 self.set_debugger_indicator() 980 981 def open_debugger(self): 982 if self.interp.rpcclt: 983 dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, 984 self) 985 else: 986 dbg_gui = debugger.Debugger(self) 987 self.interp.setdebugger(dbg_gui) 988 dbg_gui.load_breakpoints() 989 self.prompt = "[DEBUG ON]\n" + self.sys_ps1 990 self.showprompt() 991 self.set_debugger_indicator() 992 993 def beginexecuting(self): 994 "Helper for ModifiedInterpreter" 995 self.resetoutput() 996 self.executing = 1 997 998 def endexecuting(self): 999 "Helper for ModifiedInterpreter" 1000 self.executing = 0 1001 self.canceled = 0 1002 self.showprompt() 1003 1004 def close(self): 1005 "Extend EditorWindow.close()" 1006 if self.executing: 1007 response = tkMessageBox.askokcancel( 1008 "Kill?", 1009 "Your program is still running!\n Do you want to kill it?", 1010 default="ok", 1011 parent=self.text) 1012 if response is False: 1013 return "cancel" 1014 self.stop_readline() 1015 self.canceled = True 1016 self.closing = True 1017 return EditorWindow.close(self) 1018 1019 def _close(self): 1020 "Extend EditorWindow._close(), shut down debugger and execution server" 1021 self.close_debugger() 1022 if use_subprocess: 1023 self.interp.kill_subprocess() 1024 # Restore std streams 1025 sys.stdout = self.save_stdout 1026 sys.stderr = self.save_stderr 1027 sys.stdin = self.save_stdin 1028 # Break cycles 1029 self.interp = None 1030 self.console = None 1031 self.flist.pyshell = None 1032 self.history = None 1033 EditorWindow._close(self) 1034 1035 def ispythonsource(self, filename): 1036 "Override EditorWindow method: never remove the colorizer" 1037 return True 1038 1039 def short_title(self): 1040 return self.shell_title 1041 1042 COPYRIGHT = \ 1043 'Type "help", "copyright", "credits" or "license()" for more information.' 1044 1045 def begin(self): 1046 self.text.mark_set("iomark", "insert") 1047 self.resetoutput() 1048 if use_subprocess: 1049 nosub = '' 1050 client = self.interp.start_subprocess() 1051 if not client: 1052 self.close() 1053 return False 1054 else: 1055 nosub = ("==== No Subprocess ====\n\n" + 1056 "WARNING: Running IDLE without a Subprocess is deprecated\n" + 1057 "and will be removed in a later version. See Help/IDLE Help\n" + 1058 "for details.\n\n") 1059 sys.displayhook = rpc.displayhook 1060 1061 self.write("Python %s on %s\n%s\n%s" % 1062 (sys.version, sys.platform, self.COPYRIGHT, nosub)) 1063 self.text.focus_force() 1064 self.showprompt() 1065 import tkinter 1066 tkinter._default_root = None # 03Jan04 KBK What's this? 1067 return True 1068 1069 def stop_readline(self): 1070 if not self.reading: # no nested mainloop to exit. 1071 return 1072 self._stop_readline_flag = True 1073 self.top.quit() 1074 1075 def readline(self): 1076 save = self.reading 1077 try: 1078 self.reading = 1 1079 self.top.mainloop() # nested mainloop() 1080 finally: 1081 self.reading = save 1082 if self._stop_readline_flag: 1083 self._stop_readline_flag = False 1084 return "" 1085 line = self.text.get("iomark", "end-1c") 1086 if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C 1087 line = "\n" 1088 self.resetoutput() 1089 if self.canceled: 1090 self.canceled = 0 1091 if not use_subprocess: 1092 raise KeyboardInterrupt 1093 if self.endoffile: 1094 self.endoffile = 0 1095 line = "" 1096 return line 1097 1098 def isatty(self): 1099 return True 1100 1101 def cancel_callback(self, event=None): 1102 try: 1103 if self.text.compare("sel.first", "!=", "sel.last"): 1104 return # Active selection -- always use default binding 1105 except: 1106 pass 1107 if not (self.executing or self.reading): 1108 self.resetoutput() 1109 self.interp.write("KeyboardInterrupt\n") 1110 self.showprompt() 1111 return "break" 1112 self.endoffile = 0 1113 self.canceled = 1 1114 if (self.executing and self.interp.rpcclt): 1115 if self.interp.getdebugger(): 1116 self.interp.restart_subprocess() 1117 else: 1118 self.interp.interrupt_subprocess() 1119 if self.reading: 1120 self.top.quit() # exit the nested mainloop() in readline() 1121 return "break" 1122 1123 def eof_callback(self, event): 1124 if self.executing and not self.reading: 1125 return # Let the default binding (delete next char) take over 1126 if not (self.text.compare("iomark", "==", "insert") and 1127 self.text.compare("insert", "==", "end-1c")): 1128 return # Let the default binding (delete next char) take over 1129 if not self.executing: 1130 self.resetoutput() 1131 self.close() 1132 else: 1133 self.canceled = 0 1134 self.endoffile = 1 1135 self.top.quit() 1136 return "break" 1137 1138 def linefeed_callback(self, event): 1139 # Insert a linefeed without entering anything (still autoindented) 1140 if self.reading: 1141 self.text.insert("insert", "\n") 1142 self.text.see("insert") 1143 else: 1144 self.newline_and_indent_event(event) 1145 return "break" 1146 1147 def enter_callback(self, event): 1148 if self.executing and not self.reading: 1149 return # Let the default binding (insert '\n') take over 1150 # If some text is selected, recall the selection 1151 # (but only if this before the I/O mark) 1152 try: 1153 sel = self.text.get("sel.first", "sel.last") 1154 if sel: 1155 if self.text.compare("sel.last", "<=", "iomark"): 1156 self.recall(sel, event) 1157 return "break" 1158 except: 1159 pass 1160 # If we're strictly before the line containing iomark, recall 1161 # the current line, less a leading prompt, less leading or 1162 # trailing whitespace 1163 if self.text.compare("insert", "<", "iomark linestart"): 1164 # Check if there's a relevant stdin range -- if so, use it 1165 prev = self.text.tag_prevrange("stdin", "insert") 1166 if prev and self.text.compare("insert", "<", prev[1]): 1167 self.recall(self.text.get(prev[0], prev[1]), event) 1168 return "break" 1169 next = self.text.tag_nextrange("stdin", "insert") 1170 if next and self.text.compare("insert lineend", ">=", next[0]): 1171 self.recall(self.text.get(next[0], next[1]), event) 1172 return "break" 1173 # No stdin mark -- just get the current line, less any prompt 1174 indices = self.text.tag_nextrange("console", "insert linestart") 1175 if indices and \ 1176 self.text.compare(indices[0], "<=", "insert linestart"): 1177 self.recall(self.text.get(indices[1], "insert lineend"), event) 1178 else: 1179 self.recall(self.text.get("insert linestart", "insert lineend"), event) 1180 return "break" 1181 # If we're between the beginning of the line and the iomark, i.e. 1182 # in the prompt area, move to the end of the prompt 1183 if self.text.compare("insert", "<", "iomark"): 1184 self.text.mark_set("insert", "iomark") 1185 # If we're in the current input and there's only whitespace 1186 # beyond the cursor, erase that whitespace first 1187 s = self.text.get("insert", "end-1c") 1188 if s and not s.strip(): 1189 self.text.delete("insert", "end-1c") 1190 # If we're in the current input before its last line, 1191 # insert a newline right at the insert point 1192 if self.text.compare("insert", "<", "end-1c linestart"): 1193 self.newline_and_indent_event(event) 1194 return "break" 1195 # We're in the last line; append a newline and submit it 1196 self.text.mark_set("insert", "end-1c") 1197 if self.reading: 1198 self.text.insert("insert", "\n") 1199 self.text.see("insert") 1200 else: 1201 self.newline_and_indent_event(event) 1202 self.text.tag_add("stdin", "iomark", "end-1c") 1203 self.text.update_idletasks() 1204 if self.reading: 1205 self.top.quit() # Break out of recursive mainloop() 1206 else: 1207 self.runit() 1208 return "break" 1209 1210 def recall(self, s, event): 1211 # remove leading and trailing empty or whitespace lines 1212 s = re.sub(r'^\s*\n', '' , s) 1213 s = re.sub(r'\n\s*$', '', s) 1214 lines = s.split('\n') 1215 self.text.undo_block_start() 1216 try: 1217 self.text.tag_remove("sel", "1.0", "end") 1218 self.text.mark_set("insert", "end-1c") 1219 prefix = self.text.get("insert linestart", "insert") 1220 if prefix.rstrip().endswith(':'): 1221 self.newline_and_indent_event(event) 1222 prefix = self.text.get("insert linestart", "insert") 1223 self.text.insert("insert", lines[0].strip()) 1224 if len(lines) > 1: 1225 orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) 1226 new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) 1227 for line in lines[1:]: 1228 if line.startswith(orig_base_indent): 1229 # replace orig base indentation with new indentation 1230 line = new_base_indent + line[len(orig_base_indent):] 1231 self.text.insert('insert', '\n'+line.rstrip()) 1232 finally: 1233 self.text.see("insert") 1234 self.text.undo_block_stop() 1235 1236 def runit(self): 1237 line = self.text.get("iomark", "end-1c") 1238 # Strip off last newline and surrounding whitespace. 1239 # (To allow you to hit return twice to end a statement.) 1240 i = len(line) 1241 while i > 0 and line[i-1] in " \t": 1242 i = i-1 1243 if i > 0 and line[i-1] == "\n": 1244 i = i-1 1245 while i > 0 and line[i-1] in " \t": 1246 i = i-1 1247 line = line[:i] 1248 self.interp.runsource(line) 1249 1250 def open_stack_viewer(self, event=None): 1251 if self.interp.rpcclt: 1252 return self.interp.remote_stack_viewer() 1253 try: 1254 sys.last_traceback 1255 except: 1256 tkMessageBox.showerror("No stack trace", 1257 "There is no stack trace yet.\n" 1258 "(sys.last_traceback is not defined)", 1259 parent=self.text) 1260 return 1261 from idlelib.stackviewer import StackBrowser 1262 StackBrowser(self.root, self.flist) 1263 1264 def view_restart_mark(self, event=None): 1265 self.text.see("iomark") 1266 self.text.see("restart") 1267 1268 def restart_shell(self, event=None): 1269 "Callback for Run/Restart Shell Cntl-F6" 1270 self.interp.restart_subprocess(with_cwd=True) 1271 1272 def showprompt(self): 1273 self.resetoutput() 1274 self.console.write(self.prompt) 1275 self.text.mark_set("insert", "end-1c") 1276 self.set_line_and_column() 1277 self.io.reset_undo() 1278 1279 def show_warning(self, msg): 1280 width = self.interp.tkconsole.width 1281 wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True) 1282 wrapped_msg = '\n'.join(wrapper.wrap(msg)) 1283 if not wrapped_msg.endswith('\n'): 1284 wrapped_msg += '\n' 1285 self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr") 1286 1287 def resetoutput(self): 1288 source = self.text.get("iomark", "end-1c") 1289 if self.history: 1290 self.history.store(source) 1291 if self.text.get("end-2c") != "\n": 1292 self.text.insert("end-1c", "\n") 1293 self.text.mark_set("iomark", "end-1c") 1294 self.set_line_and_column() 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 = 0 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 ext = '.png' if TkVersion >= 8.6 else '.gif' 1489 iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) 1490 for size in (16, 32, 48)] 1491 icons = [PhotoImage(master=root, file=iconfile) 1492 for iconfile in iconfiles] 1493 root.wm_iconphoto(True, *icons) 1494 1495 # start editor and/or shell windows: 1496 fixwordbreaks(root) 1497 fix_x11_paste(root) 1498 flist = PyShellFileList(root) 1499 macosx.setupApp(root, flist) 1500 1501 if enable_edit: 1502 if not (cmd or script): 1503 for filename in args[:]: 1504 if flist.open(filename) is None: 1505 # filename is a directory actually, disconsider it 1506 args.remove(filename) 1507 if not args: 1508 flist.new() 1509 1510 if enable_shell: 1511 shell = flist.open_shell() 1512 if not shell: 1513 return # couldn't open shell 1514 if macosx.isAquaTk() and flist.dict: 1515 # On OSX: when the user has double-clicked on a file that causes 1516 # IDLE to be launched the shell window will open just in front of 1517 # the file she wants to see. Lower the interpreter window when 1518 # there are open files. 1519 shell.top.lower() 1520 else: 1521 shell = flist.pyshell 1522 1523 # Handle remaining options. If any of these are set, enable_shell 1524 # was set also, so shell must be true to reach here. 1525 if debug: 1526 shell.open_debugger() 1527 if startup: 1528 filename = os.environ.get("IDLESTARTUP") or \ 1529 os.environ.get("PYTHONSTARTUP") 1530 if filename and os.path.isfile(filename): 1531 shell.interp.execfile(filename) 1532 if cmd or script: 1533 shell.interp.runcommand("""if 1: 1534 import sys as _sys 1535 _sys.argv = %r 1536 del _sys 1537 \n""" % (sys.argv,)) 1538 if cmd: 1539 shell.interp.execsource(cmd) 1540 elif script: 1541 shell.interp.prepend_syspath(script) 1542 shell.interp.execfile(script) 1543 elif shell: 1544 # If there is a shell window and no cmd or script in progress, 1545 # check for problematic issues and print warning message(s) in 1546 # the IDLE shell window; this is less intrusive than always 1547 # opening a separate window. 1548 1549 # Warn if using a problematic OS X Tk version. 1550 tkversionwarning = macosx.tkVersionWarning(root) 1551 if tkversionwarning: 1552 shell.show_warning(tkversionwarning) 1553 1554 # Warn if the "Prefer tabs when opening documents" system 1555 # preference is set to "Always". 1556 prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning() 1557 if prefer_tabs_preference_warning: 1558 shell.show_warning(prefer_tabs_preference_warning) 1559 1560 while flist.inversedict: # keep IDLE running while files are open. 1561 root.mainloop() 1562 root.destroy() 1563 capture_warnings(False) 1564 1565if __name__ == "__main__": 1566 main() 1567 1568capture_warnings(False) # Make sure turned off; see issue 18081 1569