-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
bpo-26806: add 30 to the recursion limit in IDLE's shell #13944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
062b111
7d7b622
f3b93f5
af3ee2b
9ddd699
38bab8d
57c8b7b
6f6ca69
ca15c75
d89350c
7e7d21d
6c20f8c
e7427ee
fd05a6a
2c1d8d5
7d6c545
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
1. define wrapping code at the module level 2. increase recursion limit delta from 25 to 30 3. add a note to the wrapped functions' doc-strings 4. extract the recursion limit delta to a variable 5. rework test and add another one
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| 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 | ||
|
|
@@ -304,6 +305,60 @@ 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("setrecursionlimit() takes exactly one " | ||
| "argument ({} given)".format(len(args))) | ||
| 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.""" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is (only) needed for tests. Add line "# Needed for tests." so function is never deleted as 'unused'. (I have made similar notes for other test-only code.)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would it be deleted if it is used in test code? Even if it were deleted, the tests would immediately break! Also, there's no telling who might find other uses for this in the future.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me start over. I initially wasted time looking for where this was used in run.py. I like to mark test code (code we/IDLE only use as such). So I moved the comment, reworded, into the docstring and added a note that users could also use this. (Yeah, one of use might want to do so sometime.) I think that this is an improvement. |
||
| if ( | ||
| getattr(sys.setrecursionlimit, '__wrapped__') and | ||
| getattr(sys.getrecursionlimit, '__wrapped__') | ||
| ): | ||
| 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): | ||
|
|
@@ -447,6 +502,8 @@ def handle(self): | |
| # 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) | ||
|
|
||
|
|
@@ -475,50 +532,6 @@ def __init__(self, rpchandler): | |
| self.calltip = calltip.Calltip() | ||
| self.autocomplete = autocomplete.AutoComplete() | ||
|
|
||
| self._wrap_sys_setrecursionlimit() | ||
|
|
||
| def _wrap_sys_setrecursionlimit(self): | ||
| "transparently always add 25 to the recursion limit" | ||
| # see: bpo-26806 | ||
| code = textwrap.dedent("""\ | ||
| import sys | ||
| from functools import wraps | ||
|
|
||
| # add the delta to the default recursion limit, to compensate | ||
| sys.setrecursionlimit(sys.getrecursionlimit() + 25) | ||
|
|
||
| @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("setrecursionlimit() takes exactly one " | ||
| "argument ({} given)".format(len(args))) | ||
| if not limit > 0: | ||
| raise ValueError( | ||
| "recursion limit must be greater or equal than 1") | ||
|
|
||
| import sys | ||
| return sys.setrecursionlimit.__wrapped__(limit + 25) | ||
| sys.setrecursionlimit = setrecursionlimit | ||
| del setrecursionlimit | ||
|
|
||
| @wraps(sys.getrecursionlimit) | ||
| def getrecursionlimit(): | ||
| import sys | ||
| return sys.getrecursionlimit.__wrapped__() - 25 | ||
| sys.getrecursionlimit = getrecursionlimit | ||
| del getrecursionlimit | ||
|
|
||
| del sys | ||
| del wraps | ||
| """) | ||
| exec(code, self.locals) | ||
|
|
||
| def runcode(self, code): | ||
| global interruptable | ||
| try: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use f-string here also.
<DONE, and added bad call tests>
Messages verified. It is somewhat annoying that C and python functions with same signature (here, 'limit, /') give different error messages for the same error. (A separate issue for the tracker.) So we cannot, even for 3.8+, when '/' is valid, match behavior by just giving the proper signature.