17db96d56Sopenharmony_ci#! /usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport sys 47db96d56Sopenharmony_ciif __name__ == "__main__": 57db96d56Sopenharmony_ci sys.modules['idlelib.pyshell'] = sys.modules['__main__'] 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_citry: 87db96d56Sopenharmony_ci from tkinter import * 97db96d56Sopenharmony_ciexcept ImportError: 107db96d56Sopenharmony_ci print("** IDLE can't import Tkinter.\n" 117db96d56Sopenharmony_ci "Your Python may not be configured for Tk. **", file=sys.__stderr__) 127db96d56Sopenharmony_ci raise SystemExit(1) 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_ci# Valid arguments for the ...Awareness call below are defined in the following. 157db96d56Sopenharmony_ci# https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx 167db96d56Sopenharmony_ciif sys.platform == 'win32': 177db96d56Sopenharmony_ci try: 187db96d56Sopenharmony_ci import ctypes 197db96d56Sopenharmony_ci PROCESS_SYSTEM_DPI_AWARE = 1 # Int required. 207db96d56Sopenharmony_ci ctypes.OleDLL('shcore').SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE) 217db96d56Sopenharmony_ci except (ImportError, AttributeError, OSError): 227db96d56Sopenharmony_ci pass 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_cifrom tkinter import messagebox 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_cifrom code import InteractiveInterpreter 277db96d56Sopenharmony_ciimport itertools 287db96d56Sopenharmony_ciimport linecache 297db96d56Sopenharmony_ciimport os 307db96d56Sopenharmony_ciimport os.path 317db96d56Sopenharmony_cifrom platform import python_version 327db96d56Sopenharmony_ciimport re 337db96d56Sopenharmony_ciimport socket 347db96d56Sopenharmony_ciimport subprocess 357db96d56Sopenharmony_cifrom textwrap import TextWrapper 367db96d56Sopenharmony_ciimport threading 377db96d56Sopenharmony_ciimport time 387db96d56Sopenharmony_ciimport tokenize 397db96d56Sopenharmony_ciimport warnings 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_cifrom idlelib.colorizer import ColorDelegator 427db96d56Sopenharmony_cifrom idlelib.config import idleConf 437db96d56Sopenharmony_cifrom idlelib.delegator import Delegator 447db96d56Sopenharmony_cifrom idlelib import debugger 457db96d56Sopenharmony_cifrom idlelib import debugger_r 467db96d56Sopenharmony_cifrom idlelib.editor import EditorWindow, fixwordbreaks 477db96d56Sopenharmony_cifrom idlelib.filelist import FileList 487db96d56Sopenharmony_cifrom idlelib.outwin import OutputWindow 497db96d56Sopenharmony_cifrom idlelib import replace 507db96d56Sopenharmony_cifrom idlelib import rpc 517db96d56Sopenharmony_cifrom idlelib.run import idle_formatwarning, StdInputFile, StdOutputFile 527db96d56Sopenharmony_cifrom idlelib.undo import UndoDelegator 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci# Default for testing; defaults to True in main() for running. 557db96d56Sopenharmony_ciuse_subprocess = False 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ciHOST = '127.0.0.1' # python execution server on localhost loopback 587db96d56Sopenharmony_ciPORT = 0 # someday pass in host, port for remote debug capability 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_citry: # In case IDLE started with -n. 617db96d56Sopenharmony_ci eof = 'Ctrl-D (end-of-file)' 627db96d56Sopenharmony_ci exit.eof = eof 637db96d56Sopenharmony_ci quit.eof = eof 647db96d56Sopenharmony_ciexcept NameError: # In case python started with -S. 657db96d56Sopenharmony_ci pass 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci# Override warnings module to write to warning_stream. Initialize to send IDLE 687db96d56Sopenharmony_ci# internal warnings to the console. ScriptBinding.check_syntax() will 697db96d56Sopenharmony_ci# temporarily redirect the stream to the shell window to display warnings when 707db96d56Sopenharmony_ci# checking user's code. 717db96d56Sopenharmony_ciwarning_stream = sys.__stderr__ # None, at least on Windows, if no console. 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_cidef idle_showwarning( 747db96d56Sopenharmony_ci message, category, filename, lineno, file=None, line=None): 757db96d56Sopenharmony_ci """Show Idle-format warning (after replacing warnings.showwarning). 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci The differences are the formatter called, the file=None replacement, 787db96d56Sopenharmony_ci which can be None, the capture of the consequence AttributeError, 797db96d56Sopenharmony_ci and the output of a hard-coded prompt. 807db96d56Sopenharmony_ci """ 817db96d56Sopenharmony_ci if file is None: 827db96d56Sopenharmony_ci file = warning_stream 837db96d56Sopenharmony_ci try: 847db96d56Sopenharmony_ci file.write(idle_formatwarning( 857db96d56Sopenharmony_ci message, category, filename, lineno, line=line)) 867db96d56Sopenharmony_ci file.write(">>> ") 877db96d56Sopenharmony_ci except (AttributeError, OSError): 887db96d56Sopenharmony_ci pass # if file (probably __stderr__) is invalid, skip warning. 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci_warnings_showwarning = None 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_cidef capture_warnings(capture): 937db96d56Sopenharmony_ci "Replace warning.showwarning with idle_showwarning, or reverse." 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci global _warnings_showwarning 967db96d56Sopenharmony_ci if capture: 977db96d56Sopenharmony_ci if _warnings_showwarning is None: 987db96d56Sopenharmony_ci _warnings_showwarning = warnings.showwarning 997db96d56Sopenharmony_ci warnings.showwarning = idle_showwarning 1007db96d56Sopenharmony_ci else: 1017db96d56Sopenharmony_ci if _warnings_showwarning is not None: 1027db96d56Sopenharmony_ci warnings.showwarning = _warnings_showwarning 1037db96d56Sopenharmony_ci _warnings_showwarning = None 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_cicapture_warnings(True) 1067db96d56Sopenharmony_ci 1077db96d56Sopenharmony_cidef extended_linecache_checkcache(filename=None, 1087db96d56Sopenharmony_ci orig_checkcache=linecache.checkcache): 1097db96d56Sopenharmony_ci """Extend linecache.checkcache to preserve the <pyshell#...> entries 1107db96d56Sopenharmony_ci 1117db96d56Sopenharmony_ci Rather than repeating the linecache code, patch it to save the 1127db96d56Sopenharmony_ci <pyshell#...> entries, call the original linecache.checkcache() 1137db96d56Sopenharmony_ci (skipping them), and then restore the saved entries. 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci orig_checkcache is bound at definition time to the original 1167db96d56Sopenharmony_ci method, allowing it to be patched. 1177db96d56Sopenharmony_ci """ 1187db96d56Sopenharmony_ci cache = linecache.cache 1197db96d56Sopenharmony_ci save = {} 1207db96d56Sopenharmony_ci for key in list(cache): 1217db96d56Sopenharmony_ci if key[:1] + key[-1:] == '<>': 1227db96d56Sopenharmony_ci save[key] = cache.pop(key) 1237db96d56Sopenharmony_ci orig_checkcache(filename) 1247db96d56Sopenharmony_ci cache.update(save) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci# Patch linecache.checkcache(): 1277db96d56Sopenharmony_cilinecache.checkcache = extended_linecache_checkcache 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ciclass PyShellEditorWindow(EditorWindow): 1317db96d56Sopenharmony_ci "Regular text edit window in IDLE, supports breakpoints" 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci def __init__(self, *args): 1347db96d56Sopenharmony_ci self.breakpoints = [] 1357db96d56Sopenharmony_ci EditorWindow.__init__(self, *args) 1367db96d56Sopenharmony_ci self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here) 1377db96d56Sopenharmony_ci self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here) 1387db96d56Sopenharmony_ci self.text.bind("<<open-python-shell>>", self.flist.open_shell) 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_ci #TODO: don't read/write this from/to .idlerc when testing 1417db96d56Sopenharmony_ci self.breakpointPath = os.path.join( 1427db96d56Sopenharmony_ci idleConf.userdir, 'breakpoints.lst') 1437db96d56Sopenharmony_ci # whenever a file is changed, restore breakpoints 1447db96d56Sopenharmony_ci def filename_changed_hook(old_hook=self.io.filename_change_hook, 1457db96d56Sopenharmony_ci self=self): 1467db96d56Sopenharmony_ci self.restore_file_breaks() 1477db96d56Sopenharmony_ci old_hook() 1487db96d56Sopenharmony_ci self.io.set_filename_change_hook(filename_changed_hook) 1497db96d56Sopenharmony_ci if self.io.filename: 1507db96d56Sopenharmony_ci self.restore_file_breaks() 1517db96d56Sopenharmony_ci self.color_breakpoint_text() 1527db96d56Sopenharmony_ci 1537db96d56Sopenharmony_ci rmenu_specs = [ 1547db96d56Sopenharmony_ci ("Cut", "<<cut>>", "rmenu_check_cut"), 1557db96d56Sopenharmony_ci ("Copy", "<<copy>>", "rmenu_check_copy"), 1567db96d56Sopenharmony_ci ("Paste", "<<paste>>", "rmenu_check_paste"), 1577db96d56Sopenharmony_ci (None, None, None), 1587db96d56Sopenharmony_ci ("Set Breakpoint", "<<set-breakpoint-here>>", None), 1597db96d56Sopenharmony_ci ("Clear Breakpoint", "<<clear-breakpoint-here>>", None) 1607db96d56Sopenharmony_ci ] 1617db96d56Sopenharmony_ci 1627db96d56Sopenharmony_ci def color_breakpoint_text(self, color=True): 1637db96d56Sopenharmony_ci "Turn colorizing of breakpoint text on or off" 1647db96d56Sopenharmony_ci if self.io is None: 1657db96d56Sopenharmony_ci # possible due to update in restore_file_breaks 1667db96d56Sopenharmony_ci return 1677db96d56Sopenharmony_ci if color: 1687db96d56Sopenharmony_ci theme = idleConf.CurrentTheme() 1697db96d56Sopenharmony_ci cfg = idleConf.GetHighlight(theme, "break") 1707db96d56Sopenharmony_ci else: 1717db96d56Sopenharmony_ci cfg = {'foreground': '', 'background': ''} 1727db96d56Sopenharmony_ci self.text.tag_config('BREAK', cfg) 1737db96d56Sopenharmony_ci 1747db96d56Sopenharmony_ci def set_breakpoint(self, lineno): 1757db96d56Sopenharmony_ci text = self.text 1767db96d56Sopenharmony_ci filename = self.io.filename 1777db96d56Sopenharmony_ci text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) 1787db96d56Sopenharmony_ci try: 1797db96d56Sopenharmony_ci self.breakpoints.index(lineno) 1807db96d56Sopenharmony_ci except ValueError: # only add if missing, i.e. do once 1817db96d56Sopenharmony_ci self.breakpoints.append(lineno) 1827db96d56Sopenharmony_ci try: # update the subprocess debugger 1837db96d56Sopenharmony_ci debug = self.flist.pyshell.interp.debugger 1847db96d56Sopenharmony_ci debug.set_breakpoint_here(filename, lineno) 1857db96d56Sopenharmony_ci except: # but debugger may not be active right now.... 1867db96d56Sopenharmony_ci pass 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci def set_breakpoint_here(self, event=None): 1897db96d56Sopenharmony_ci text = self.text 1907db96d56Sopenharmony_ci filename = self.io.filename 1917db96d56Sopenharmony_ci if not filename: 1927db96d56Sopenharmony_ci text.bell() 1937db96d56Sopenharmony_ci return 1947db96d56Sopenharmony_ci lineno = int(float(text.index("insert"))) 1957db96d56Sopenharmony_ci self.set_breakpoint(lineno) 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ci def clear_breakpoint_here(self, event=None): 1987db96d56Sopenharmony_ci text = self.text 1997db96d56Sopenharmony_ci filename = self.io.filename 2007db96d56Sopenharmony_ci if not filename: 2017db96d56Sopenharmony_ci text.bell() 2027db96d56Sopenharmony_ci return 2037db96d56Sopenharmony_ci lineno = int(float(text.index("insert"))) 2047db96d56Sopenharmony_ci try: 2057db96d56Sopenharmony_ci self.breakpoints.remove(lineno) 2067db96d56Sopenharmony_ci except: 2077db96d56Sopenharmony_ci pass 2087db96d56Sopenharmony_ci text.tag_remove("BREAK", "insert linestart",\ 2097db96d56Sopenharmony_ci "insert lineend +1char") 2107db96d56Sopenharmony_ci try: 2117db96d56Sopenharmony_ci debug = self.flist.pyshell.interp.debugger 2127db96d56Sopenharmony_ci debug.clear_breakpoint_here(filename, lineno) 2137db96d56Sopenharmony_ci except: 2147db96d56Sopenharmony_ci pass 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci def clear_file_breaks(self): 2177db96d56Sopenharmony_ci if self.breakpoints: 2187db96d56Sopenharmony_ci text = self.text 2197db96d56Sopenharmony_ci filename = self.io.filename 2207db96d56Sopenharmony_ci if not filename: 2217db96d56Sopenharmony_ci text.bell() 2227db96d56Sopenharmony_ci return 2237db96d56Sopenharmony_ci self.breakpoints = [] 2247db96d56Sopenharmony_ci text.tag_remove("BREAK", "1.0", END) 2257db96d56Sopenharmony_ci try: 2267db96d56Sopenharmony_ci debug = self.flist.pyshell.interp.debugger 2277db96d56Sopenharmony_ci debug.clear_file_breaks(filename) 2287db96d56Sopenharmony_ci except: 2297db96d56Sopenharmony_ci pass 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci def store_file_breaks(self): 2327db96d56Sopenharmony_ci "Save breakpoints when file is saved" 2337db96d56Sopenharmony_ci # XXX 13 Dec 2002 KBK Currently the file must be saved before it can 2347db96d56Sopenharmony_ci # be run. The breaks are saved at that time. If we introduce 2357db96d56Sopenharmony_ci # a temporary file save feature the save breaks functionality 2367db96d56Sopenharmony_ci # needs to be re-verified, since the breaks at the time the 2377db96d56Sopenharmony_ci # temp file is created may differ from the breaks at the last 2387db96d56Sopenharmony_ci # permanent save of the file. Currently, a break introduced 2397db96d56Sopenharmony_ci # after a save will be effective, but not persistent. 2407db96d56Sopenharmony_ci # This is necessary to keep the saved breaks synched with the 2417db96d56Sopenharmony_ci # saved file. 2427db96d56Sopenharmony_ci # 2437db96d56Sopenharmony_ci # Breakpoints are set as tagged ranges in the text. 2447db96d56Sopenharmony_ci # Since a modified file has to be saved before it is 2457db96d56Sopenharmony_ci # run, and since self.breakpoints (from which the subprocess 2467db96d56Sopenharmony_ci # debugger is loaded) is updated during the save, the visible 2477db96d56Sopenharmony_ci # breaks stay synched with the subprocess even if one of these 2487db96d56Sopenharmony_ci # unexpected breakpoint deletions occurs. 2497db96d56Sopenharmony_ci breaks = self.breakpoints 2507db96d56Sopenharmony_ci filename = self.io.filename 2517db96d56Sopenharmony_ci try: 2527db96d56Sopenharmony_ci with open(self.breakpointPath) as fp: 2537db96d56Sopenharmony_ci lines = fp.readlines() 2547db96d56Sopenharmony_ci except OSError: 2557db96d56Sopenharmony_ci lines = [] 2567db96d56Sopenharmony_ci try: 2577db96d56Sopenharmony_ci with open(self.breakpointPath, "w") as new_file: 2587db96d56Sopenharmony_ci for line in lines: 2597db96d56Sopenharmony_ci if not line.startswith(filename + '='): 2607db96d56Sopenharmony_ci new_file.write(line) 2617db96d56Sopenharmony_ci self.update_breakpoints() 2627db96d56Sopenharmony_ci breaks = self.breakpoints 2637db96d56Sopenharmony_ci if breaks: 2647db96d56Sopenharmony_ci new_file.write(filename + '=' + str(breaks) + '\n') 2657db96d56Sopenharmony_ci except OSError as err: 2667db96d56Sopenharmony_ci if not getattr(self.root, "breakpoint_error_displayed", False): 2677db96d56Sopenharmony_ci self.root.breakpoint_error_displayed = True 2687db96d56Sopenharmony_ci messagebox.showerror(title='IDLE Error', 2697db96d56Sopenharmony_ci message='Unable to update breakpoint list:\n%s' 2707db96d56Sopenharmony_ci % str(err), 2717db96d56Sopenharmony_ci parent=self.text) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci def restore_file_breaks(self): 2747db96d56Sopenharmony_ci self.text.update() # this enables setting "BREAK" tags to be visible 2757db96d56Sopenharmony_ci if self.io is None: 2767db96d56Sopenharmony_ci # can happen if IDLE closes due to the .update() call 2777db96d56Sopenharmony_ci return 2787db96d56Sopenharmony_ci filename = self.io.filename 2797db96d56Sopenharmony_ci if filename is None: 2807db96d56Sopenharmony_ci return 2817db96d56Sopenharmony_ci if os.path.isfile(self.breakpointPath): 2827db96d56Sopenharmony_ci with open(self.breakpointPath) as fp: 2837db96d56Sopenharmony_ci lines = fp.readlines() 2847db96d56Sopenharmony_ci for line in lines: 2857db96d56Sopenharmony_ci if line.startswith(filename + '='): 2867db96d56Sopenharmony_ci breakpoint_linenumbers = eval(line[len(filename)+1:]) 2877db96d56Sopenharmony_ci for breakpoint_linenumber in breakpoint_linenumbers: 2887db96d56Sopenharmony_ci self.set_breakpoint(breakpoint_linenumber) 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ci def update_breakpoints(self): 2917db96d56Sopenharmony_ci "Retrieves all the breakpoints in the current window" 2927db96d56Sopenharmony_ci text = self.text 2937db96d56Sopenharmony_ci ranges = text.tag_ranges("BREAK") 2947db96d56Sopenharmony_ci linenumber_list = self.ranges_to_linenumbers(ranges) 2957db96d56Sopenharmony_ci self.breakpoints = linenumber_list 2967db96d56Sopenharmony_ci 2977db96d56Sopenharmony_ci def ranges_to_linenumbers(self, ranges): 2987db96d56Sopenharmony_ci lines = [] 2997db96d56Sopenharmony_ci for index in range(0, len(ranges), 2): 3007db96d56Sopenharmony_ci lineno = int(float(ranges[index].string)) 3017db96d56Sopenharmony_ci end = int(float(ranges[index+1].string)) 3027db96d56Sopenharmony_ci while lineno < end: 3037db96d56Sopenharmony_ci lines.append(lineno) 3047db96d56Sopenharmony_ci lineno += 1 3057db96d56Sopenharmony_ci return lines 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_ci# XXX 13 Dec 2002 KBK Not used currently 3087db96d56Sopenharmony_ci# def saved_change_hook(self): 3097db96d56Sopenharmony_ci# "Extend base method - clear breaks if module is modified" 3107db96d56Sopenharmony_ci# if not self.get_saved(): 3117db96d56Sopenharmony_ci# self.clear_file_breaks() 3127db96d56Sopenharmony_ci# EditorWindow.saved_change_hook(self) 3137db96d56Sopenharmony_ci 3147db96d56Sopenharmony_ci def _close(self): 3157db96d56Sopenharmony_ci "Extend base method - clear breaks when module is closed" 3167db96d56Sopenharmony_ci self.clear_file_breaks() 3177db96d56Sopenharmony_ci EditorWindow._close(self) 3187db96d56Sopenharmony_ci 3197db96d56Sopenharmony_ci 3207db96d56Sopenharmony_ciclass PyShellFileList(FileList): 3217db96d56Sopenharmony_ci "Extend base class: IDLE supports a shell and breakpoints" 3227db96d56Sopenharmony_ci 3237db96d56Sopenharmony_ci # override FileList's class variable, instances return PyShellEditorWindow 3247db96d56Sopenharmony_ci # instead of EditorWindow when new edit windows are created. 3257db96d56Sopenharmony_ci EditorWindow = PyShellEditorWindow 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci pyshell = None 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci def open_shell(self, event=None): 3307db96d56Sopenharmony_ci if self.pyshell: 3317db96d56Sopenharmony_ci self.pyshell.top.wakeup() 3327db96d56Sopenharmony_ci else: 3337db96d56Sopenharmony_ci self.pyshell = PyShell(self) 3347db96d56Sopenharmony_ci if self.pyshell: 3357db96d56Sopenharmony_ci if not self.pyshell.begin(): 3367db96d56Sopenharmony_ci return None 3377db96d56Sopenharmony_ci return self.pyshell 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_ci 3407db96d56Sopenharmony_ciclass ModifiedColorDelegator(ColorDelegator): 3417db96d56Sopenharmony_ci "Extend base class: colorizer for the shell window itself" 3427db96d56Sopenharmony_ci def recolorize_main(self): 3437db96d56Sopenharmony_ci self.tag_remove("TODO", "1.0", "iomark") 3447db96d56Sopenharmony_ci self.tag_add("SYNC", "1.0", "iomark") 3457db96d56Sopenharmony_ci ColorDelegator.recolorize_main(self) 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci def removecolors(self): 3487db96d56Sopenharmony_ci # Don't remove shell color tags before "iomark" 3497db96d56Sopenharmony_ci for tag in self.tagdefs: 3507db96d56Sopenharmony_ci self.tag_remove(tag, "iomark", "end") 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ciclass ModifiedUndoDelegator(UndoDelegator): 3547db96d56Sopenharmony_ci "Extend base class: forbid insert/delete before the I/O mark" 3557db96d56Sopenharmony_ci def insert(self, index, chars, tags=None): 3567db96d56Sopenharmony_ci try: 3577db96d56Sopenharmony_ci if self.delegate.compare(index, "<", "iomark"): 3587db96d56Sopenharmony_ci self.delegate.bell() 3597db96d56Sopenharmony_ci return 3607db96d56Sopenharmony_ci except TclError: 3617db96d56Sopenharmony_ci pass 3627db96d56Sopenharmony_ci UndoDelegator.insert(self, index, chars, tags) 3637db96d56Sopenharmony_ci 3647db96d56Sopenharmony_ci def delete(self, index1, index2=None): 3657db96d56Sopenharmony_ci try: 3667db96d56Sopenharmony_ci if self.delegate.compare(index1, "<", "iomark"): 3677db96d56Sopenharmony_ci self.delegate.bell() 3687db96d56Sopenharmony_ci return 3697db96d56Sopenharmony_ci except TclError: 3707db96d56Sopenharmony_ci pass 3717db96d56Sopenharmony_ci UndoDelegator.delete(self, index1, index2) 3727db96d56Sopenharmony_ci 3737db96d56Sopenharmony_ci def undo_event(self, event): 3747db96d56Sopenharmony_ci # Temporarily monkey-patch the delegate's .insert() method to 3757db96d56Sopenharmony_ci # always use the "stdin" tag. This is needed for undo-ing 3767db96d56Sopenharmony_ci # deletions to preserve the "stdin" tag, because UndoDelegator 3777db96d56Sopenharmony_ci # doesn't preserve tags for deleted text. 3787db96d56Sopenharmony_ci orig_insert = self.delegate.insert 3797db96d56Sopenharmony_ci self.delegate.insert = \ 3807db96d56Sopenharmony_ci lambda index, chars: orig_insert(index, chars, "stdin") 3817db96d56Sopenharmony_ci try: 3827db96d56Sopenharmony_ci super().undo_event(event) 3837db96d56Sopenharmony_ci finally: 3847db96d56Sopenharmony_ci self.delegate.insert = orig_insert 3857db96d56Sopenharmony_ci 3867db96d56Sopenharmony_ci 3877db96d56Sopenharmony_ciclass UserInputTaggingDelegator(Delegator): 3887db96d56Sopenharmony_ci """Delegator used to tag user input with "stdin".""" 3897db96d56Sopenharmony_ci def insert(self, index, chars, tags=None): 3907db96d56Sopenharmony_ci if tags is None: 3917db96d56Sopenharmony_ci tags = "stdin" 3927db96d56Sopenharmony_ci self.delegate.insert(index, chars, tags) 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci 3957db96d56Sopenharmony_ciclass MyRPCClient(rpc.RPCClient): 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ci def handle_EOF(self): 3987db96d56Sopenharmony_ci "Override the base class - just re-raise EOFError" 3997db96d56Sopenharmony_ci raise EOFError 4007db96d56Sopenharmony_ci 4017db96d56Sopenharmony_cidef restart_line(width, filename): # See bpo-38141. 4027db96d56Sopenharmony_ci """Return width long restart line formatted with filename. 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ci Fill line with balanced '='s, with any extras and at least one at 4057db96d56Sopenharmony_ci the beginning. Do not end with a trailing space. 4067db96d56Sopenharmony_ci """ 4077db96d56Sopenharmony_ci tag = f"= RESTART: {filename or 'Shell'} =" 4087db96d56Sopenharmony_ci if width >= len(tag): 4097db96d56Sopenharmony_ci div, mod = divmod((width -len(tag)), 2) 4107db96d56Sopenharmony_ci return f"{(div+mod)*'='}{tag}{div*'='}" 4117db96d56Sopenharmony_ci else: 4127db96d56Sopenharmony_ci return tag[:-2] # Remove ' ='. 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_ci 4157db96d56Sopenharmony_ciclass ModifiedInterpreter(InteractiveInterpreter): 4167db96d56Sopenharmony_ci 4177db96d56Sopenharmony_ci def __init__(self, tkconsole): 4187db96d56Sopenharmony_ci self.tkconsole = tkconsole 4197db96d56Sopenharmony_ci locals = sys.modules['__main__'].__dict__ 4207db96d56Sopenharmony_ci InteractiveInterpreter.__init__(self, locals=locals) 4217db96d56Sopenharmony_ci self.restarting = False 4227db96d56Sopenharmony_ci self.subprocess_arglist = None 4237db96d56Sopenharmony_ci self.port = PORT 4247db96d56Sopenharmony_ci self.original_compiler_flags = self.compile.compiler.flags 4257db96d56Sopenharmony_ci 4267db96d56Sopenharmony_ci _afterid = None 4277db96d56Sopenharmony_ci rpcclt = None 4287db96d56Sopenharmony_ci rpcsubproc = None 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci def spawn_subprocess(self): 4317db96d56Sopenharmony_ci if self.subprocess_arglist is None: 4327db96d56Sopenharmony_ci self.subprocess_arglist = self.build_subprocess_arglist() 4337db96d56Sopenharmony_ci self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci def build_subprocess_arglist(self): 4367db96d56Sopenharmony_ci assert (self.port!=0), ( 4377db96d56Sopenharmony_ci "Socket should have been assigned a port number.") 4387db96d56Sopenharmony_ci w = ['-W' + s for s in sys.warnoptions] 4397db96d56Sopenharmony_ci # Maybe IDLE is installed and is being accessed via sys.path, 4407db96d56Sopenharmony_ci # or maybe it's not installed and the idle.py script is being 4417db96d56Sopenharmony_ci # run from the IDLE source directory. 4427db96d56Sopenharmony_ci del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', 4437db96d56Sopenharmony_ci default=False, type='bool') 4447db96d56Sopenharmony_ci command = f"__import__('idlelib.run').run.main({del_exitf!r})" 4457db96d56Sopenharmony_ci return [sys.executable] + w + ["-c", command, str(self.port)] 4467db96d56Sopenharmony_ci 4477db96d56Sopenharmony_ci def start_subprocess(self): 4487db96d56Sopenharmony_ci addr = (HOST, self.port) 4497db96d56Sopenharmony_ci # GUI makes several attempts to acquire socket, listens for connection 4507db96d56Sopenharmony_ci for i in range(3): 4517db96d56Sopenharmony_ci time.sleep(i) 4527db96d56Sopenharmony_ci try: 4537db96d56Sopenharmony_ci self.rpcclt = MyRPCClient(addr) 4547db96d56Sopenharmony_ci break 4557db96d56Sopenharmony_ci except OSError: 4567db96d56Sopenharmony_ci pass 4577db96d56Sopenharmony_ci else: 4587db96d56Sopenharmony_ci self.display_port_binding_error() 4597db96d56Sopenharmony_ci return None 4607db96d56Sopenharmony_ci # if PORT was 0, system will assign an 'ephemeral' port. Find it out: 4617db96d56Sopenharmony_ci self.port = self.rpcclt.listening_sock.getsockname()[1] 4627db96d56Sopenharmony_ci # if PORT was not 0, probably working with a remote execution server 4637db96d56Sopenharmony_ci if PORT != 0: 4647db96d56Sopenharmony_ci # To allow reconnection within the 2MSL wait (cf. Stevens TCP 4657db96d56Sopenharmony_ci # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic 4667db96d56Sopenharmony_ci # on Windows since the implementation allows two active sockets on 4677db96d56Sopenharmony_ci # the same address! 4687db96d56Sopenharmony_ci self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, 4697db96d56Sopenharmony_ci socket.SO_REUSEADDR, 1) 4707db96d56Sopenharmony_ci self.spawn_subprocess() 4717db96d56Sopenharmony_ci #time.sleep(20) # test to simulate GUI not accepting connection 4727db96d56Sopenharmony_ci # Accept the connection from the Python execution server 4737db96d56Sopenharmony_ci self.rpcclt.listening_sock.settimeout(10) 4747db96d56Sopenharmony_ci try: 4757db96d56Sopenharmony_ci self.rpcclt.accept() 4767db96d56Sopenharmony_ci except TimeoutError: 4777db96d56Sopenharmony_ci self.display_no_subprocess_error() 4787db96d56Sopenharmony_ci return None 4797db96d56Sopenharmony_ci self.rpcclt.register("console", self.tkconsole) 4807db96d56Sopenharmony_ci self.rpcclt.register("stdin", self.tkconsole.stdin) 4817db96d56Sopenharmony_ci self.rpcclt.register("stdout", self.tkconsole.stdout) 4827db96d56Sopenharmony_ci self.rpcclt.register("stderr", self.tkconsole.stderr) 4837db96d56Sopenharmony_ci self.rpcclt.register("flist", self.tkconsole.flist) 4847db96d56Sopenharmony_ci self.rpcclt.register("linecache", linecache) 4857db96d56Sopenharmony_ci self.rpcclt.register("interp", self) 4867db96d56Sopenharmony_ci self.transfer_path(with_cwd=True) 4877db96d56Sopenharmony_ci self.poll_subprocess() 4887db96d56Sopenharmony_ci return self.rpcclt 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ci def restart_subprocess(self, with_cwd=False, filename=''): 4917db96d56Sopenharmony_ci if self.restarting: 4927db96d56Sopenharmony_ci return self.rpcclt 4937db96d56Sopenharmony_ci self.restarting = True 4947db96d56Sopenharmony_ci # close only the subprocess debugger 4957db96d56Sopenharmony_ci debug = self.getdebugger() 4967db96d56Sopenharmony_ci if debug: 4977db96d56Sopenharmony_ci try: 4987db96d56Sopenharmony_ci # Only close subprocess debugger, don't unregister gui_adap! 4997db96d56Sopenharmony_ci debugger_r.close_subprocess_debugger(self.rpcclt) 5007db96d56Sopenharmony_ci except: 5017db96d56Sopenharmony_ci pass 5027db96d56Sopenharmony_ci # Kill subprocess, spawn a new one, accept connection. 5037db96d56Sopenharmony_ci self.rpcclt.close() 5047db96d56Sopenharmony_ci self.terminate_subprocess() 5057db96d56Sopenharmony_ci console = self.tkconsole 5067db96d56Sopenharmony_ci was_executing = console.executing 5077db96d56Sopenharmony_ci console.executing = False 5087db96d56Sopenharmony_ci self.spawn_subprocess() 5097db96d56Sopenharmony_ci try: 5107db96d56Sopenharmony_ci self.rpcclt.accept() 5117db96d56Sopenharmony_ci except TimeoutError: 5127db96d56Sopenharmony_ci self.display_no_subprocess_error() 5137db96d56Sopenharmony_ci return None 5147db96d56Sopenharmony_ci self.transfer_path(with_cwd=with_cwd) 5157db96d56Sopenharmony_ci console.stop_readline() 5167db96d56Sopenharmony_ci # annotate restart in shell window and mark it 5177db96d56Sopenharmony_ci console.text.delete("iomark", "end-1c") 5187db96d56Sopenharmony_ci console.write('\n') 5197db96d56Sopenharmony_ci console.write(restart_line(console.width, filename)) 5207db96d56Sopenharmony_ci console.text.mark_set("restart", "end-1c") 5217db96d56Sopenharmony_ci console.text.mark_gravity("restart", "left") 5227db96d56Sopenharmony_ci if not filename: 5237db96d56Sopenharmony_ci console.showprompt() 5247db96d56Sopenharmony_ci # restart subprocess debugger 5257db96d56Sopenharmony_ci if debug: 5267db96d56Sopenharmony_ci # Restarted debugger connects to current instance of debug GUI 5277db96d56Sopenharmony_ci debugger_r.restart_subprocess_debugger(self.rpcclt) 5287db96d56Sopenharmony_ci # reload remote debugger breakpoints for all PyShellEditWindows 5297db96d56Sopenharmony_ci debug.load_breakpoints() 5307db96d56Sopenharmony_ci self.compile.compiler.flags = self.original_compiler_flags 5317db96d56Sopenharmony_ci self.restarting = False 5327db96d56Sopenharmony_ci return self.rpcclt 5337db96d56Sopenharmony_ci 5347db96d56Sopenharmony_ci def __request_interrupt(self): 5357db96d56Sopenharmony_ci self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) 5367db96d56Sopenharmony_ci 5377db96d56Sopenharmony_ci def interrupt_subprocess(self): 5387db96d56Sopenharmony_ci threading.Thread(target=self.__request_interrupt).start() 5397db96d56Sopenharmony_ci 5407db96d56Sopenharmony_ci def kill_subprocess(self): 5417db96d56Sopenharmony_ci if self._afterid is not None: 5427db96d56Sopenharmony_ci self.tkconsole.text.after_cancel(self._afterid) 5437db96d56Sopenharmony_ci try: 5447db96d56Sopenharmony_ci self.rpcclt.listening_sock.close() 5457db96d56Sopenharmony_ci except AttributeError: # no socket 5467db96d56Sopenharmony_ci pass 5477db96d56Sopenharmony_ci try: 5487db96d56Sopenharmony_ci self.rpcclt.close() 5497db96d56Sopenharmony_ci except AttributeError: # no socket 5507db96d56Sopenharmony_ci pass 5517db96d56Sopenharmony_ci self.terminate_subprocess() 5527db96d56Sopenharmony_ci self.tkconsole.executing = False 5537db96d56Sopenharmony_ci self.rpcclt = None 5547db96d56Sopenharmony_ci 5557db96d56Sopenharmony_ci def terminate_subprocess(self): 5567db96d56Sopenharmony_ci "Make sure subprocess is terminated" 5577db96d56Sopenharmony_ci try: 5587db96d56Sopenharmony_ci self.rpcsubproc.kill() 5597db96d56Sopenharmony_ci except OSError: 5607db96d56Sopenharmony_ci # process already terminated 5617db96d56Sopenharmony_ci return 5627db96d56Sopenharmony_ci else: 5637db96d56Sopenharmony_ci try: 5647db96d56Sopenharmony_ci self.rpcsubproc.wait() 5657db96d56Sopenharmony_ci except OSError: 5667db96d56Sopenharmony_ci return 5677db96d56Sopenharmony_ci 5687db96d56Sopenharmony_ci def transfer_path(self, with_cwd=False): 5697db96d56Sopenharmony_ci if with_cwd: # Issue 13506 5707db96d56Sopenharmony_ci path = [''] # include Current Working Directory 5717db96d56Sopenharmony_ci path.extend(sys.path) 5727db96d56Sopenharmony_ci else: 5737db96d56Sopenharmony_ci path = sys.path 5747db96d56Sopenharmony_ci 5757db96d56Sopenharmony_ci self.runcommand("""if 1: 5767db96d56Sopenharmony_ci import sys as _sys 5777db96d56Sopenharmony_ci _sys.path = {!r} 5787db96d56Sopenharmony_ci del _sys 5797db96d56Sopenharmony_ci \n""".format(path)) 5807db96d56Sopenharmony_ci 5817db96d56Sopenharmony_ci active_seq = None 5827db96d56Sopenharmony_ci 5837db96d56Sopenharmony_ci def poll_subprocess(self): 5847db96d56Sopenharmony_ci clt = self.rpcclt 5857db96d56Sopenharmony_ci if clt is None: 5867db96d56Sopenharmony_ci return 5877db96d56Sopenharmony_ci try: 5887db96d56Sopenharmony_ci response = clt.pollresponse(self.active_seq, wait=0.05) 5897db96d56Sopenharmony_ci except (EOFError, OSError, KeyboardInterrupt): 5907db96d56Sopenharmony_ci # lost connection or subprocess terminated itself, restart 5917db96d56Sopenharmony_ci # [the KBI is from rpc.SocketIO.handle_EOF()] 5927db96d56Sopenharmony_ci if self.tkconsole.closing: 5937db96d56Sopenharmony_ci return 5947db96d56Sopenharmony_ci response = None 5957db96d56Sopenharmony_ci self.restart_subprocess() 5967db96d56Sopenharmony_ci if response: 5977db96d56Sopenharmony_ci self.tkconsole.resetoutput() 5987db96d56Sopenharmony_ci self.active_seq = None 5997db96d56Sopenharmony_ci how, what = response 6007db96d56Sopenharmony_ci console = self.tkconsole.console 6017db96d56Sopenharmony_ci if how == "OK": 6027db96d56Sopenharmony_ci if what is not None: 6037db96d56Sopenharmony_ci print(repr(what), file=console) 6047db96d56Sopenharmony_ci elif how == "EXCEPTION": 6057db96d56Sopenharmony_ci if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 6067db96d56Sopenharmony_ci self.remote_stack_viewer() 6077db96d56Sopenharmony_ci elif how == "ERROR": 6087db96d56Sopenharmony_ci errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n" 6097db96d56Sopenharmony_ci print(errmsg, what, file=sys.__stderr__) 6107db96d56Sopenharmony_ci print(errmsg, what, file=console) 6117db96d56Sopenharmony_ci # we received a response to the currently active seq number: 6127db96d56Sopenharmony_ci try: 6137db96d56Sopenharmony_ci self.tkconsole.endexecuting() 6147db96d56Sopenharmony_ci except AttributeError: # shell may have closed 6157db96d56Sopenharmony_ci pass 6167db96d56Sopenharmony_ci # Reschedule myself 6177db96d56Sopenharmony_ci if not self.tkconsole.closing: 6187db96d56Sopenharmony_ci self._afterid = self.tkconsole.text.after( 6197db96d56Sopenharmony_ci self.tkconsole.pollinterval, self.poll_subprocess) 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ci debugger = None 6227db96d56Sopenharmony_ci 6237db96d56Sopenharmony_ci def setdebugger(self, debugger): 6247db96d56Sopenharmony_ci self.debugger = debugger 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci def getdebugger(self): 6277db96d56Sopenharmony_ci return self.debugger 6287db96d56Sopenharmony_ci 6297db96d56Sopenharmony_ci def open_remote_stack_viewer(self): 6307db96d56Sopenharmony_ci """Initiate the remote stack viewer from a separate thread. 6317db96d56Sopenharmony_ci 6327db96d56Sopenharmony_ci This method is called from the subprocess, and by returning from this 6337db96d56Sopenharmony_ci method we allow the subprocess to unblock. After a bit the shell 6347db96d56Sopenharmony_ci requests the subprocess to open the remote stack viewer which returns a 6357db96d56Sopenharmony_ci static object looking at the last exception. It is queried through 6367db96d56Sopenharmony_ci the RPC mechanism. 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci """ 6397db96d56Sopenharmony_ci self.tkconsole.text.after(300, self.remote_stack_viewer) 6407db96d56Sopenharmony_ci return 6417db96d56Sopenharmony_ci 6427db96d56Sopenharmony_ci def remote_stack_viewer(self): 6437db96d56Sopenharmony_ci from idlelib import debugobj_r 6447db96d56Sopenharmony_ci oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) 6457db96d56Sopenharmony_ci if oid is None: 6467db96d56Sopenharmony_ci self.tkconsole.root.bell() 6477db96d56Sopenharmony_ci return 6487db96d56Sopenharmony_ci item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid) 6497db96d56Sopenharmony_ci from idlelib.tree import ScrolledCanvas, TreeNode 6507db96d56Sopenharmony_ci top = Toplevel(self.tkconsole.root) 6517db96d56Sopenharmony_ci theme = idleConf.CurrentTheme() 6527db96d56Sopenharmony_ci background = idleConf.GetHighlight(theme, 'normal')['background'] 6537db96d56Sopenharmony_ci sc = ScrolledCanvas(top, bg=background, highlightthickness=0) 6547db96d56Sopenharmony_ci sc.frame.pack(expand=1, fill="both") 6557db96d56Sopenharmony_ci node = TreeNode(sc.canvas, None, item) 6567db96d56Sopenharmony_ci node.expand() 6577db96d56Sopenharmony_ci # XXX Should GC the remote tree when closing the window 6587db96d56Sopenharmony_ci 6597db96d56Sopenharmony_ci gid = 0 6607db96d56Sopenharmony_ci 6617db96d56Sopenharmony_ci def execsource(self, source): 6627db96d56Sopenharmony_ci "Like runsource() but assumes complete exec source" 6637db96d56Sopenharmony_ci filename = self.stuffsource(source) 6647db96d56Sopenharmony_ci self.execfile(filename, source) 6657db96d56Sopenharmony_ci 6667db96d56Sopenharmony_ci def execfile(self, filename, source=None): 6677db96d56Sopenharmony_ci "Execute an existing file" 6687db96d56Sopenharmony_ci if source is None: 6697db96d56Sopenharmony_ci with tokenize.open(filename) as fp: 6707db96d56Sopenharmony_ci source = fp.read() 6717db96d56Sopenharmony_ci if use_subprocess: 6727db96d56Sopenharmony_ci source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n" 6737db96d56Sopenharmony_ci + source + "\ndel __file__") 6747db96d56Sopenharmony_ci try: 6757db96d56Sopenharmony_ci code = compile(source, filename, "exec") 6767db96d56Sopenharmony_ci except (OverflowError, SyntaxError): 6777db96d56Sopenharmony_ci self.tkconsole.resetoutput() 6787db96d56Sopenharmony_ci print('*** Error in script or command!\n' 6797db96d56Sopenharmony_ci 'Traceback (most recent call last):', 6807db96d56Sopenharmony_ci file=self.tkconsole.stderr) 6817db96d56Sopenharmony_ci InteractiveInterpreter.showsyntaxerror(self, filename) 6827db96d56Sopenharmony_ci self.tkconsole.showprompt() 6837db96d56Sopenharmony_ci else: 6847db96d56Sopenharmony_ci self.runcode(code) 6857db96d56Sopenharmony_ci 6867db96d56Sopenharmony_ci def runsource(self, source): 6877db96d56Sopenharmony_ci "Extend base class method: Stuff the source in the line cache first" 6887db96d56Sopenharmony_ci filename = self.stuffsource(source) 6897db96d56Sopenharmony_ci # at the moment, InteractiveInterpreter expects str 6907db96d56Sopenharmony_ci assert isinstance(source, str) 6917db96d56Sopenharmony_ci # InteractiveInterpreter.runsource() calls its runcode() method, 6927db96d56Sopenharmony_ci # which is overridden (see below) 6937db96d56Sopenharmony_ci return InteractiveInterpreter.runsource(self, source, filename) 6947db96d56Sopenharmony_ci 6957db96d56Sopenharmony_ci def stuffsource(self, source): 6967db96d56Sopenharmony_ci "Stuff source in the filename cache" 6977db96d56Sopenharmony_ci filename = "<pyshell#%d>" % self.gid 6987db96d56Sopenharmony_ci self.gid = self.gid + 1 6997db96d56Sopenharmony_ci lines = source.split("\n") 7007db96d56Sopenharmony_ci linecache.cache[filename] = len(source)+1, 0, lines, filename 7017db96d56Sopenharmony_ci return filename 7027db96d56Sopenharmony_ci 7037db96d56Sopenharmony_ci def prepend_syspath(self, filename): 7047db96d56Sopenharmony_ci "Prepend sys.path with file's directory if not already included" 7057db96d56Sopenharmony_ci self.runcommand("""if 1: 7067db96d56Sopenharmony_ci _filename = {!r} 7077db96d56Sopenharmony_ci import sys as _sys 7087db96d56Sopenharmony_ci from os.path import dirname as _dirname 7097db96d56Sopenharmony_ci _dir = _dirname(_filename) 7107db96d56Sopenharmony_ci if not _dir in _sys.path: 7117db96d56Sopenharmony_ci _sys.path.insert(0, _dir) 7127db96d56Sopenharmony_ci del _filename, _sys, _dirname, _dir 7137db96d56Sopenharmony_ci \n""".format(filename)) 7147db96d56Sopenharmony_ci 7157db96d56Sopenharmony_ci def showsyntaxerror(self, filename=None): 7167db96d56Sopenharmony_ci """Override Interactive Interpreter method: Use Colorizing 7177db96d56Sopenharmony_ci 7187db96d56Sopenharmony_ci Color the offending position instead of printing it and pointing at it 7197db96d56Sopenharmony_ci with a caret. 7207db96d56Sopenharmony_ci 7217db96d56Sopenharmony_ci """ 7227db96d56Sopenharmony_ci tkconsole = self.tkconsole 7237db96d56Sopenharmony_ci text = tkconsole.text 7247db96d56Sopenharmony_ci text.tag_remove("ERROR", "1.0", "end") 7257db96d56Sopenharmony_ci type, value, tb = sys.exc_info() 7267db96d56Sopenharmony_ci msg = getattr(value, 'msg', '') or value or "<no detail available>" 7277db96d56Sopenharmony_ci lineno = getattr(value, 'lineno', '') or 1 7287db96d56Sopenharmony_ci offset = getattr(value, 'offset', '') or 0 7297db96d56Sopenharmony_ci if offset == 0: 7307db96d56Sopenharmony_ci lineno += 1 #mark end of offending line 7317db96d56Sopenharmony_ci if lineno == 1: 7327db96d56Sopenharmony_ci pos = "iomark + %d chars" % (offset-1) 7337db96d56Sopenharmony_ci else: 7347db96d56Sopenharmony_ci pos = "iomark linestart + %d lines + %d chars" % \ 7357db96d56Sopenharmony_ci (lineno-1, offset-1) 7367db96d56Sopenharmony_ci tkconsole.colorize_syntax_error(text, pos) 7377db96d56Sopenharmony_ci tkconsole.resetoutput() 7387db96d56Sopenharmony_ci self.write("SyntaxError: %s\n" % msg) 7397db96d56Sopenharmony_ci tkconsole.showprompt() 7407db96d56Sopenharmony_ci 7417db96d56Sopenharmony_ci def showtraceback(self): 7427db96d56Sopenharmony_ci "Extend base class method to reset output properly" 7437db96d56Sopenharmony_ci self.tkconsole.resetoutput() 7447db96d56Sopenharmony_ci self.checklinecache() 7457db96d56Sopenharmony_ci InteractiveInterpreter.showtraceback(self) 7467db96d56Sopenharmony_ci if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"): 7477db96d56Sopenharmony_ci self.tkconsole.open_stack_viewer() 7487db96d56Sopenharmony_ci 7497db96d56Sopenharmony_ci def checklinecache(self): 7507db96d56Sopenharmony_ci c = linecache.cache 7517db96d56Sopenharmony_ci for key in list(c.keys()): 7527db96d56Sopenharmony_ci if key[:1] + key[-1:] != "<>": 7537db96d56Sopenharmony_ci del c[key] 7547db96d56Sopenharmony_ci 7557db96d56Sopenharmony_ci def runcommand(self, code): 7567db96d56Sopenharmony_ci "Run the code without invoking the debugger" 7577db96d56Sopenharmony_ci # The code better not raise an exception! 7587db96d56Sopenharmony_ci if self.tkconsole.executing: 7597db96d56Sopenharmony_ci self.display_executing_dialog() 7607db96d56Sopenharmony_ci return 0 7617db96d56Sopenharmony_ci if self.rpcclt: 7627db96d56Sopenharmony_ci self.rpcclt.remotequeue("exec", "runcode", (code,), {}) 7637db96d56Sopenharmony_ci else: 7647db96d56Sopenharmony_ci exec(code, self.locals) 7657db96d56Sopenharmony_ci return 1 7667db96d56Sopenharmony_ci 7677db96d56Sopenharmony_ci def runcode(self, code): 7687db96d56Sopenharmony_ci "Override base class method" 7697db96d56Sopenharmony_ci if self.tkconsole.executing: 7707db96d56Sopenharmony_ci self.restart_subprocess() 7717db96d56Sopenharmony_ci self.checklinecache() 7727db96d56Sopenharmony_ci debugger = self.debugger 7737db96d56Sopenharmony_ci try: 7747db96d56Sopenharmony_ci self.tkconsole.beginexecuting() 7757db96d56Sopenharmony_ci if not debugger and self.rpcclt is not None: 7767db96d56Sopenharmony_ci self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", 7777db96d56Sopenharmony_ci (code,), {}) 7787db96d56Sopenharmony_ci elif debugger: 7797db96d56Sopenharmony_ci debugger.run(code, self.locals) 7807db96d56Sopenharmony_ci else: 7817db96d56Sopenharmony_ci exec(code, self.locals) 7827db96d56Sopenharmony_ci except SystemExit: 7837db96d56Sopenharmony_ci if not self.tkconsole.closing: 7847db96d56Sopenharmony_ci if messagebox.askyesno( 7857db96d56Sopenharmony_ci "Exit?", 7867db96d56Sopenharmony_ci "Do you want to exit altogether?", 7877db96d56Sopenharmony_ci default="yes", 7887db96d56Sopenharmony_ci parent=self.tkconsole.text): 7897db96d56Sopenharmony_ci raise 7907db96d56Sopenharmony_ci else: 7917db96d56Sopenharmony_ci self.showtraceback() 7927db96d56Sopenharmony_ci else: 7937db96d56Sopenharmony_ci raise 7947db96d56Sopenharmony_ci except: 7957db96d56Sopenharmony_ci if use_subprocess: 7967db96d56Sopenharmony_ci print("IDLE internal error in runcode()", 7977db96d56Sopenharmony_ci file=self.tkconsole.stderr) 7987db96d56Sopenharmony_ci self.showtraceback() 7997db96d56Sopenharmony_ci self.tkconsole.endexecuting() 8007db96d56Sopenharmony_ci else: 8017db96d56Sopenharmony_ci if self.tkconsole.canceled: 8027db96d56Sopenharmony_ci self.tkconsole.canceled = False 8037db96d56Sopenharmony_ci print("KeyboardInterrupt", file=self.tkconsole.stderr) 8047db96d56Sopenharmony_ci else: 8057db96d56Sopenharmony_ci self.showtraceback() 8067db96d56Sopenharmony_ci finally: 8077db96d56Sopenharmony_ci if not use_subprocess: 8087db96d56Sopenharmony_ci try: 8097db96d56Sopenharmony_ci self.tkconsole.endexecuting() 8107db96d56Sopenharmony_ci except AttributeError: # shell may have closed 8117db96d56Sopenharmony_ci pass 8127db96d56Sopenharmony_ci 8137db96d56Sopenharmony_ci def write(self, s): 8147db96d56Sopenharmony_ci "Override base class method" 8157db96d56Sopenharmony_ci return self.tkconsole.stderr.write(s) 8167db96d56Sopenharmony_ci 8177db96d56Sopenharmony_ci def display_port_binding_error(self): 8187db96d56Sopenharmony_ci messagebox.showerror( 8197db96d56Sopenharmony_ci "Port Binding Error", 8207db96d56Sopenharmony_ci "IDLE can't bind to a TCP/IP port, which is necessary to " 8217db96d56Sopenharmony_ci "communicate with its Python execution server. This might be " 8227db96d56Sopenharmony_ci "because no networking is installed on this computer. " 8237db96d56Sopenharmony_ci "Run IDLE with the -n command line switch to start without a " 8247db96d56Sopenharmony_ci "subprocess and refer to Help/IDLE Help 'Running without a " 8257db96d56Sopenharmony_ci "subprocess' for further details.", 8267db96d56Sopenharmony_ci parent=self.tkconsole.text) 8277db96d56Sopenharmony_ci 8287db96d56Sopenharmony_ci def display_no_subprocess_error(self): 8297db96d56Sopenharmony_ci messagebox.showerror( 8307db96d56Sopenharmony_ci "Subprocess Connection Error", 8317db96d56Sopenharmony_ci "IDLE's subprocess didn't make connection.\n" 8327db96d56Sopenharmony_ci "See the 'Startup failure' section of the IDLE doc, online at\n" 8337db96d56Sopenharmony_ci "https://docs.python.org/3/library/idle.html#startup-failure", 8347db96d56Sopenharmony_ci parent=self.tkconsole.text) 8357db96d56Sopenharmony_ci 8367db96d56Sopenharmony_ci def display_executing_dialog(self): 8377db96d56Sopenharmony_ci messagebox.showerror( 8387db96d56Sopenharmony_ci "Already executing", 8397db96d56Sopenharmony_ci "The Python Shell window is already executing a command; " 8407db96d56Sopenharmony_ci "please wait until it is finished.", 8417db96d56Sopenharmony_ci parent=self.tkconsole.text) 8427db96d56Sopenharmony_ci 8437db96d56Sopenharmony_ci 8447db96d56Sopenharmony_ciclass PyShell(OutputWindow): 8457db96d56Sopenharmony_ci from idlelib.squeezer import Squeezer 8467db96d56Sopenharmony_ci 8477db96d56Sopenharmony_ci shell_title = "IDLE Shell " + python_version() 8487db96d56Sopenharmony_ci 8497db96d56Sopenharmony_ci # Override classes 8507db96d56Sopenharmony_ci ColorDelegator = ModifiedColorDelegator 8517db96d56Sopenharmony_ci UndoDelegator = ModifiedUndoDelegator 8527db96d56Sopenharmony_ci 8537db96d56Sopenharmony_ci # Override menus 8547db96d56Sopenharmony_ci menu_specs = [ 8557db96d56Sopenharmony_ci ("file", "_File"), 8567db96d56Sopenharmony_ci ("edit", "_Edit"), 8577db96d56Sopenharmony_ci ("debug", "_Debug"), 8587db96d56Sopenharmony_ci ("options", "_Options"), 8597db96d56Sopenharmony_ci ("window", "_Window"), 8607db96d56Sopenharmony_ci ("help", "_Help"), 8617db96d56Sopenharmony_ci ] 8627db96d56Sopenharmony_ci 8637db96d56Sopenharmony_ci # Extend right-click context menu 8647db96d56Sopenharmony_ci rmenu_specs = OutputWindow.rmenu_specs + [ 8657db96d56Sopenharmony_ci ("Squeeze", "<<squeeze-current-text>>"), 8667db96d56Sopenharmony_ci ] 8677db96d56Sopenharmony_ci _idx = 1 + len(list(itertools.takewhile( 8687db96d56Sopenharmony_ci lambda rmenu_item: rmenu_item[0] != "Copy", rmenu_specs) 8697db96d56Sopenharmony_ci )) 8707db96d56Sopenharmony_ci rmenu_specs.insert(_idx, ("Copy with prompts", 8717db96d56Sopenharmony_ci "<<copy-with-prompts>>", 8727db96d56Sopenharmony_ci "rmenu_check_copy")) 8737db96d56Sopenharmony_ci del _idx 8747db96d56Sopenharmony_ci 8757db96d56Sopenharmony_ci allow_line_numbers = False 8767db96d56Sopenharmony_ci user_input_insert_tags = "stdin" 8777db96d56Sopenharmony_ci 8787db96d56Sopenharmony_ci # New classes 8797db96d56Sopenharmony_ci from idlelib.history import History 8807db96d56Sopenharmony_ci from idlelib.sidebar import ShellSidebar 8817db96d56Sopenharmony_ci 8827db96d56Sopenharmony_ci def __init__(self, flist=None): 8837db96d56Sopenharmony_ci if use_subprocess: 8847db96d56Sopenharmony_ci ms = self.menu_specs 8857db96d56Sopenharmony_ci if ms[2][0] != "shell": 8867db96d56Sopenharmony_ci ms.insert(2, ("shell", "She_ll")) 8877db96d56Sopenharmony_ci self.interp = ModifiedInterpreter(self) 8887db96d56Sopenharmony_ci if flist is None: 8897db96d56Sopenharmony_ci root = Tk() 8907db96d56Sopenharmony_ci fixwordbreaks(root) 8917db96d56Sopenharmony_ci root.withdraw() 8927db96d56Sopenharmony_ci flist = PyShellFileList(root) 8937db96d56Sopenharmony_ci 8947db96d56Sopenharmony_ci self.shell_sidebar = None # initialized below 8957db96d56Sopenharmony_ci 8967db96d56Sopenharmony_ci OutputWindow.__init__(self, flist, None, None) 8977db96d56Sopenharmony_ci 8987db96d56Sopenharmony_ci self.usetabs = False 8997db96d56Sopenharmony_ci # indentwidth must be 8 when using tabs. See note in EditorWindow: 9007db96d56Sopenharmony_ci self.indentwidth = 4 9017db96d56Sopenharmony_ci 9027db96d56Sopenharmony_ci self.sys_ps1 = sys.ps1 if hasattr(sys, 'ps1') else '>>>\n' 9037db96d56Sopenharmony_ci self.prompt_last_line = self.sys_ps1.split('\n')[-1] 9047db96d56Sopenharmony_ci self.prompt = self.sys_ps1 # Changes when debug active 9057db96d56Sopenharmony_ci 9067db96d56Sopenharmony_ci text = self.text 9077db96d56Sopenharmony_ci text.configure(wrap="char") 9087db96d56Sopenharmony_ci text.bind("<<newline-and-indent>>", self.enter_callback) 9097db96d56Sopenharmony_ci text.bind("<<plain-newline-and-indent>>", self.linefeed_callback) 9107db96d56Sopenharmony_ci text.bind("<<interrupt-execution>>", self.cancel_callback) 9117db96d56Sopenharmony_ci text.bind("<<end-of-file>>", self.eof_callback) 9127db96d56Sopenharmony_ci text.bind("<<open-stack-viewer>>", self.open_stack_viewer) 9137db96d56Sopenharmony_ci text.bind("<<toggle-debugger>>", self.toggle_debugger) 9147db96d56Sopenharmony_ci text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer) 9157db96d56Sopenharmony_ci text.bind("<<copy-with-prompts>>", self.copy_with_prompts_callback) 9167db96d56Sopenharmony_ci if use_subprocess: 9177db96d56Sopenharmony_ci text.bind("<<view-restart>>", self.view_restart_mark) 9187db96d56Sopenharmony_ci text.bind("<<restart-shell>>", self.restart_shell) 9197db96d56Sopenharmony_ci self.squeezer = self.Squeezer(self) 9207db96d56Sopenharmony_ci text.bind("<<squeeze-current-text>>", 9217db96d56Sopenharmony_ci self.squeeze_current_text_event) 9227db96d56Sopenharmony_ci 9237db96d56Sopenharmony_ci self.save_stdout = sys.stdout 9247db96d56Sopenharmony_ci self.save_stderr = sys.stderr 9257db96d56Sopenharmony_ci self.save_stdin = sys.stdin 9267db96d56Sopenharmony_ci from idlelib import iomenu 9277db96d56Sopenharmony_ci self.stdin = StdInputFile(self, "stdin", 9287db96d56Sopenharmony_ci iomenu.encoding, iomenu.errors) 9297db96d56Sopenharmony_ci self.stdout = StdOutputFile(self, "stdout", 9307db96d56Sopenharmony_ci iomenu.encoding, iomenu.errors) 9317db96d56Sopenharmony_ci self.stderr = StdOutputFile(self, "stderr", 9327db96d56Sopenharmony_ci iomenu.encoding, "backslashreplace") 9337db96d56Sopenharmony_ci self.console = StdOutputFile(self, "console", 9347db96d56Sopenharmony_ci iomenu.encoding, iomenu.errors) 9357db96d56Sopenharmony_ci if not use_subprocess: 9367db96d56Sopenharmony_ci sys.stdout = self.stdout 9377db96d56Sopenharmony_ci sys.stderr = self.stderr 9387db96d56Sopenharmony_ci sys.stdin = self.stdin 9397db96d56Sopenharmony_ci try: 9407db96d56Sopenharmony_ci # page help() text to shell. 9417db96d56Sopenharmony_ci import pydoc # import must be done here to capture i/o rebinding. 9427db96d56Sopenharmony_ci # XXX KBK 27Dec07 use text viewer someday, but must work w/o subproc 9437db96d56Sopenharmony_ci pydoc.pager = pydoc.plainpager 9447db96d56Sopenharmony_ci except: 9457db96d56Sopenharmony_ci sys.stderr = sys.__stderr__ 9467db96d56Sopenharmony_ci raise 9477db96d56Sopenharmony_ci # 9487db96d56Sopenharmony_ci self.history = self.History(self.text) 9497db96d56Sopenharmony_ci # 9507db96d56Sopenharmony_ci self.pollinterval = 50 # millisec 9517db96d56Sopenharmony_ci 9527db96d56Sopenharmony_ci self.shell_sidebar = self.ShellSidebar(self) 9537db96d56Sopenharmony_ci 9547db96d56Sopenharmony_ci # Insert UserInputTaggingDelegator at the top of the percolator, 9557db96d56Sopenharmony_ci # but make calls to text.insert() skip it. This causes only insert 9567db96d56Sopenharmony_ci # events generated in Tcl/Tk to go through this delegator. 9577db96d56Sopenharmony_ci self.text.insert = self.per.top.insert 9587db96d56Sopenharmony_ci self.per.insertfilter(UserInputTaggingDelegator()) 9597db96d56Sopenharmony_ci 9607db96d56Sopenharmony_ci def ResetFont(self): 9617db96d56Sopenharmony_ci super().ResetFont() 9627db96d56Sopenharmony_ci 9637db96d56Sopenharmony_ci if self.shell_sidebar is not None: 9647db96d56Sopenharmony_ci self.shell_sidebar.update_font() 9657db96d56Sopenharmony_ci 9667db96d56Sopenharmony_ci def ResetColorizer(self): 9677db96d56Sopenharmony_ci super().ResetColorizer() 9687db96d56Sopenharmony_ci 9697db96d56Sopenharmony_ci theme = idleConf.CurrentTheme() 9707db96d56Sopenharmony_ci tag_colors = { 9717db96d56Sopenharmony_ci "stdin": {'background': None, 'foreground': None}, 9727db96d56Sopenharmony_ci "stdout": idleConf.GetHighlight(theme, "stdout"), 9737db96d56Sopenharmony_ci "stderr": idleConf.GetHighlight(theme, "stderr"), 9747db96d56Sopenharmony_ci "console": idleConf.GetHighlight(theme, "normal"), 9757db96d56Sopenharmony_ci } 9767db96d56Sopenharmony_ci for tag, tag_colors_config in tag_colors.items(): 9777db96d56Sopenharmony_ci self.text.tag_configure(tag, **tag_colors_config) 9787db96d56Sopenharmony_ci 9797db96d56Sopenharmony_ci if self.shell_sidebar is not None: 9807db96d56Sopenharmony_ci self.shell_sidebar.update_colors() 9817db96d56Sopenharmony_ci 9827db96d56Sopenharmony_ci def replace_event(self, event): 9837db96d56Sopenharmony_ci replace.replace(self.text, insert_tags="stdin") 9847db96d56Sopenharmony_ci return "break" 9857db96d56Sopenharmony_ci 9867db96d56Sopenharmony_ci def get_standard_extension_names(self): 9877db96d56Sopenharmony_ci return idleConf.GetExtensions(shell_only=True) 9887db96d56Sopenharmony_ci 9897db96d56Sopenharmony_ci def get_prompt_text(self, first, last): 9907db96d56Sopenharmony_ci """Return text between first and last with prompts added.""" 9917db96d56Sopenharmony_ci text = self.text.get(first, last) 9927db96d56Sopenharmony_ci lineno_range = range( 9937db96d56Sopenharmony_ci int(float(first)), 9947db96d56Sopenharmony_ci int(float(last)) 9957db96d56Sopenharmony_ci ) 9967db96d56Sopenharmony_ci prompts = [ 9977db96d56Sopenharmony_ci self.shell_sidebar.line_prompts.get(lineno) 9987db96d56Sopenharmony_ci for lineno in lineno_range 9997db96d56Sopenharmony_ci ] 10007db96d56Sopenharmony_ci return "\n".join( 10017db96d56Sopenharmony_ci line if prompt is None else f"{prompt} {line}" 10027db96d56Sopenharmony_ci for prompt, line in zip(prompts, text.splitlines()) 10037db96d56Sopenharmony_ci ) + "\n" 10047db96d56Sopenharmony_ci 10057db96d56Sopenharmony_ci 10067db96d56Sopenharmony_ci def copy_with_prompts_callback(self, event=None): 10077db96d56Sopenharmony_ci """Copy selected lines to the clipboard, with prompts. 10087db96d56Sopenharmony_ci 10097db96d56Sopenharmony_ci This makes the copied text useful for doc-tests and interactive 10107db96d56Sopenharmony_ci shell code examples. 10117db96d56Sopenharmony_ci 10127db96d56Sopenharmony_ci This always copies entire lines, even if only part of the first 10137db96d56Sopenharmony_ci and/or last lines is selected. 10147db96d56Sopenharmony_ci """ 10157db96d56Sopenharmony_ci text = self.text 10167db96d56Sopenharmony_ci selfirst = text.index('sel.first linestart') 10177db96d56Sopenharmony_ci if selfirst is None: # Should not be possible. 10187db96d56Sopenharmony_ci return # No selection, do nothing. 10197db96d56Sopenharmony_ci sellast = text.index('sel.last') 10207db96d56Sopenharmony_ci if sellast[-1] != '0': 10217db96d56Sopenharmony_ci sellast = text.index("sel.last+1line linestart") 10227db96d56Sopenharmony_ci text.clipboard_clear() 10237db96d56Sopenharmony_ci prompt_text = self.get_prompt_text(selfirst, sellast) 10247db96d56Sopenharmony_ci text.clipboard_append(prompt_text) 10257db96d56Sopenharmony_ci 10267db96d56Sopenharmony_ci reading = False 10277db96d56Sopenharmony_ci executing = False 10287db96d56Sopenharmony_ci canceled = False 10297db96d56Sopenharmony_ci endoffile = False 10307db96d56Sopenharmony_ci closing = False 10317db96d56Sopenharmony_ci _stop_readline_flag = False 10327db96d56Sopenharmony_ci 10337db96d56Sopenharmony_ci def set_warning_stream(self, stream): 10347db96d56Sopenharmony_ci global warning_stream 10357db96d56Sopenharmony_ci warning_stream = stream 10367db96d56Sopenharmony_ci 10377db96d56Sopenharmony_ci def get_warning_stream(self): 10387db96d56Sopenharmony_ci return warning_stream 10397db96d56Sopenharmony_ci 10407db96d56Sopenharmony_ci def toggle_debugger(self, event=None): 10417db96d56Sopenharmony_ci if self.executing: 10427db96d56Sopenharmony_ci messagebox.showerror("Don't debug now", 10437db96d56Sopenharmony_ci "You can only toggle the debugger when idle", 10447db96d56Sopenharmony_ci parent=self.text) 10457db96d56Sopenharmony_ci self.set_debugger_indicator() 10467db96d56Sopenharmony_ci return "break" 10477db96d56Sopenharmony_ci else: 10487db96d56Sopenharmony_ci db = self.interp.getdebugger() 10497db96d56Sopenharmony_ci if db: 10507db96d56Sopenharmony_ci self.close_debugger() 10517db96d56Sopenharmony_ci else: 10527db96d56Sopenharmony_ci self.open_debugger() 10537db96d56Sopenharmony_ci 10547db96d56Sopenharmony_ci def set_debugger_indicator(self): 10557db96d56Sopenharmony_ci db = self.interp.getdebugger() 10567db96d56Sopenharmony_ci self.setvar("<<toggle-debugger>>", not not db) 10577db96d56Sopenharmony_ci 10587db96d56Sopenharmony_ci def toggle_jit_stack_viewer(self, event=None): 10597db96d56Sopenharmony_ci pass # All we need is the variable 10607db96d56Sopenharmony_ci 10617db96d56Sopenharmony_ci def close_debugger(self): 10627db96d56Sopenharmony_ci db = self.interp.getdebugger() 10637db96d56Sopenharmony_ci if db: 10647db96d56Sopenharmony_ci self.interp.setdebugger(None) 10657db96d56Sopenharmony_ci db.close() 10667db96d56Sopenharmony_ci if self.interp.rpcclt: 10677db96d56Sopenharmony_ci debugger_r.close_remote_debugger(self.interp.rpcclt) 10687db96d56Sopenharmony_ci self.resetoutput() 10697db96d56Sopenharmony_ci self.console.write("[DEBUG OFF]\n") 10707db96d56Sopenharmony_ci self.prompt = self.sys_ps1 10717db96d56Sopenharmony_ci self.showprompt() 10727db96d56Sopenharmony_ci self.set_debugger_indicator() 10737db96d56Sopenharmony_ci 10747db96d56Sopenharmony_ci def open_debugger(self): 10757db96d56Sopenharmony_ci if self.interp.rpcclt: 10767db96d56Sopenharmony_ci dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, 10777db96d56Sopenharmony_ci self) 10787db96d56Sopenharmony_ci else: 10797db96d56Sopenharmony_ci dbg_gui = debugger.Debugger(self) 10807db96d56Sopenharmony_ci self.interp.setdebugger(dbg_gui) 10817db96d56Sopenharmony_ci dbg_gui.load_breakpoints() 10827db96d56Sopenharmony_ci self.prompt = "[DEBUG ON]\n" + self.sys_ps1 10837db96d56Sopenharmony_ci self.showprompt() 10847db96d56Sopenharmony_ci self.set_debugger_indicator() 10857db96d56Sopenharmony_ci 10867db96d56Sopenharmony_ci def debug_menu_postcommand(self): 10877db96d56Sopenharmony_ci state = 'disabled' if self.executing else 'normal' 10887db96d56Sopenharmony_ci self.update_menu_state('debug', '*tack*iewer', state) 10897db96d56Sopenharmony_ci 10907db96d56Sopenharmony_ci def beginexecuting(self): 10917db96d56Sopenharmony_ci "Helper for ModifiedInterpreter" 10927db96d56Sopenharmony_ci self.resetoutput() 10937db96d56Sopenharmony_ci self.executing = True 10947db96d56Sopenharmony_ci 10957db96d56Sopenharmony_ci def endexecuting(self): 10967db96d56Sopenharmony_ci "Helper for ModifiedInterpreter" 10977db96d56Sopenharmony_ci self.executing = False 10987db96d56Sopenharmony_ci self.canceled = False 10997db96d56Sopenharmony_ci self.showprompt() 11007db96d56Sopenharmony_ci 11017db96d56Sopenharmony_ci def close(self): 11027db96d56Sopenharmony_ci "Extend EditorWindow.close()" 11037db96d56Sopenharmony_ci if self.executing: 11047db96d56Sopenharmony_ci response = messagebox.askokcancel( 11057db96d56Sopenharmony_ci "Kill?", 11067db96d56Sopenharmony_ci "Your program is still running!\n Do you want to kill it?", 11077db96d56Sopenharmony_ci default="ok", 11087db96d56Sopenharmony_ci parent=self.text) 11097db96d56Sopenharmony_ci if response is False: 11107db96d56Sopenharmony_ci return "cancel" 11117db96d56Sopenharmony_ci self.stop_readline() 11127db96d56Sopenharmony_ci self.canceled = True 11137db96d56Sopenharmony_ci self.closing = True 11147db96d56Sopenharmony_ci return EditorWindow.close(self) 11157db96d56Sopenharmony_ci 11167db96d56Sopenharmony_ci def _close(self): 11177db96d56Sopenharmony_ci "Extend EditorWindow._close(), shut down debugger and execution server" 11187db96d56Sopenharmony_ci self.close_debugger() 11197db96d56Sopenharmony_ci if use_subprocess: 11207db96d56Sopenharmony_ci self.interp.kill_subprocess() 11217db96d56Sopenharmony_ci # Restore std streams 11227db96d56Sopenharmony_ci sys.stdout = self.save_stdout 11237db96d56Sopenharmony_ci sys.stderr = self.save_stderr 11247db96d56Sopenharmony_ci sys.stdin = self.save_stdin 11257db96d56Sopenharmony_ci # Break cycles 11267db96d56Sopenharmony_ci self.interp = None 11277db96d56Sopenharmony_ci self.console = None 11287db96d56Sopenharmony_ci self.flist.pyshell = None 11297db96d56Sopenharmony_ci self.history = None 11307db96d56Sopenharmony_ci EditorWindow._close(self) 11317db96d56Sopenharmony_ci 11327db96d56Sopenharmony_ci def ispythonsource(self, filename): 11337db96d56Sopenharmony_ci "Override EditorWindow method: never remove the colorizer" 11347db96d56Sopenharmony_ci return True 11357db96d56Sopenharmony_ci 11367db96d56Sopenharmony_ci def short_title(self): 11377db96d56Sopenharmony_ci return self.shell_title 11387db96d56Sopenharmony_ci 11397db96d56Sopenharmony_ci COPYRIGHT = \ 11407db96d56Sopenharmony_ci 'Type "help", "copyright", "credits" or "license()" for more information.' 11417db96d56Sopenharmony_ci 11427db96d56Sopenharmony_ci def begin(self): 11437db96d56Sopenharmony_ci self.text.mark_set("iomark", "insert") 11447db96d56Sopenharmony_ci self.resetoutput() 11457db96d56Sopenharmony_ci if use_subprocess: 11467db96d56Sopenharmony_ci nosub = '' 11477db96d56Sopenharmony_ci client = self.interp.start_subprocess() 11487db96d56Sopenharmony_ci if not client: 11497db96d56Sopenharmony_ci self.close() 11507db96d56Sopenharmony_ci return False 11517db96d56Sopenharmony_ci else: 11527db96d56Sopenharmony_ci nosub = ("==== No Subprocess ====\n\n" + 11537db96d56Sopenharmony_ci "WARNING: Running IDLE without a Subprocess is deprecated\n" + 11547db96d56Sopenharmony_ci "and will be removed in a later version. See Help/IDLE Help\n" + 11557db96d56Sopenharmony_ci "for details.\n\n") 11567db96d56Sopenharmony_ci sys.displayhook = rpc.displayhook 11577db96d56Sopenharmony_ci 11587db96d56Sopenharmony_ci self.write("Python %s on %s\n%s\n%s" % 11597db96d56Sopenharmony_ci (sys.version, sys.platform, self.COPYRIGHT, nosub)) 11607db96d56Sopenharmony_ci self.text.focus_force() 11617db96d56Sopenharmony_ci self.showprompt() 11627db96d56Sopenharmony_ci # User code should use separate default Tk root window 11637db96d56Sopenharmony_ci import tkinter 11647db96d56Sopenharmony_ci tkinter._support_default_root = True 11657db96d56Sopenharmony_ci tkinter._default_root = None 11667db96d56Sopenharmony_ci return True 11677db96d56Sopenharmony_ci 11687db96d56Sopenharmony_ci def stop_readline(self): 11697db96d56Sopenharmony_ci if not self.reading: # no nested mainloop to exit. 11707db96d56Sopenharmony_ci return 11717db96d56Sopenharmony_ci self._stop_readline_flag = True 11727db96d56Sopenharmony_ci self.top.quit() 11737db96d56Sopenharmony_ci 11747db96d56Sopenharmony_ci def readline(self): 11757db96d56Sopenharmony_ci save = self.reading 11767db96d56Sopenharmony_ci try: 11777db96d56Sopenharmony_ci self.reading = True 11787db96d56Sopenharmony_ci self.top.mainloop() # nested mainloop() 11797db96d56Sopenharmony_ci finally: 11807db96d56Sopenharmony_ci self.reading = save 11817db96d56Sopenharmony_ci if self._stop_readline_flag: 11827db96d56Sopenharmony_ci self._stop_readline_flag = False 11837db96d56Sopenharmony_ci return "" 11847db96d56Sopenharmony_ci line = self.text.get("iomark", "end-1c") 11857db96d56Sopenharmony_ci if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C 11867db96d56Sopenharmony_ci line = "\n" 11877db96d56Sopenharmony_ci self.resetoutput() 11887db96d56Sopenharmony_ci if self.canceled: 11897db96d56Sopenharmony_ci self.canceled = False 11907db96d56Sopenharmony_ci if not use_subprocess: 11917db96d56Sopenharmony_ci raise KeyboardInterrupt 11927db96d56Sopenharmony_ci if self.endoffile: 11937db96d56Sopenharmony_ci self.endoffile = False 11947db96d56Sopenharmony_ci line = "" 11957db96d56Sopenharmony_ci return line 11967db96d56Sopenharmony_ci 11977db96d56Sopenharmony_ci def isatty(self): 11987db96d56Sopenharmony_ci return True 11997db96d56Sopenharmony_ci 12007db96d56Sopenharmony_ci def cancel_callback(self, event=None): 12017db96d56Sopenharmony_ci try: 12027db96d56Sopenharmony_ci if self.text.compare("sel.first", "!=", "sel.last"): 12037db96d56Sopenharmony_ci return # Active selection -- always use default binding 12047db96d56Sopenharmony_ci except: 12057db96d56Sopenharmony_ci pass 12067db96d56Sopenharmony_ci if not (self.executing or self.reading): 12077db96d56Sopenharmony_ci self.resetoutput() 12087db96d56Sopenharmony_ci self.interp.write("KeyboardInterrupt\n") 12097db96d56Sopenharmony_ci self.showprompt() 12107db96d56Sopenharmony_ci return "break" 12117db96d56Sopenharmony_ci self.endoffile = False 12127db96d56Sopenharmony_ci self.canceled = True 12137db96d56Sopenharmony_ci if (self.executing and self.interp.rpcclt): 12147db96d56Sopenharmony_ci if self.interp.getdebugger(): 12157db96d56Sopenharmony_ci self.interp.restart_subprocess() 12167db96d56Sopenharmony_ci else: 12177db96d56Sopenharmony_ci self.interp.interrupt_subprocess() 12187db96d56Sopenharmony_ci if self.reading: 12197db96d56Sopenharmony_ci self.top.quit() # exit the nested mainloop() in readline() 12207db96d56Sopenharmony_ci return "break" 12217db96d56Sopenharmony_ci 12227db96d56Sopenharmony_ci def eof_callback(self, event): 12237db96d56Sopenharmony_ci if self.executing and not self.reading: 12247db96d56Sopenharmony_ci return # Let the default binding (delete next char) take over 12257db96d56Sopenharmony_ci if not (self.text.compare("iomark", "==", "insert") and 12267db96d56Sopenharmony_ci self.text.compare("insert", "==", "end-1c")): 12277db96d56Sopenharmony_ci return # Let the default binding (delete next char) take over 12287db96d56Sopenharmony_ci if not self.executing: 12297db96d56Sopenharmony_ci self.resetoutput() 12307db96d56Sopenharmony_ci self.close() 12317db96d56Sopenharmony_ci else: 12327db96d56Sopenharmony_ci self.canceled = False 12337db96d56Sopenharmony_ci self.endoffile = True 12347db96d56Sopenharmony_ci self.top.quit() 12357db96d56Sopenharmony_ci return "break" 12367db96d56Sopenharmony_ci 12377db96d56Sopenharmony_ci def linefeed_callback(self, event): 12387db96d56Sopenharmony_ci # Insert a linefeed without entering anything (still autoindented) 12397db96d56Sopenharmony_ci if self.reading: 12407db96d56Sopenharmony_ci self.text.insert("insert", "\n") 12417db96d56Sopenharmony_ci self.text.see("insert") 12427db96d56Sopenharmony_ci else: 12437db96d56Sopenharmony_ci self.newline_and_indent_event(event) 12447db96d56Sopenharmony_ci return "break" 12457db96d56Sopenharmony_ci 12467db96d56Sopenharmony_ci def enter_callback(self, event): 12477db96d56Sopenharmony_ci if self.executing and not self.reading: 12487db96d56Sopenharmony_ci return # Let the default binding (insert '\n') take over 12497db96d56Sopenharmony_ci # If some text is selected, recall the selection 12507db96d56Sopenharmony_ci # (but only if this before the I/O mark) 12517db96d56Sopenharmony_ci try: 12527db96d56Sopenharmony_ci sel = self.text.get("sel.first", "sel.last") 12537db96d56Sopenharmony_ci if sel: 12547db96d56Sopenharmony_ci if self.text.compare("sel.last", "<=", "iomark"): 12557db96d56Sopenharmony_ci self.recall(sel, event) 12567db96d56Sopenharmony_ci return "break" 12577db96d56Sopenharmony_ci except: 12587db96d56Sopenharmony_ci pass 12597db96d56Sopenharmony_ci # If we're strictly before the line containing iomark, recall 12607db96d56Sopenharmony_ci # the current line, less a leading prompt, less leading or 12617db96d56Sopenharmony_ci # trailing whitespace 12627db96d56Sopenharmony_ci if self.text.compare("insert", "<", "iomark linestart"): 12637db96d56Sopenharmony_ci # Check if there's a relevant stdin range -- if so, use it. 12647db96d56Sopenharmony_ci # Note: "stdin" blocks may include several successive statements, 12657db96d56Sopenharmony_ci # so look for "console" tags on the newline before each statement 12667db96d56Sopenharmony_ci # (and possibly on prompts). 12677db96d56Sopenharmony_ci prev = self.text.tag_prevrange("stdin", "insert") 12687db96d56Sopenharmony_ci if ( 12697db96d56Sopenharmony_ci prev and 12707db96d56Sopenharmony_ci self.text.compare("insert", "<", prev[1]) and 12717db96d56Sopenharmony_ci # The following is needed to handle empty statements. 12727db96d56Sopenharmony_ci "console" not in self.text.tag_names("insert") 12737db96d56Sopenharmony_ci ): 12747db96d56Sopenharmony_ci prev_cons = self.text.tag_prevrange("console", "insert") 12757db96d56Sopenharmony_ci if prev_cons and self.text.compare(prev_cons[1], ">=", prev[0]): 12767db96d56Sopenharmony_ci prev = (prev_cons[1], prev[1]) 12777db96d56Sopenharmony_ci next_cons = self.text.tag_nextrange("console", "insert") 12787db96d56Sopenharmony_ci if next_cons and self.text.compare(next_cons[0], "<", prev[1]): 12797db96d56Sopenharmony_ci prev = (prev[0], self.text.index(next_cons[0] + "+1c")) 12807db96d56Sopenharmony_ci self.recall(self.text.get(prev[0], prev[1]), event) 12817db96d56Sopenharmony_ci return "break" 12827db96d56Sopenharmony_ci next = self.text.tag_nextrange("stdin", "insert") 12837db96d56Sopenharmony_ci if next and self.text.compare("insert lineend", ">=", next[0]): 12847db96d56Sopenharmony_ci next_cons = self.text.tag_nextrange("console", "insert lineend") 12857db96d56Sopenharmony_ci if next_cons and self.text.compare(next_cons[0], "<", next[1]): 12867db96d56Sopenharmony_ci next = (next[0], self.text.index(next_cons[0] + "+1c")) 12877db96d56Sopenharmony_ci self.recall(self.text.get(next[0], next[1]), event) 12887db96d56Sopenharmony_ci return "break" 12897db96d56Sopenharmony_ci # No stdin mark -- just get the current line, less any prompt 12907db96d56Sopenharmony_ci indices = self.text.tag_nextrange("console", "insert linestart") 12917db96d56Sopenharmony_ci if indices and \ 12927db96d56Sopenharmony_ci self.text.compare(indices[0], "<=", "insert linestart"): 12937db96d56Sopenharmony_ci self.recall(self.text.get(indices[1], "insert lineend"), event) 12947db96d56Sopenharmony_ci else: 12957db96d56Sopenharmony_ci self.recall(self.text.get("insert linestart", "insert lineend"), event) 12967db96d56Sopenharmony_ci return "break" 12977db96d56Sopenharmony_ci # If we're between the beginning of the line and the iomark, i.e. 12987db96d56Sopenharmony_ci # in the prompt area, move to the end of the prompt 12997db96d56Sopenharmony_ci if self.text.compare("insert", "<", "iomark"): 13007db96d56Sopenharmony_ci self.text.mark_set("insert", "iomark") 13017db96d56Sopenharmony_ci # If we're in the current input and there's only whitespace 13027db96d56Sopenharmony_ci # beyond the cursor, erase that whitespace first 13037db96d56Sopenharmony_ci s = self.text.get("insert", "end-1c") 13047db96d56Sopenharmony_ci if s and not s.strip(): 13057db96d56Sopenharmony_ci self.text.delete("insert", "end-1c") 13067db96d56Sopenharmony_ci # If we're in the current input before its last line, 13077db96d56Sopenharmony_ci # insert a newline right at the insert point 13087db96d56Sopenharmony_ci if self.text.compare("insert", "<", "end-1c linestart"): 13097db96d56Sopenharmony_ci self.newline_and_indent_event(event) 13107db96d56Sopenharmony_ci return "break" 13117db96d56Sopenharmony_ci # We're in the last line; append a newline and submit it 13127db96d56Sopenharmony_ci self.text.mark_set("insert", "end-1c") 13137db96d56Sopenharmony_ci if self.reading: 13147db96d56Sopenharmony_ci self.text.insert("insert", "\n") 13157db96d56Sopenharmony_ci self.text.see("insert") 13167db96d56Sopenharmony_ci else: 13177db96d56Sopenharmony_ci self.newline_and_indent_event(event) 13187db96d56Sopenharmony_ci self.text.update_idletasks() 13197db96d56Sopenharmony_ci if self.reading: 13207db96d56Sopenharmony_ci self.top.quit() # Break out of recursive mainloop() 13217db96d56Sopenharmony_ci else: 13227db96d56Sopenharmony_ci self.runit() 13237db96d56Sopenharmony_ci return "break" 13247db96d56Sopenharmony_ci 13257db96d56Sopenharmony_ci def recall(self, s, event): 13267db96d56Sopenharmony_ci # remove leading and trailing empty or whitespace lines 13277db96d56Sopenharmony_ci s = re.sub(r'^\s*\n', '', s) 13287db96d56Sopenharmony_ci s = re.sub(r'\n\s*$', '', s) 13297db96d56Sopenharmony_ci lines = s.split('\n') 13307db96d56Sopenharmony_ci self.text.undo_block_start() 13317db96d56Sopenharmony_ci try: 13327db96d56Sopenharmony_ci self.text.tag_remove("sel", "1.0", "end") 13337db96d56Sopenharmony_ci self.text.mark_set("insert", "end-1c") 13347db96d56Sopenharmony_ci prefix = self.text.get("insert linestart", "insert") 13357db96d56Sopenharmony_ci if prefix.rstrip().endswith(':'): 13367db96d56Sopenharmony_ci self.newline_and_indent_event(event) 13377db96d56Sopenharmony_ci prefix = self.text.get("insert linestart", "insert") 13387db96d56Sopenharmony_ci self.text.insert("insert", lines[0].strip(), 13397db96d56Sopenharmony_ci self.user_input_insert_tags) 13407db96d56Sopenharmony_ci if len(lines) > 1: 13417db96d56Sopenharmony_ci orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) 13427db96d56Sopenharmony_ci new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) 13437db96d56Sopenharmony_ci for line in lines[1:]: 13447db96d56Sopenharmony_ci if line.startswith(orig_base_indent): 13457db96d56Sopenharmony_ci # replace orig base indentation with new indentation 13467db96d56Sopenharmony_ci line = new_base_indent + line[len(orig_base_indent):] 13477db96d56Sopenharmony_ci self.text.insert('insert', '\n' + line.rstrip(), 13487db96d56Sopenharmony_ci self.user_input_insert_tags) 13497db96d56Sopenharmony_ci finally: 13507db96d56Sopenharmony_ci self.text.see("insert") 13517db96d56Sopenharmony_ci self.text.undo_block_stop() 13527db96d56Sopenharmony_ci 13537db96d56Sopenharmony_ci _last_newline_re = re.compile(r"[ \t]*(\n[ \t]*)?\Z") 13547db96d56Sopenharmony_ci def runit(self): 13557db96d56Sopenharmony_ci index_before = self.text.index("end-2c") 13567db96d56Sopenharmony_ci line = self.text.get("iomark", "end-1c") 13577db96d56Sopenharmony_ci # Strip off last newline and surrounding whitespace. 13587db96d56Sopenharmony_ci # (To allow you to hit return twice to end a statement.) 13597db96d56Sopenharmony_ci line = self._last_newline_re.sub("", line) 13607db96d56Sopenharmony_ci input_is_complete = self.interp.runsource(line) 13617db96d56Sopenharmony_ci if not input_is_complete: 13627db96d56Sopenharmony_ci if self.text.get(index_before) == '\n': 13637db96d56Sopenharmony_ci self.text.tag_remove(self.user_input_insert_tags, index_before) 13647db96d56Sopenharmony_ci self.shell_sidebar.update_sidebar() 13657db96d56Sopenharmony_ci 13667db96d56Sopenharmony_ci def open_stack_viewer(self, event=None): 13677db96d56Sopenharmony_ci if self.interp.rpcclt: 13687db96d56Sopenharmony_ci return self.interp.remote_stack_viewer() 13697db96d56Sopenharmony_ci try: 13707db96d56Sopenharmony_ci sys.last_traceback 13717db96d56Sopenharmony_ci except: 13727db96d56Sopenharmony_ci messagebox.showerror("No stack trace", 13737db96d56Sopenharmony_ci "There is no stack trace yet.\n" 13747db96d56Sopenharmony_ci "(sys.last_traceback is not defined)", 13757db96d56Sopenharmony_ci parent=self.text) 13767db96d56Sopenharmony_ci return 13777db96d56Sopenharmony_ci from idlelib.stackviewer import StackBrowser 13787db96d56Sopenharmony_ci StackBrowser(self.root, self.flist) 13797db96d56Sopenharmony_ci 13807db96d56Sopenharmony_ci def view_restart_mark(self, event=None): 13817db96d56Sopenharmony_ci self.text.see("iomark") 13827db96d56Sopenharmony_ci self.text.see("restart") 13837db96d56Sopenharmony_ci 13847db96d56Sopenharmony_ci def restart_shell(self, event=None): 13857db96d56Sopenharmony_ci "Callback for Run/Restart Shell Cntl-F6" 13867db96d56Sopenharmony_ci self.interp.restart_subprocess(with_cwd=True) 13877db96d56Sopenharmony_ci 13887db96d56Sopenharmony_ci def showprompt(self): 13897db96d56Sopenharmony_ci self.resetoutput() 13907db96d56Sopenharmony_ci 13917db96d56Sopenharmony_ci prompt = self.prompt 13927db96d56Sopenharmony_ci if self.sys_ps1 and prompt.endswith(self.sys_ps1): 13937db96d56Sopenharmony_ci prompt = prompt[:-len(self.sys_ps1)] 13947db96d56Sopenharmony_ci self.text.tag_add("console", "iomark-1c") 13957db96d56Sopenharmony_ci self.console.write(prompt) 13967db96d56Sopenharmony_ci 13977db96d56Sopenharmony_ci self.shell_sidebar.update_sidebar() 13987db96d56Sopenharmony_ci self.text.mark_set("insert", "end-1c") 13997db96d56Sopenharmony_ci self.set_line_and_column() 14007db96d56Sopenharmony_ci self.io.reset_undo() 14017db96d56Sopenharmony_ci 14027db96d56Sopenharmony_ci def show_warning(self, msg): 14037db96d56Sopenharmony_ci width = self.interp.tkconsole.width 14047db96d56Sopenharmony_ci wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True) 14057db96d56Sopenharmony_ci wrapped_msg = '\n'.join(wrapper.wrap(msg)) 14067db96d56Sopenharmony_ci if not wrapped_msg.endswith('\n'): 14077db96d56Sopenharmony_ci wrapped_msg += '\n' 14087db96d56Sopenharmony_ci self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr") 14097db96d56Sopenharmony_ci 14107db96d56Sopenharmony_ci def resetoutput(self): 14117db96d56Sopenharmony_ci source = self.text.get("iomark", "end-1c") 14127db96d56Sopenharmony_ci if self.history: 14137db96d56Sopenharmony_ci self.history.store(source) 14147db96d56Sopenharmony_ci if self.text.get("end-2c") != "\n": 14157db96d56Sopenharmony_ci self.text.insert("end-1c", "\n") 14167db96d56Sopenharmony_ci self.text.mark_set("iomark", "end-1c") 14177db96d56Sopenharmony_ci self.set_line_and_column() 14187db96d56Sopenharmony_ci self.ctip.remove_calltip_window() 14197db96d56Sopenharmony_ci 14207db96d56Sopenharmony_ci def write(self, s, tags=()): 14217db96d56Sopenharmony_ci try: 14227db96d56Sopenharmony_ci self.text.mark_gravity("iomark", "right") 14237db96d56Sopenharmony_ci count = OutputWindow.write(self, s, tags, "iomark") 14247db96d56Sopenharmony_ci self.text.mark_gravity("iomark", "left") 14257db96d56Sopenharmony_ci except: 14267db96d56Sopenharmony_ci raise ###pass # ### 11Aug07 KBK if we are expecting exceptions 14277db96d56Sopenharmony_ci # let's find out what they are and be specific. 14287db96d56Sopenharmony_ci if self.canceled: 14297db96d56Sopenharmony_ci self.canceled = False 14307db96d56Sopenharmony_ci if not use_subprocess: 14317db96d56Sopenharmony_ci raise KeyboardInterrupt 14327db96d56Sopenharmony_ci return count 14337db96d56Sopenharmony_ci 14347db96d56Sopenharmony_ci def rmenu_check_cut(self): 14357db96d56Sopenharmony_ci try: 14367db96d56Sopenharmony_ci if self.text.compare('sel.first', '<', 'iomark'): 14377db96d56Sopenharmony_ci return 'disabled' 14387db96d56Sopenharmony_ci except TclError: # no selection, so the index 'sel.first' doesn't exist 14397db96d56Sopenharmony_ci return 'disabled' 14407db96d56Sopenharmony_ci return super().rmenu_check_cut() 14417db96d56Sopenharmony_ci 14427db96d56Sopenharmony_ci def rmenu_check_paste(self): 14437db96d56Sopenharmony_ci if self.text.compare('insert','<','iomark'): 14447db96d56Sopenharmony_ci return 'disabled' 14457db96d56Sopenharmony_ci return super().rmenu_check_paste() 14467db96d56Sopenharmony_ci 14477db96d56Sopenharmony_ci def squeeze_current_text_event(self, event=None): 14487db96d56Sopenharmony_ci self.squeezer.squeeze_current_text() 14497db96d56Sopenharmony_ci self.shell_sidebar.update_sidebar() 14507db96d56Sopenharmony_ci 14517db96d56Sopenharmony_ci def on_squeezed_expand(self, index, text, tags): 14527db96d56Sopenharmony_ci self.shell_sidebar.update_sidebar() 14537db96d56Sopenharmony_ci 14547db96d56Sopenharmony_ci 14557db96d56Sopenharmony_cidef fix_x11_paste(root): 14567db96d56Sopenharmony_ci "Make paste replace selection on x11. See issue #5124." 14577db96d56Sopenharmony_ci if root._windowingsystem == 'x11': 14587db96d56Sopenharmony_ci for cls in 'Text', 'Entry', 'Spinbox': 14597db96d56Sopenharmony_ci root.bind_class( 14607db96d56Sopenharmony_ci cls, 14617db96d56Sopenharmony_ci '<<Paste>>', 14627db96d56Sopenharmony_ci 'catch {%W delete sel.first sel.last}\n' + 14637db96d56Sopenharmony_ci root.bind_class(cls, '<<Paste>>')) 14647db96d56Sopenharmony_ci 14657db96d56Sopenharmony_ci 14667db96d56Sopenharmony_ciusage_msg = """\ 14677db96d56Sopenharmony_ci 14687db96d56Sopenharmony_ciUSAGE: idle [-deins] [-t title] [file]* 14697db96d56Sopenharmony_ci idle [-dns] [-t title] (-c cmd | -r file) [arg]* 14707db96d56Sopenharmony_ci idle [-dns] [-t title] - [arg]* 14717db96d56Sopenharmony_ci 14727db96d56Sopenharmony_ci -h print this help message and exit 14737db96d56Sopenharmony_ci -n run IDLE without a subprocess (DEPRECATED, 14747db96d56Sopenharmony_ci see Help/IDLE Help for details) 14757db96d56Sopenharmony_ci 14767db96d56Sopenharmony_ciThe following options will override the IDLE 'settings' configuration: 14777db96d56Sopenharmony_ci 14787db96d56Sopenharmony_ci -e open an edit window 14797db96d56Sopenharmony_ci -i open a shell window 14807db96d56Sopenharmony_ci 14817db96d56Sopenharmony_ciThe following options imply -i and will open a shell: 14827db96d56Sopenharmony_ci 14837db96d56Sopenharmony_ci -c cmd run the command in a shell, or 14847db96d56Sopenharmony_ci -r file run script from file 14857db96d56Sopenharmony_ci 14867db96d56Sopenharmony_ci -d enable the debugger 14877db96d56Sopenharmony_ci -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else 14887db96d56Sopenharmony_ci -t title set title of shell window 14897db96d56Sopenharmony_ci 14907db96d56Sopenharmony_ciA default edit window will be bypassed when -c, -r, or - are used. 14917db96d56Sopenharmony_ci 14927db96d56Sopenharmony_ci[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. 14937db96d56Sopenharmony_ci 14947db96d56Sopenharmony_ciExamples: 14957db96d56Sopenharmony_ci 14967db96d56Sopenharmony_ciidle 14977db96d56Sopenharmony_ci Open an edit window or shell depending on IDLE's configuration. 14987db96d56Sopenharmony_ci 14997db96d56Sopenharmony_ciidle foo.py foobar.py 15007db96d56Sopenharmony_ci Edit the files, also open a shell if configured to start with shell. 15017db96d56Sopenharmony_ci 15027db96d56Sopenharmony_ciidle -est "Baz" foo.py 15037db96d56Sopenharmony_ci Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell 15047db96d56Sopenharmony_ci window with the title "Baz". 15057db96d56Sopenharmony_ci 15067db96d56Sopenharmony_ciidle -c "import sys; print(sys.argv)" "foo" 15077db96d56Sopenharmony_ci Open a shell window and run the command, passing "-c" in sys.argv[0] 15087db96d56Sopenharmony_ci and "foo" in sys.argv[1]. 15097db96d56Sopenharmony_ci 15107db96d56Sopenharmony_ciidle -d -s -r foo.py "Hello World" 15117db96d56Sopenharmony_ci Open a shell window, run a startup script, enable the debugger, and 15127db96d56Sopenharmony_ci run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in 15137db96d56Sopenharmony_ci sys.argv[1]. 15147db96d56Sopenharmony_ci 15157db96d56Sopenharmony_ciecho "import sys; print(sys.argv)" | idle - "foobar" 15167db96d56Sopenharmony_ci Open a shell window, run the script piped in, passing '' in sys.argv[0] 15177db96d56Sopenharmony_ci and "foobar" in sys.argv[1]. 15187db96d56Sopenharmony_ci""" 15197db96d56Sopenharmony_ci 15207db96d56Sopenharmony_cidef main(): 15217db96d56Sopenharmony_ci import getopt 15227db96d56Sopenharmony_ci from platform import system 15237db96d56Sopenharmony_ci from idlelib import testing # bool value 15247db96d56Sopenharmony_ci from idlelib import macosx 15257db96d56Sopenharmony_ci 15267db96d56Sopenharmony_ci global flist, root, use_subprocess 15277db96d56Sopenharmony_ci 15287db96d56Sopenharmony_ci capture_warnings(True) 15297db96d56Sopenharmony_ci use_subprocess = True 15307db96d56Sopenharmony_ci enable_shell = False 15317db96d56Sopenharmony_ci enable_edit = False 15327db96d56Sopenharmony_ci debug = False 15337db96d56Sopenharmony_ci cmd = None 15347db96d56Sopenharmony_ci script = None 15357db96d56Sopenharmony_ci startup = False 15367db96d56Sopenharmony_ci try: 15377db96d56Sopenharmony_ci opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") 15387db96d56Sopenharmony_ci except getopt.error as msg: 15397db96d56Sopenharmony_ci print(f"Error: {msg}\n{usage_msg}", file=sys.stderr) 15407db96d56Sopenharmony_ci sys.exit(2) 15417db96d56Sopenharmony_ci for o, a in opts: 15427db96d56Sopenharmony_ci if o == '-c': 15437db96d56Sopenharmony_ci cmd = a 15447db96d56Sopenharmony_ci enable_shell = True 15457db96d56Sopenharmony_ci if o == '-d': 15467db96d56Sopenharmony_ci debug = True 15477db96d56Sopenharmony_ci enable_shell = True 15487db96d56Sopenharmony_ci if o == '-e': 15497db96d56Sopenharmony_ci enable_edit = True 15507db96d56Sopenharmony_ci if o == '-h': 15517db96d56Sopenharmony_ci sys.stdout.write(usage_msg) 15527db96d56Sopenharmony_ci sys.exit() 15537db96d56Sopenharmony_ci if o == '-i': 15547db96d56Sopenharmony_ci enable_shell = True 15557db96d56Sopenharmony_ci if o == '-n': 15567db96d56Sopenharmony_ci print(" Warning: running IDLE without a subprocess is deprecated.", 15577db96d56Sopenharmony_ci file=sys.stderr) 15587db96d56Sopenharmony_ci use_subprocess = False 15597db96d56Sopenharmony_ci if o == '-r': 15607db96d56Sopenharmony_ci script = a 15617db96d56Sopenharmony_ci if os.path.isfile(script): 15627db96d56Sopenharmony_ci pass 15637db96d56Sopenharmony_ci else: 15647db96d56Sopenharmony_ci print("No script file: ", script) 15657db96d56Sopenharmony_ci sys.exit() 15667db96d56Sopenharmony_ci enable_shell = True 15677db96d56Sopenharmony_ci if o == '-s': 15687db96d56Sopenharmony_ci startup = True 15697db96d56Sopenharmony_ci enable_shell = True 15707db96d56Sopenharmony_ci if o == '-t': 15717db96d56Sopenharmony_ci PyShell.shell_title = a 15727db96d56Sopenharmony_ci enable_shell = True 15737db96d56Sopenharmony_ci if args and args[0] == '-': 15747db96d56Sopenharmony_ci cmd = sys.stdin.read() 15757db96d56Sopenharmony_ci enable_shell = True 15767db96d56Sopenharmony_ci # process sys.argv and sys.path: 15777db96d56Sopenharmony_ci for i in range(len(sys.path)): 15787db96d56Sopenharmony_ci sys.path[i] = os.path.abspath(sys.path[i]) 15797db96d56Sopenharmony_ci if args and args[0] == '-': 15807db96d56Sopenharmony_ci sys.argv = [''] + args[1:] 15817db96d56Sopenharmony_ci elif cmd: 15827db96d56Sopenharmony_ci sys.argv = ['-c'] + args 15837db96d56Sopenharmony_ci elif script: 15847db96d56Sopenharmony_ci sys.argv = [script] + args 15857db96d56Sopenharmony_ci elif args: 15867db96d56Sopenharmony_ci enable_edit = True 15877db96d56Sopenharmony_ci pathx = [] 15887db96d56Sopenharmony_ci for filename in args: 15897db96d56Sopenharmony_ci pathx.append(os.path.dirname(filename)) 15907db96d56Sopenharmony_ci for dir in pathx: 15917db96d56Sopenharmony_ci dir = os.path.abspath(dir) 15927db96d56Sopenharmony_ci if not dir in sys.path: 15937db96d56Sopenharmony_ci sys.path.insert(0, dir) 15947db96d56Sopenharmony_ci else: 15957db96d56Sopenharmony_ci dir = os.getcwd() 15967db96d56Sopenharmony_ci if dir not in sys.path: 15977db96d56Sopenharmony_ci sys.path.insert(0, dir) 15987db96d56Sopenharmony_ci # check the IDLE settings configuration (but command line overrides) 15997db96d56Sopenharmony_ci edit_start = idleConf.GetOption('main', 'General', 16007db96d56Sopenharmony_ci 'editor-on-startup', type='bool') 16017db96d56Sopenharmony_ci enable_edit = enable_edit or edit_start 16027db96d56Sopenharmony_ci enable_shell = enable_shell or not enable_edit 16037db96d56Sopenharmony_ci 16047db96d56Sopenharmony_ci # Setup root. Don't break user code run in IDLE process. 16057db96d56Sopenharmony_ci # Don't change environment when testing. 16067db96d56Sopenharmony_ci if use_subprocess and not testing: 16077db96d56Sopenharmony_ci NoDefaultRoot() 16087db96d56Sopenharmony_ci root = Tk(className="Idle") 16097db96d56Sopenharmony_ci root.withdraw() 16107db96d56Sopenharmony_ci from idlelib.run import fix_scaling 16117db96d56Sopenharmony_ci fix_scaling(root) 16127db96d56Sopenharmony_ci 16137db96d56Sopenharmony_ci # set application icon 16147db96d56Sopenharmony_ci icondir = os.path.join(os.path.dirname(__file__), 'Icons') 16157db96d56Sopenharmony_ci if system() == 'Windows': 16167db96d56Sopenharmony_ci iconfile = os.path.join(icondir, 'idle.ico') 16177db96d56Sopenharmony_ci root.wm_iconbitmap(default=iconfile) 16187db96d56Sopenharmony_ci elif not macosx.isAquaTk(): 16197db96d56Sopenharmony_ci if TkVersion >= 8.6: 16207db96d56Sopenharmony_ci ext = '.png' 16217db96d56Sopenharmony_ci sizes = (16, 32, 48, 256) 16227db96d56Sopenharmony_ci else: 16237db96d56Sopenharmony_ci ext = '.gif' 16247db96d56Sopenharmony_ci sizes = (16, 32, 48) 16257db96d56Sopenharmony_ci iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) 16267db96d56Sopenharmony_ci for size in sizes] 16277db96d56Sopenharmony_ci icons = [PhotoImage(master=root, file=iconfile) 16287db96d56Sopenharmony_ci for iconfile in iconfiles] 16297db96d56Sopenharmony_ci root.wm_iconphoto(True, *icons) 16307db96d56Sopenharmony_ci 16317db96d56Sopenharmony_ci # start editor and/or shell windows: 16327db96d56Sopenharmony_ci fixwordbreaks(root) 16337db96d56Sopenharmony_ci fix_x11_paste(root) 16347db96d56Sopenharmony_ci flist = PyShellFileList(root) 16357db96d56Sopenharmony_ci macosx.setupApp(root, flist) 16367db96d56Sopenharmony_ci 16377db96d56Sopenharmony_ci if enable_edit: 16387db96d56Sopenharmony_ci if not (cmd or script): 16397db96d56Sopenharmony_ci for filename in args[:]: 16407db96d56Sopenharmony_ci if flist.open(filename) is None: 16417db96d56Sopenharmony_ci # filename is a directory actually, disconsider it 16427db96d56Sopenharmony_ci args.remove(filename) 16437db96d56Sopenharmony_ci if not args: 16447db96d56Sopenharmony_ci flist.new() 16457db96d56Sopenharmony_ci 16467db96d56Sopenharmony_ci if enable_shell: 16477db96d56Sopenharmony_ci shell = flist.open_shell() 16487db96d56Sopenharmony_ci if not shell: 16497db96d56Sopenharmony_ci return # couldn't open shell 16507db96d56Sopenharmony_ci if macosx.isAquaTk() and flist.dict: 16517db96d56Sopenharmony_ci # On OSX: when the user has double-clicked on a file that causes 16527db96d56Sopenharmony_ci # IDLE to be launched the shell window will open just in front of 16537db96d56Sopenharmony_ci # the file she wants to see. Lower the interpreter window when 16547db96d56Sopenharmony_ci # there are open files. 16557db96d56Sopenharmony_ci shell.top.lower() 16567db96d56Sopenharmony_ci else: 16577db96d56Sopenharmony_ci shell = flist.pyshell 16587db96d56Sopenharmony_ci 16597db96d56Sopenharmony_ci # Handle remaining options. If any of these are set, enable_shell 16607db96d56Sopenharmony_ci # was set also, so shell must be true to reach here. 16617db96d56Sopenharmony_ci if debug: 16627db96d56Sopenharmony_ci shell.open_debugger() 16637db96d56Sopenharmony_ci if startup: 16647db96d56Sopenharmony_ci filename = os.environ.get("IDLESTARTUP") or \ 16657db96d56Sopenharmony_ci os.environ.get("PYTHONSTARTUP") 16667db96d56Sopenharmony_ci if filename and os.path.isfile(filename): 16677db96d56Sopenharmony_ci shell.interp.execfile(filename) 16687db96d56Sopenharmony_ci if cmd or script: 16697db96d56Sopenharmony_ci shell.interp.runcommand("""if 1: 16707db96d56Sopenharmony_ci import sys as _sys 16717db96d56Sopenharmony_ci _sys.argv = {!r} 16727db96d56Sopenharmony_ci del _sys 16737db96d56Sopenharmony_ci \n""".format(sys.argv)) 16747db96d56Sopenharmony_ci if cmd: 16757db96d56Sopenharmony_ci shell.interp.execsource(cmd) 16767db96d56Sopenharmony_ci elif script: 16777db96d56Sopenharmony_ci shell.interp.prepend_syspath(script) 16787db96d56Sopenharmony_ci shell.interp.execfile(script) 16797db96d56Sopenharmony_ci elif shell: 16807db96d56Sopenharmony_ci # If there is a shell window and no cmd or script in progress, 16817db96d56Sopenharmony_ci # check for problematic issues and print warning message(s) in 16827db96d56Sopenharmony_ci # the IDLE shell window; this is less intrusive than always 16837db96d56Sopenharmony_ci # opening a separate window. 16847db96d56Sopenharmony_ci 16857db96d56Sopenharmony_ci # Warn if the "Prefer tabs when opening documents" system 16867db96d56Sopenharmony_ci # preference is set to "Always". 16877db96d56Sopenharmony_ci prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning() 16887db96d56Sopenharmony_ci if prefer_tabs_preference_warning: 16897db96d56Sopenharmony_ci shell.show_warning(prefer_tabs_preference_warning) 16907db96d56Sopenharmony_ci 16917db96d56Sopenharmony_ci while flist.inversedict: # keep IDLE running while files are open. 16927db96d56Sopenharmony_ci root.mainloop() 16937db96d56Sopenharmony_ci root.destroy() 16947db96d56Sopenharmony_ci capture_warnings(False) 16957db96d56Sopenharmony_ci 16967db96d56Sopenharmony_ciif __name__ == "__main__": 16977db96d56Sopenharmony_ci main() 16987db96d56Sopenharmony_ci 16997db96d56Sopenharmony_cicapture_warnings(False) # Make sure turned off; see issue 18081 1700