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