diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2019-07-06 12:54:17 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-06 12:54:17 (GMT) |
commit | d4af55391f56286ab8d478591017174a5a0a5ce2 (patch) | |
tree | e2bbbb3535f87d3286a25beb90f2dc29f0d43d87 /Lib/idlelib/run.py | |
parent | ea9c8caa13977561787bf2de430f18c2031dde0d (diff) | |
download | cpython-d4af55391f56286ab8d478591017174a5a0a5ce2.zip cpython-d4af55391f56286ab8d478591017174a5a0a5ce2.tar.gz cpython-d4af55391f56286ab8d478591017174a5a0a5ce2.tar.bz2 |
bpo-26806: add 30 to the recursion limit in IDLE's shell (GH-13944)
This is done to compensate for the extra stack frames added by
IDLE itself, which cause problems when setting the recursion limit
to low values.
This wraps sys.setrecursionlimit() and sys.getrecursionlimit()
as invisibly as possible.
(cherry picked from commit fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71)
Co-authored-by: Tal Einat <taleinat+github@gmail.com>
Diffstat (limited to 'Lib/idlelib/run.py')
-rw-r--r-- | Lib/idlelib/run.py | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 6b3928b..c6ed76b 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -4,10 +4,12 @@ Simplified, pyshell.ModifiedInterpreter spawns a subprocess with f'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' '.run' is needed because __import__ returns idlelib, not idlelib.run. """ +import functools import io import linecache import queue import sys +import textwrap import time import traceback import _thread as thread @@ -305,6 +307,64 @@ def fix_scaling(root): font['size'] = round(-0.75*size) +RECURSIONLIMIT_DELTA = 30 +def install_recursionlimit_wrappers(): + """Install wrappers to always add 30 to the recursion limit.""" + # see: bpo-26806 + + @functools.wraps(sys.setrecursionlimit) + def setrecursionlimit(*args, **kwargs): + # mimic the original sys.setrecursionlimit()'s input handling + if kwargs: + raise TypeError( + "setrecursionlimit() takes no keyword arguments") + try: + limit, = args + except ValueError: + raise TypeError(f"setrecursionlimit() takes exactly one " + f"argument ({len(args)} given)") + if not limit > 0: + raise ValueError( + "recursion limit must be greater or equal than 1") + + return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA) + + setrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible + uninterruptible loops. + """).strip()) + + @functools.wraps(sys.getrecursionlimit) + def getrecursionlimit(): + return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA + + getrecursionlimit.__doc__ += "\n\n" + textwrap.fill(textwrap.dedent(f"""\ + This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate for + the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit. + """).strip()) + + # add the delta to the default recursion limit, to compensate + sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA) + + sys.setrecursionlimit = setrecursionlimit + sys.getrecursionlimit = getrecursionlimit + + +def uninstall_recursionlimit_wrappers(): + """Uninstall the recursion limit wrappers from the sys module. + + IDLE only uses this for tests. Users can import run and call + this to remove the wrapping. + """ + if ( + getattr(sys.setrecursionlimit, '__wrapped__', None) and + getattr(sys.getrecursionlimit, '__wrapped__', None) + ): + sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__ + sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__ + sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA) + + class MyRPCServer(rpc.RPCServer): def handle_error(self, request, client_address): @@ -448,6 +508,8 @@ class MyHandler(rpc.RPCHandler): # sys.stdin gets changed from within IDLE's shell. See issue17838. self._keep_stdin = sys.stdin + install_recursionlimit_wrappers() + self.interp = self.get_remote_proxy("interp") rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) |