17db96d56Sopenharmony_ci""" idlelib.run 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciSimplified, pyshell.ModifiedInterpreter spawns a subprocess with 47db96d56Sopenharmony_cif'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' 57db96d56Sopenharmony_ci'.run' is needed because __import__ returns idlelib, not idlelib.run. 67db96d56Sopenharmony_ci""" 77db96d56Sopenharmony_ciimport contextlib 87db96d56Sopenharmony_ciimport functools 97db96d56Sopenharmony_ciimport io 107db96d56Sopenharmony_ciimport linecache 117db96d56Sopenharmony_ciimport queue 127db96d56Sopenharmony_ciimport sys 137db96d56Sopenharmony_ciimport textwrap 147db96d56Sopenharmony_ciimport time 157db96d56Sopenharmony_ciimport traceback 167db96d56Sopenharmony_ciimport _thread as thread 177db96d56Sopenharmony_ciimport threading 187db96d56Sopenharmony_ciimport warnings 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_ciimport idlelib # testing 217db96d56Sopenharmony_cifrom idlelib import autocomplete # AutoComplete, fetch_encodings 227db96d56Sopenharmony_cifrom idlelib import calltip # Calltip 237db96d56Sopenharmony_cifrom idlelib import debugger_r # start_debugger 247db96d56Sopenharmony_cifrom idlelib import debugobj_r # remote_object_tree_item 257db96d56Sopenharmony_cifrom idlelib import iomenu # encoding 267db96d56Sopenharmony_cifrom idlelib import rpc # multiple objects 277db96d56Sopenharmony_cifrom idlelib import stackviewer # StackTreeItem 287db96d56Sopenharmony_ciimport __main__ 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ciimport tkinter # Use tcl and, if startup fails, messagebox. 317db96d56Sopenharmony_ciif not hasattr(sys.modules['idlelib.run'], 'firstrun'): 327db96d56Sopenharmony_ci # Undo modifications of tkinter by idlelib imports; see bpo-25507. 337db96d56Sopenharmony_ci for mod in ('simpledialog', 'messagebox', 'font', 347db96d56Sopenharmony_ci 'dialog', 'filedialog', 'commondialog', 357db96d56Sopenharmony_ci 'ttk'): 367db96d56Sopenharmony_ci delattr(tkinter, mod) 377db96d56Sopenharmony_ci del sys.modules['tkinter.' + mod] 387db96d56Sopenharmony_ci # Avoid AttributeError if run again; see bpo-37038. 397db96d56Sopenharmony_ci sys.modules['idlelib.run'].firstrun = False 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ciLOCALHOST = '127.0.0.1' 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_citry: 447db96d56Sopenharmony_ci eof = 'Ctrl-D (end-of-file)' 457db96d56Sopenharmony_ci exit.eof = eof 467db96d56Sopenharmony_ci quit.eof = eof 477db96d56Sopenharmony_ciexcept NameError: # In case subprocess started with -S (maybe in future). 487db96d56Sopenharmony_ci pass 497db96d56Sopenharmony_ci 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_cidef idle_formatwarning(message, category, filename, lineno, line=None): 527db96d56Sopenharmony_ci """Format warnings the IDLE way.""" 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci s = "\nWarning (from warnings module):\n" 557db96d56Sopenharmony_ci s += f' File \"{filename}\", line {lineno}\n' 567db96d56Sopenharmony_ci if line is None: 577db96d56Sopenharmony_ci line = linecache.getline(filename, lineno) 587db96d56Sopenharmony_ci line = line.strip() 597db96d56Sopenharmony_ci if line: 607db96d56Sopenharmony_ci s += " %s\n" % line 617db96d56Sopenharmony_ci s += f"{category.__name__}: {message}\n" 627db96d56Sopenharmony_ci return s 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_cidef idle_showwarning_subproc( 657db96d56Sopenharmony_ci message, category, filename, lineno, file=None, line=None): 667db96d56Sopenharmony_ci """Show Idle-format warning after replacing warnings.showwarning. 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci The only difference is the formatter called. 697db96d56Sopenharmony_ci """ 707db96d56Sopenharmony_ci if file is None: 717db96d56Sopenharmony_ci file = sys.stderr 727db96d56Sopenharmony_ci try: 737db96d56Sopenharmony_ci file.write(idle_formatwarning( 747db96d56Sopenharmony_ci message, category, filename, lineno, line)) 757db96d56Sopenharmony_ci except OSError: 767db96d56Sopenharmony_ci pass # the file (probably stderr) is invalid - this warning gets lost. 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci_warnings_showwarning = None 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_cidef capture_warnings(capture): 817db96d56Sopenharmony_ci "Replace warning.showwarning with idle_showwarning_subproc, or reverse." 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci global _warnings_showwarning 847db96d56Sopenharmony_ci if capture: 857db96d56Sopenharmony_ci if _warnings_showwarning is None: 867db96d56Sopenharmony_ci _warnings_showwarning = warnings.showwarning 877db96d56Sopenharmony_ci warnings.showwarning = idle_showwarning_subproc 887db96d56Sopenharmony_ci else: 897db96d56Sopenharmony_ci if _warnings_showwarning is not None: 907db96d56Sopenharmony_ci warnings.showwarning = _warnings_showwarning 917db96d56Sopenharmony_ci _warnings_showwarning = None 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_cicapture_warnings(True) 947db96d56Sopenharmony_citcl = tkinter.Tcl() 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_cidef handle_tk_events(tcl=tcl): 977db96d56Sopenharmony_ci """Process any tk events that are ready to be dispatched if tkinter 987db96d56Sopenharmony_ci has been imported, a tcl interpreter has been created and tk has been 997db96d56Sopenharmony_ci loaded.""" 1007db96d56Sopenharmony_ci tcl.eval("update") 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_ci# Thread shared globals: Establish a queue between a subthread (which handles 1037db96d56Sopenharmony_ci# the socket) and the main thread (which runs user code), plus global 1047db96d56Sopenharmony_ci# completion, exit and interruptable (the main thread) flags: 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ciexit_now = False 1077db96d56Sopenharmony_ciquitting = False 1087db96d56Sopenharmony_ciinterruptable = False 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_cidef main(del_exitfunc=False): 1117db96d56Sopenharmony_ci """Start the Python execution server in a subprocess 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_ci In the Python subprocess, RPCServer is instantiated with handlerclass 1147db96d56Sopenharmony_ci MyHandler, which inherits register/unregister methods from RPCHandler via 1157db96d56Sopenharmony_ci the mix-in class SocketIO. 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci When the RPCServer 'server' is instantiated, the TCPServer initialization 1187db96d56Sopenharmony_ci creates an instance of run.MyHandler and calls its handle() method. 1197db96d56Sopenharmony_ci handle() instantiates a run.Executive object, passing it a reference to the 1207db96d56Sopenharmony_ci MyHandler object. That reference is saved as attribute rpchandler of the 1217db96d56Sopenharmony_ci Executive instance. The Executive methods have access to the reference and 1227db96d56Sopenharmony_ci can pass it on to entities that they command 1237db96d56Sopenharmony_ci (e.g. debugger_r.Debugger.start_debugger()). The latter, in turn, can 1247db96d56Sopenharmony_ci call MyHandler(SocketIO) register/unregister methods via the reference to 1257db96d56Sopenharmony_ci register and unregister themselves. 1267db96d56Sopenharmony_ci 1277db96d56Sopenharmony_ci """ 1287db96d56Sopenharmony_ci global exit_now 1297db96d56Sopenharmony_ci global quitting 1307db96d56Sopenharmony_ci global no_exitfunc 1317db96d56Sopenharmony_ci no_exitfunc = del_exitfunc 1327db96d56Sopenharmony_ci #time.sleep(15) # test subprocess not responding 1337db96d56Sopenharmony_ci try: 1347db96d56Sopenharmony_ci assert(len(sys.argv) > 1) 1357db96d56Sopenharmony_ci port = int(sys.argv[-1]) 1367db96d56Sopenharmony_ci except: 1377db96d56Sopenharmony_ci print("IDLE Subprocess: no IP port passed in sys.argv.", 1387db96d56Sopenharmony_ci file=sys.__stderr__) 1397db96d56Sopenharmony_ci return 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci capture_warnings(True) 1427db96d56Sopenharmony_ci sys.argv[:] = [""] 1437db96d56Sopenharmony_ci sockthread = threading.Thread(target=manage_socket, 1447db96d56Sopenharmony_ci name='SockThread', 1457db96d56Sopenharmony_ci args=((LOCALHOST, port),)) 1467db96d56Sopenharmony_ci sockthread.daemon = True 1477db96d56Sopenharmony_ci sockthread.start() 1487db96d56Sopenharmony_ci while True: 1497db96d56Sopenharmony_ci try: 1507db96d56Sopenharmony_ci if exit_now: 1517db96d56Sopenharmony_ci try: 1527db96d56Sopenharmony_ci exit() 1537db96d56Sopenharmony_ci except KeyboardInterrupt: 1547db96d56Sopenharmony_ci # exiting but got an extra KBI? Try again! 1557db96d56Sopenharmony_ci continue 1567db96d56Sopenharmony_ci try: 1577db96d56Sopenharmony_ci request = rpc.request_queue.get(block=True, timeout=0.05) 1587db96d56Sopenharmony_ci except queue.Empty: 1597db96d56Sopenharmony_ci request = None 1607db96d56Sopenharmony_ci # Issue 32207: calling handle_tk_events here adds spurious 1617db96d56Sopenharmony_ci # queue.Empty traceback to event handling exceptions. 1627db96d56Sopenharmony_ci if request: 1637db96d56Sopenharmony_ci seq, (method, args, kwargs) = request 1647db96d56Sopenharmony_ci ret = method(*args, **kwargs) 1657db96d56Sopenharmony_ci rpc.response_queue.put((seq, ret)) 1667db96d56Sopenharmony_ci else: 1677db96d56Sopenharmony_ci handle_tk_events() 1687db96d56Sopenharmony_ci except KeyboardInterrupt: 1697db96d56Sopenharmony_ci if quitting: 1707db96d56Sopenharmony_ci exit_now = True 1717db96d56Sopenharmony_ci continue 1727db96d56Sopenharmony_ci except SystemExit: 1737db96d56Sopenharmony_ci capture_warnings(False) 1747db96d56Sopenharmony_ci raise 1757db96d56Sopenharmony_ci except: 1767db96d56Sopenharmony_ci type, value, tb = sys.exc_info() 1777db96d56Sopenharmony_ci try: 1787db96d56Sopenharmony_ci print_exception() 1797db96d56Sopenharmony_ci rpc.response_queue.put((seq, None)) 1807db96d56Sopenharmony_ci except: 1817db96d56Sopenharmony_ci # Link didn't work, print same exception to __stderr__ 1827db96d56Sopenharmony_ci traceback.print_exception(type, value, tb, file=sys.__stderr__) 1837db96d56Sopenharmony_ci exit() 1847db96d56Sopenharmony_ci else: 1857db96d56Sopenharmony_ci continue 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_cidef manage_socket(address): 1887db96d56Sopenharmony_ci for i in range(3): 1897db96d56Sopenharmony_ci time.sleep(i) 1907db96d56Sopenharmony_ci try: 1917db96d56Sopenharmony_ci server = MyRPCServer(address, MyHandler) 1927db96d56Sopenharmony_ci break 1937db96d56Sopenharmony_ci except OSError as err: 1947db96d56Sopenharmony_ci print("IDLE Subprocess: OSError: " + err.args[1] + 1957db96d56Sopenharmony_ci ", retrying....", file=sys.__stderr__) 1967db96d56Sopenharmony_ci socket_error = err 1977db96d56Sopenharmony_ci else: 1987db96d56Sopenharmony_ci print("IDLE Subprocess: Connection to " 1997db96d56Sopenharmony_ci "IDLE GUI failed, exiting.", file=sys.__stderr__) 2007db96d56Sopenharmony_ci show_socket_error(socket_error, address) 2017db96d56Sopenharmony_ci global exit_now 2027db96d56Sopenharmony_ci exit_now = True 2037db96d56Sopenharmony_ci return 2047db96d56Sopenharmony_ci server.handle_request() # A single request only 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_cidef show_socket_error(err, address): 2077db96d56Sopenharmony_ci "Display socket error from manage_socket." 2087db96d56Sopenharmony_ci import tkinter 2097db96d56Sopenharmony_ci from tkinter.messagebox import showerror 2107db96d56Sopenharmony_ci root = tkinter.Tk() 2117db96d56Sopenharmony_ci fix_scaling(root) 2127db96d56Sopenharmony_ci root.withdraw() 2137db96d56Sopenharmony_ci showerror( 2147db96d56Sopenharmony_ci "Subprocess Connection Error", 2157db96d56Sopenharmony_ci f"IDLE's subprocess can't connect to {address[0]}:{address[1]}.\n" 2167db96d56Sopenharmony_ci f"Fatal OSError #{err.errno}: {err.strerror}.\n" 2177db96d56Sopenharmony_ci "See the 'Startup failure' section of the IDLE doc, online at\n" 2187db96d56Sopenharmony_ci "https://docs.python.org/3/library/idle.html#startup-failure", 2197db96d56Sopenharmony_ci parent=root) 2207db96d56Sopenharmony_ci root.destroy() 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_cidef get_message_lines(typ, exc, tb): 2247db96d56Sopenharmony_ci "Return line composing the exception message." 2257db96d56Sopenharmony_ci if typ in (AttributeError, NameError): 2267db96d56Sopenharmony_ci # 3.10+ hints are not directly accessible from python (#44026). 2277db96d56Sopenharmony_ci err = io.StringIO() 2287db96d56Sopenharmony_ci with contextlib.redirect_stderr(err): 2297db96d56Sopenharmony_ci sys.__excepthook__(typ, exc, tb) 2307db96d56Sopenharmony_ci return [err.getvalue().split("\n")[-2] + "\n"] 2317db96d56Sopenharmony_ci else: 2327db96d56Sopenharmony_ci return traceback.format_exception_only(typ, exc) 2337db96d56Sopenharmony_ci 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_cidef print_exception(): 2367db96d56Sopenharmony_ci import linecache 2377db96d56Sopenharmony_ci linecache.checkcache() 2387db96d56Sopenharmony_ci flush_stdout() 2397db96d56Sopenharmony_ci efile = sys.stderr 2407db96d56Sopenharmony_ci typ, val, tb = excinfo = sys.exc_info() 2417db96d56Sopenharmony_ci sys.last_type, sys.last_value, sys.last_traceback = excinfo 2427db96d56Sopenharmony_ci seen = set() 2437db96d56Sopenharmony_ci 2447db96d56Sopenharmony_ci def print_exc(typ, exc, tb): 2457db96d56Sopenharmony_ci seen.add(id(exc)) 2467db96d56Sopenharmony_ci context = exc.__context__ 2477db96d56Sopenharmony_ci cause = exc.__cause__ 2487db96d56Sopenharmony_ci if cause is not None and id(cause) not in seen: 2497db96d56Sopenharmony_ci print_exc(type(cause), cause, cause.__traceback__) 2507db96d56Sopenharmony_ci print("\nThe above exception was the direct cause " 2517db96d56Sopenharmony_ci "of the following exception:\n", file=efile) 2527db96d56Sopenharmony_ci elif (context is not None and 2537db96d56Sopenharmony_ci not exc.__suppress_context__ and 2547db96d56Sopenharmony_ci id(context) not in seen): 2557db96d56Sopenharmony_ci print_exc(type(context), context, context.__traceback__) 2567db96d56Sopenharmony_ci print("\nDuring handling of the above exception, " 2577db96d56Sopenharmony_ci "another exception occurred:\n", file=efile) 2587db96d56Sopenharmony_ci if tb: 2597db96d56Sopenharmony_ci tbe = traceback.extract_tb(tb) 2607db96d56Sopenharmony_ci print('Traceback (most recent call last):', file=efile) 2617db96d56Sopenharmony_ci exclude = ("run.py", "rpc.py", "threading.py", "queue.py", 2627db96d56Sopenharmony_ci "debugger_r.py", "bdb.py") 2637db96d56Sopenharmony_ci cleanup_traceback(tbe, exclude) 2647db96d56Sopenharmony_ci traceback.print_list(tbe, file=efile) 2657db96d56Sopenharmony_ci lines = get_message_lines(typ, exc, tb) 2667db96d56Sopenharmony_ci for line in lines: 2677db96d56Sopenharmony_ci print(line, end='', file=efile) 2687db96d56Sopenharmony_ci 2697db96d56Sopenharmony_ci print_exc(typ, val, tb) 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_cidef cleanup_traceback(tb, exclude): 2727db96d56Sopenharmony_ci "Remove excluded traces from beginning/end of tb; get cached lines" 2737db96d56Sopenharmony_ci orig_tb = tb[:] 2747db96d56Sopenharmony_ci while tb: 2757db96d56Sopenharmony_ci for rpcfile in exclude: 2767db96d56Sopenharmony_ci if tb[0][0].count(rpcfile): 2777db96d56Sopenharmony_ci break # found an exclude, break for: and delete tb[0] 2787db96d56Sopenharmony_ci else: 2797db96d56Sopenharmony_ci break # no excludes, have left RPC code, break while: 2807db96d56Sopenharmony_ci del tb[0] 2817db96d56Sopenharmony_ci while tb: 2827db96d56Sopenharmony_ci for rpcfile in exclude: 2837db96d56Sopenharmony_ci if tb[-1][0].count(rpcfile): 2847db96d56Sopenharmony_ci break 2857db96d56Sopenharmony_ci else: 2867db96d56Sopenharmony_ci break 2877db96d56Sopenharmony_ci del tb[-1] 2887db96d56Sopenharmony_ci if len(tb) == 0: 2897db96d56Sopenharmony_ci # exception was in IDLE internals, don't prune! 2907db96d56Sopenharmony_ci tb[:] = orig_tb[:] 2917db96d56Sopenharmony_ci print("** IDLE Internal Exception: ", file=sys.stderr) 2927db96d56Sopenharmony_ci rpchandler = rpc.objecttable['exec'].rpchandler 2937db96d56Sopenharmony_ci for i in range(len(tb)): 2947db96d56Sopenharmony_ci fn, ln, nm, line = tb[i] 2957db96d56Sopenharmony_ci if nm == '?': 2967db96d56Sopenharmony_ci nm = "-toplevel-" 2977db96d56Sopenharmony_ci if not line and fn.startswith("<pyshell#"): 2987db96d56Sopenharmony_ci line = rpchandler.remotecall('linecache', 'getline', 2997db96d56Sopenharmony_ci (fn, ln), {}) 3007db96d56Sopenharmony_ci tb[i] = fn, ln, nm, line 3017db96d56Sopenharmony_ci 3027db96d56Sopenharmony_cidef flush_stdout(): 3037db96d56Sopenharmony_ci """XXX How to do this now?""" 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_cidef exit(): 3067db96d56Sopenharmony_ci """Exit subprocess, possibly after first clearing exit functions. 3077db96d56Sopenharmony_ci 3087db96d56Sopenharmony_ci If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any 3097db96d56Sopenharmony_ci functions registered with atexit will be removed before exiting. 3107db96d56Sopenharmony_ci (VPython support) 3117db96d56Sopenharmony_ci 3127db96d56Sopenharmony_ci """ 3137db96d56Sopenharmony_ci if no_exitfunc: 3147db96d56Sopenharmony_ci import atexit 3157db96d56Sopenharmony_ci atexit._clear() 3167db96d56Sopenharmony_ci capture_warnings(False) 3177db96d56Sopenharmony_ci sys.exit(0) 3187db96d56Sopenharmony_ci 3197db96d56Sopenharmony_ci 3207db96d56Sopenharmony_cidef fix_scaling(root): 3217db96d56Sopenharmony_ci """Scale fonts on HiDPI displays.""" 3227db96d56Sopenharmony_ci import tkinter.font 3237db96d56Sopenharmony_ci scaling = float(root.tk.call('tk', 'scaling')) 3247db96d56Sopenharmony_ci if scaling > 1.4: 3257db96d56Sopenharmony_ci for name in tkinter.font.names(root): 3267db96d56Sopenharmony_ci font = tkinter.font.Font(root=root, name=name, exists=True) 3277db96d56Sopenharmony_ci size = int(font['size']) 3287db96d56Sopenharmony_ci if size < 0: 3297db96d56Sopenharmony_ci font['size'] = round(-0.75*size) 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci 3327db96d56Sopenharmony_cidef fixdoc(fun, text): 3337db96d56Sopenharmony_ci tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else '' 3347db96d56Sopenharmony_ci fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text)) 3357db96d56Sopenharmony_ci 3367db96d56Sopenharmony_ciRECURSIONLIMIT_DELTA = 30 3377db96d56Sopenharmony_ci 3387db96d56Sopenharmony_cidef install_recursionlimit_wrappers(): 3397db96d56Sopenharmony_ci """Install wrappers to always add 30 to the recursion limit.""" 3407db96d56Sopenharmony_ci # see: bpo-26806 3417db96d56Sopenharmony_ci 3427db96d56Sopenharmony_ci @functools.wraps(sys.setrecursionlimit) 3437db96d56Sopenharmony_ci def setrecursionlimit(*args, **kwargs): 3447db96d56Sopenharmony_ci # mimic the original sys.setrecursionlimit()'s input handling 3457db96d56Sopenharmony_ci if kwargs: 3467db96d56Sopenharmony_ci raise TypeError( 3477db96d56Sopenharmony_ci "setrecursionlimit() takes no keyword arguments") 3487db96d56Sopenharmony_ci try: 3497db96d56Sopenharmony_ci limit, = args 3507db96d56Sopenharmony_ci except ValueError: 3517db96d56Sopenharmony_ci raise TypeError(f"setrecursionlimit() takes exactly one " 3527db96d56Sopenharmony_ci f"argument ({len(args)} given)") 3537db96d56Sopenharmony_ci if not limit > 0: 3547db96d56Sopenharmony_ci raise ValueError( 3557db96d56Sopenharmony_ci "recursion limit must be greater or equal than 1") 3567db96d56Sopenharmony_ci 3577db96d56Sopenharmony_ci return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci fixdoc(setrecursionlimit, f"""\ 3607db96d56Sopenharmony_ci This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible 3617db96d56Sopenharmony_ci uninterruptible loops.""") 3627db96d56Sopenharmony_ci 3637db96d56Sopenharmony_ci @functools.wraps(sys.getrecursionlimit) 3647db96d56Sopenharmony_ci def getrecursionlimit(): 3657db96d56Sopenharmony_ci return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA 3667db96d56Sopenharmony_ci 3677db96d56Sopenharmony_ci fixdoc(getrecursionlimit, f"""\ 3687db96d56Sopenharmony_ci This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate 3697db96d56Sopenharmony_ci for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""") 3707db96d56Sopenharmony_ci 3717db96d56Sopenharmony_ci # add the delta to the default recursion limit, to compensate 3727db96d56Sopenharmony_ci sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci sys.setrecursionlimit = setrecursionlimit 3757db96d56Sopenharmony_ci sys.getrecursionlimit = getrecursionlimit 3767db96d56Sopenharmony_ci 3777db96d56Sopenharmony_ci 3787db96d56Sopenharmony_cidef uninstall_recursionlimit_wrappers(): 3797db96d56Sopenharmony_ci """Uninstall the recursion limit wrappers from the sys module. 3807db96d56Sopenharmony_ci 3817db96d56Sopenharmony_ci IDLE only uses this for tests. Users can import run and call 3827db96d56Sopenharmony_ci this to remove the wrapping. 3837db96d56Sopenharmony_ci """ 3847db96d56Sopenharmony_ci if ( 3857db96d56Sopenharmony_ci getattr(sys.setrecursionlimit, '__wrapped__', None) and 3867db96d56Sopenharmony_ci getattr(sys.getrecursionlimit, '__wrapped__', None) 3877db96d56Sopenharmony_ci ): 3887db96d56Sopenharmony_ci sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__ 3897db96d56Sopenharmony_ci sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__ 3907db96d56Sopenharmony_ci sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA) 3917db96d56Sopenharmony_ci 3927db96d56Sopenharmony_ci 3937db96d56Sopenharmony_ciclass MyRPCServer(rpc.RPCServer): 3947db96d56Sopenharmony_ci 3957db96d56Sopenharmony_ci def handle_error(self, request, client_address): 3967db96d56Sopenharmony_ci """Override RPCServer method for IDLE 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci Interrupt the MainThread and exit server if link is dropped. 3997db96d56Sopenharmony_ci 4007db96d56Sopenharmony_ci """ 4017db96d56Sopenharmony_ci global quitting 4027db96d56Sopenharmony_ci try: 4037db96d56Sopenharmony_ci raise 4047db96d56Sopenharmony_ci except SystemExit: 4057db96d56Sopenharmony_ci raise 4067db96d56Sopenharmony_ci except EOFError: 4077db96d56Sopenharmony_ci global exit_now 4087db96d56Sopenharmony_ci exit_now = True 4097db96d56Sopenharmony_ci thread.interrupt_main() 4107db96d56Sopenharmony_ci except: 4117db96d56Sopenharmony_ci erf = sys.__stderr__ 4127db96d56Sopenharmony_ci print(textwrap.dedent(f""" 4137db96d56Sopenharmony_ci {'-'*40} 4147db96d56Sopenharmony_ci Unhandled exception in user code execution server!' 4157db96d56Sopenharmony_ci Thread: {threading.current_thread().name} 4167db96d56Sopenharmony_ci IDLE Client Address: {client_address} 4177db96d56Sopenharmony_ci Request: {request!r} 4187db96d56Sopenharmony_ci """), file=erf) 4197db96d56Sopenharmony_ci traceback.print_exc(limit=-20, file=erf) 4207db96d56Sopenharmony_ci print(textwrap.dedent(f""" 4217db96d56Sopenharmony_ci *** Unrecoverable, server exiting! 4227db96d56Sopenharmony_ci 4237db96d56Sopenharmony_ci Users should never see this message; it is likely transient. 4247db96d56Sopenharmony_ci If this recurs, report this with a copy of the message 4257db96d56Sopenharmony_ci and an explanation of how to make it repeat. 4267db96d56Sopenharmony_ci {'-'*40}"""), file=erf) 4277db96d56Sopenharmony_ci quitting = True 4287db96d56Sopenharmony_ci thread.interrupt_main() 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci 4317db96d56Sopenharmony_ci# Pseudofiles for shell-remote communication (also used in pyshell) 4327db96d56Sopenharmony_ci 4337db96d56Sopenharmony_ciclass StdioFile(io.TextIOBase): 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci def __init__(self, shell, tags, encoding='utf-8', errors='strict'): 4367db96d56Sopenharmony_ci self.shell = shell 4377db96d56Sopenharmony_ci self.tags = tags 4387db96d56Sopenharmony_ci self._encoding = encoding 4397db96d56Sopenharmony_ci self._errors = errors 4407db96d56Sopenharmony_ci 4417db96d56Sopenharmony_ci @property 4427db96d56Sopenharmony_ci def encoding(self): 4437db96d56Sopenharmony_ci return self._encoding 4447db96d56Sopenharmony_ci 4457db96d56Sopenharmony_ci @property 4467db96d56Sopenharmony_ci def errors(self): 4477db96d56Sopenharmony_ci return self._errors 4487db96d56Sopenharmony_ci 4497db96d56Sopenharmony_ci @property 4507db96d56Sopenharmony_ci def name(self): 4517db96d56Sopenharmony_ci return '<%s>' % self.tags 4527db96d56Sopenharmony_ci 4537db96d56Sopenharmony_ci def isatty(self): 4547db96d56Sopenharmony_ci return True 4557db96d56Sopenharmony_ci 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ciclass StdOutputFile(StdioFile): 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_ci def writable(self): 4607db96d56Sopenharmony_ci return True 4617db96d56Sopenharmony_ci 4627db96d56Sopenharmony_ci def write(self, s): 4637db96d56Sopenharmony_ci if self.closed: 4647db96d56Sopenharmony_ci raise ValueError("write to closed file") 4657db96d56Sopenharmony_ci s = str.encode(s, self.encoding, self.errors).decode(self.encoding, self.errors) 4667db96d56Sopenharmony_ci return self.shell.write(s, self.tags) 4677db96d56Sopenharmony_ci 4687db96d56Sopenharmony_ci 4697db96d56Sopenharmony_ciclass StdInputFile(StdioFile): 4707db96d56Sopenharmony_ci _line_buffer = '' 4717db96d56Sopenharmony_ci 4727db96d56Sopenharmony_ci def readable(self): 4737db96d56Sopenharmony_ci return True 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci def read(self, size=-1): 4767db96d56Sopenharmony_ci if self.closed: 4777db96d56Sopenharmony_ci raise ValueError("read from closed file") 4787db96d56Sopenharmony_ci if size is None: 4797db96d56Sopenharmony_ci size = -1 4807db96d56Sopenharmony_ci elif not isinstance(size, int): 4817db96d56Sopenharmony_ci raise TypeError('must be int, not ' + type(size).__name__) 4827db96d56Sopenharmony_ci result = self._line_buffer 4837db96d56Sopenharmony_ci self._line_buffer = '' 4847db96d56Sopenharmony_ci if size < 0: 4857db96d56Sopenharmony_ci while line := self.shell.readline(): 4867db96d56Sopenharmony_ci result += line 4877db96d56Sopenharmony_ci else: 4887db96d56Sopenharmony_ci while len(result) < size: 4897db96d56Sopenharmony_ci line = self.shell.readline() 4907db96d56Sopenharmony_ci if not line: break 4917db96d56Sopenharmony_ci result += line 4927db96d56Sopenharmony_ci self._line_buffer = result[size:] 4937db96d56Sopenharmony_ci result = result[:size] 4947db96d56Sopenharmony_ci return result 4957db96d56Sopenharmony_ci 4967db96d56Sopenharmony_ci def readline(self, size=-1): 4977db96d56Sopenharmony_ci if self.closed: 4987db96d56Sopenharmony_ci raise ValueError("read from closed file") 4997db96d56Sopenharmony_ci if size is None: 5007db96d56Sopenharmony_ci size = -1 5017db96d56Sopenharmony_ci elif not isinstance(size, int): 5027db96d56Sopenharmony_ci raise TypeError('must be int, not ' + type(size).__name__) 5037db96d56Sopenharmony_ci line = self._line_buffer or self.shell.readline() 5047db96d56Sopenharmony_ci if size < 0: 5057db96d56Sopenharmony_ci size = len(line) 5067db96d56Sopenharmony_ci eol = line.find('\n', 0, size) 5077db96d56Sopenharmony_ci if eol >= 0: 5087db96d56Sopenharmony_ci size = eol + 1 5097db96d56Sopenharmony_ci self._line_buffer = line[size:] 5107db96d56Sopenharmony_ci return line[:size] 5117db96d56Sopenharmony_ci 5127db96d56Sopenharmony_ci def close(self): 5137db96d56Sopenharmony_ci self.shell.close() 5147db96d56Sopenharmony_ci 5157db96d56Sopenharmony_ci 5167db96d56Sopenharmony_ciclass MyHandler(rpc.RPCHandler): 5177db96d56Sopenharmony_ci 5187db96d56Sopenharmony_ci def handle(self): 5197db96d56Sopenharmony_ci """Override base method""" 5207db96d56Sopenharmony_ci executive = Executive(self) 5217db96d56Sopenharmony_ci self.register("exec", executive) 5227db96d56Sopenharmony_ci self.console = self.get_remote_proxy("console") 5237db96d56Sopenharmony_ci sys.stdin = StdInputFile(self.console, "stdin", 5247db96d56Sopenharmony_ci iomenu.encoding, iomenu.errors) 5257db96d56Sopenharmony_ci sys.stdout = StdOutputFile(self.console, "stdout", 5267db96d56Sopenharmony_ci iomenu.encoding, iomenu.errors) 5277db96d56Sopenharmony_ci sys.stderr = StdOutputFile(self.console, "stderr", 5287db96d56Sopenharmony_ci iomenu.encoding, "backslashreplace") 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci sys.displayhook = rpc.displayhook 5317db96d56Sopenharmony_ci # page help() text to shell. 5327db96d56Sopenharmony_ci import pydoc # import must be done here to capture i/o binding 5337db96d56Sopenharmony_ci pydoc.pager = pydoc.plainpager 5347db96d56Sopenharmony_ci 5357db96d56Sopenharmony_ci # Keep a reference to stdin so that it won't try to exit IDLE if 5367db96d56Sopenharmony_ci # sys.stdin gets changed from within IDLE's shell. See issue17838. 5377db96d56Sopenharmony_ci self._keep_stdin = sys.stdin 5387db96d56Sopenharmony_ci 5397db96d56Sopenharmony_ci install_recursionlimit_wrappers() 5407db96d56Sopenharmony_ci 5417db96d56Sopenharmony_ci self.interp = self.get_remote_proxy("interp") 5427db96d56Sopenharmony_ci rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) 5437db96d56Sopenharmony_ci 5447db96d56Sopenharmony_ci def exithook(self): 5457db96d56Sopenharmony_ci "override SocketIO method - wait for MainThread to shut us down" 5467db96d56Sopenharmony_ci time.sleep(10) 5477db96d56Sopenharmony_ci 5487db96d56Sopenharmony_ci def EOFhook(self): 5497db96d56Sopenharmony_ci "Override SocketIO method - terminate wait on callback and exit thread" 5507db96d56Sopenharmony_ci global quitting 5517db96d56Sopenharmony_ci quitting = True 5527db96d56Sopenharmony_ci thread.interrupt_main() 5537db96d56Sopenharmony_ci 5547db96d56Sopenharmony_ci def decode_interrupthook(self): 5557db96d56Sopenharmony_ci "interrupt awakened thread" 5567db96d56Sopenharmony_ci global quitting 5577db96d56Sopenharmony_ci quitting = True 5587db96d56Sopenharmony_ci thread.interrupt_main() 5597db96d56Sopenharmony_ci 5607db96d56Sopenharmony_ci 5617db96d56Sopenharmony_ciclass Executive: 5627db96d56Sopenharmony_ci 5637db96d56Sopenharmony_ci def __init__(self, rpchandler): 5647db96d56Sopenharmony_ci self.rpchandler = rpchandler 5657db96d56Sopenharmony_ci if idlelib.testing is False: 5667db96d56Sopenharmony_ci self.locals = __main__.__dict__ 5677db96d56Sopenharmony_ci self.calltip = calltip.Calltip() 5687db96d56Sopenharmony_ci self.autocomplete = autocomplete.AutoComplete() 5697db96d56Sopenharmony_ci else: 5707db96d56Sopenharmony_ci self.locals = {} 5717db96d56Sopenharmony_ci 5727db96d56Sopenharmony_ci def runcode(self, code): 5737db96d56Sopenharmony_ci global interruptable 5747db96d56Sopenharmony_ci try: 5757db96d56Sopenharmony_ci self.user_exc_info = None 5767db96d56Sopenharmony_ci interruptable = True 5777db96d56Sopenharmony_ci try: 5787db96d56Sopenharmony_ci exec(code, self.locals) 5797db96d56Sopenharmony_ci finally: 5807db96d56Sopenharmony_ci interruptable = False 5817db96d56Sopenharmony_ci except SystemExit as e: 5827db96d56Sopenharmony_ci if e.args: # SystemExit called with an argument. 5837db96d56Sopenharmony_ci ob = e.args[0] 5847db96d56Sopenharmony_ci if not isinstance(ob, (type(None), int)): 5857db96d56Sopenharmony_ci print('SystemExit: ' + str(ob), file=sys.stderr) 5867db96d56Sopenharmony_ci # Return to the interactive prompt. 5877db96d56Sopenharmony_ci except: 5887db96d56Sopenharmony_ci self.user_exc_info = sys.exc_info() # For testing, hook, viewer. 5897db96d56Sopenharmony_ci if quitting: 5907db96d56Sopenharmony_ci exit() 5917db96d56Sopenharmony_ci if sys.excepthook is sys.__excepthook__: 5927db96d56Sopenharmony_ci print_exception() 5937db96d56Sopenharmony_ci else: 5947db96d56Sopenharmony_ci try: 5957db96d56Sopenharmony_ci sys.excepthook(*self.user_exc_info) 5967db96d56Sopenharmony_ci except: 5977db96d56Sopenharmony_ci self.user_exc_info = sys.exc_info() # For testing. 5987db96d56Sopenharmony_ci print_exception() 5997db96d56Sopenharmony_ci jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>") 6007db96d56Sopenharmony_ci if jit: 6017db96d56Sopenharmony_ci self.rpchandler.interp.open_remote_stack_viewer() 6027db96d56Sopenharmony_ci else: 6037db96d56Sopenharmony_ci flush_stdout() 6047db96d56Sopenharmony_ci 6057db96d56Sopenharmony_ci def interrupt_the_server(self): 6067db96d56Sopenharmony_ci if interruptable: 6077db96d56Sopenharmony_ci thread.interrupt_main() 6087db96d56Sopenharmony_ci 6097db96d56Sopenharmony_ci def start_the_debugger(self, gui_adap_oid): 6107db96d56Sopenharmony_ci return debugger_r.start_debugger(self.rpchandler, gui_adap_oid) 6117db96d56Sopenharmony_ci 6127db96d56Sopenharmony_ci def stop_the_debugger(self, idb_adap_oid): 6137db96d56Sopenharmony_ci "Unregister the Idb Adapter. Link objects and Idb then subject to GC" 6147db96d56Sopenharmony_ci self.rpchandler.unregister(idb_adap_oid) 6157db96d56Sopenharmony_ci 6167db96d56Sopenharmony_ci def get_the_calltip(self, name): 6177db96d56Sopenharmony_ci return self.calltip.fetch_tip(name) 6187db96d56Sopenharmony_ci 6197db96d56Sopenharmony_ci def get_the_completion_list(self, what, mode): 6207db96d56Sopenharmony_ci return self.autocomplete.fetch_completions(what, mode) 6217db96d56Sopenharmony_ci 6227db96d56Sopenharmony_ci def stackviewer(self, flist_oid=None): 6237db96d56Sopenharmony_ci if self.user_exc_info: 6247db96d56Sopenharmony_ci typ, val, tb = self.user_exc_info 6257db96d56Sopenharmony_ci else: 6267db96d56Sopenharmony_ci return None 6277db96d56Sopenharmony_ci flist = None 6287db96d56Sopenharmony_ci if flist_oid is not None: 6297db96d56Sopenharmony_ci flist = self.rpchandler.get_remote_proxy(flist_oid) 6307db96d56Sopenharmony_ci while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]: 6317db96d56Sopenharmony_ci tb = tb.tb_next 6327db96d56Sopenharmony_ci sys.last_type = typ 6337db96d56Sopenharmony_ci sys.last_value = val 6347db96d56Sopenharmony_ci item = stackviewer.StackTreeItem(flist, tb) 6357db96d56Sopenharmony_ci return debugobj_r.remote_object_tree_item(item) 6367db96d56Sopenharmony_ci 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ciif __name__ == '__main__': 6397db96d56Sopenharmony_ci from unittest import main 6407db96d56Sopenharmony_ci main('idlelib.idle_test.test_run', verbosity=2) 6417db96d56Sopenharmony_ci 6427db96d56Sopenharmony_cicapture_warnings(False) # Make sure turned off; see bpo-18081. 643