xref: /third_party/python/Lib/idlelib/pyshell.py (revision 7db96d56)
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