-
-
Notifications
You must be signed in to change notification settings - Fork 273
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
MultiLineBlockMixin
causes stack overflow from recursive _get_assign_nodes()
#786
Comments
Hey @DavidCain That's a great analysis, thank you. Moving to a stack based solution definitely works for us, so would be great if you can write up a PR for this! |
Great! I'll whip up a PR then and reference this issue. Thanks for reviewing. It will probably take me until this weekend to find the time. |
Sorry, haven't forgotten about this issue just yet, but it clearly took me more time than just a week to find the time. =) I'll hopefully have a PR out soon-ish. |
I get this RecursionError very consistently with pylint==2.5.2 and astroid==2.4.1. I'd appreciate any help resolving it because it makes pylint pretty much useless.
|
Hi I'm having a hard time reproducing this error with thrift @DavidCain Here are the commands I used: Download your thrift code as
What versions of thrift are you using? |
Hi, Bryce. Pretty much any version of (Note that my $ echo 'struct LargeStruct {' > poc.thrift
$ for i in {1..200}; do; echo " $i: optional string arg_$i;"; done >> poc.thrift
$ echo '}' >> poc.thrift
$ md5 poc.thrift
MD5 (poc.thrift) = 92d97c7a0e9d57c37d95e8adfe93fbfc
$ thrift --gen py poc.thrift
$ pylint gen-py
...
File "/Users/david/.pyenv/versions/3.7.7/envs/venv-poc/lib/python3.7/site-packages/astroid/mixins.py", line 130, in <genexpr>
return tuple(getattr(self, field) for field in self._multi_line_block_fields)
RecursionError: maximum recursion depth exceeded while calling a Python object
|
We're seeing a similar issue using method chaining. I have an example here: https://pastebin.com/uTyDLrDY Removing one .method() call prevents the recursion error. So does (Obviously in our production case each method is different and does more than just increase a counter) |
Argh, sorry for leaving this issue unresolved for so long. I won't make any future promises about when I might tackle a PR, but maybe one day soon. In the meantime, @jbwdevries - I can at least document the workaround we're using! Temporary workaround: increase stack sizeWe've implemented a custom pylint checker. Because this checker is imported before # checkers/__init__.py - in a custom checker package
import sys
def register(linter):
# HACK: Increase recursion limit to prevent stack overflow
# See: https://github.com/PyCQA/astroid/issues/786
sys.setrecursionlimit(3000) # (Larger than `sys.getrecursionlimit()`)
# (registration of custom checkers is omitted for brevity) Then in the project's
( 3000 is an arbitrary limit that should be tuned to your own needs. Too large of a stack will obviously create problems, and this doesn't solve the actual problem but merely increases the number of times that astroid can recurse). Hopefully this is helpful. |
@DavidCain thanks for this little hack! And do not worry about your promise! We are all volunteers here and we do as much as we can within our free time! 😄 |
Problem
The recursive nature of the
_get_assign_nodes()
method can lead to aRecursionError
and break runningpylint
on a project that has a class with a large control structure in its method.Workaround
A hackish means of avoiding this bug is to increase the stack size to some value that enables further recursion without exhausting the stack. It's documented in a comment, but in essence the idea is calling
sys.setrecursionlimit(some_int)
before runningpylint
orastroid
). A more robust fix would be to avoid recursion altogether since this hack only increases the number of lines astroid can parse.Affected versions
I've tested this on every minor version since Astroid 2.1.0. Astroid 2.0.4 is not affected.
Since the release of Astroid 2.1.0, we've been experiencing stack overflow exceptions for a class with a very large chain of
if/else
statements.How to reproduce:
The following Python file is enough to trigger a recursion error on any version of Astroid from 2.1.0 through the unreleased 2.5.0:
Source of the
RecursionError
The current implementation of
MultiLineBlockMixin
makes a recursive call to_get_assign_nodes()
:Possible fix
I think that a possible fix would be to switch from a recursive design, towards a stack-based solution. If we instead built a list of nodes as we go (say, with a
while
loop & a stack), we could avoid the overflow.The generators used in
_get_assign_nodes()
are eagerly converted into a list anyway, so keeping a list of children then popping to get all assign nodes should have no negative performance effects.A quick attempt on my own machine seems to work (though I've not written any new tests, or tested my changes against the test suite).
A less contrived reproduction
I stumbled upon this error by trying to run
astroid
on a class that's automatically produced by Apache Thrift.Given an input file like so:
One can run
thrift --gen py proof_of_concept.thrift
to producegen-py/proof_of_concept/ttypes.py
In the produced file is a large Python class - the many
if/elif
statements inread()
method is what trips upastroid
:Large Thrift structs are fairly common, so this can happen with some frequency.
Pull request?
I understand if parsing hundreds of control statements isn't something that
astroid
seeks to be able to do.However, if you're open to refactoring out recursion, I'd be willing to write up a PR for this problem.
The text was updated successfully, but these errors were encountered: