diff --git a/pyflakes/checker.py b/pyflakes/checker.py index bd5eba57..04c994ee 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -494,6 +494,7 @@ def __init__(self, tree, filename='(none)', builtins=None, self.scopeStack = [ModuleScope()] self.exceptHandlers = [()] self.root = tree + self._recursion_limit = sys.getrecursionlimit() self.handleChildren(tree) self.runDeferred(self._deferredFunctions) # Set _deferredFunctions to None so that deferFunction will fail @@ -820,6 +821,15 @@ def on_conditional_branch(): self.report(messages.UndefinedName, node, name) def handleChildren(self, tree, omit=None): + # The recursion limit needs to be at least double nodeDepth + # as the recursion cycles between handleChildren and handleNode. + # Set it to triple nodeDepth to account for other items on the stack, + # and to reduce the frequency of changes to the limit. + acceptable_recursion_limit = self.nodeDepth * 3 + if self._recursion_limit <= acceptable_recursion_limit: + sys.setrecursionlimit(acceptable_recursion_limit) + self._recursion_limit = acceptable_recursion_limit + for node in iter_child_nodes(tree, omit=omit): self.handleNode(node, tree) diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py index 364b3750..5cad372f 100644 --- a/pyflakes/test/test_other.py +++ b/pyflakes/test/test_other.py @@ -1,6 +1,7 @@ """ Tests for various Pyflakes behavior. """ +import sys from sys import version_info @@ -1993,3 +1994,18 @@ def test_raise_notimplemented(self): self.flakes(''' raise NotImplemented ''', m.RaiseNotImplemented) + + +class TestMaximumRecursion(TestCase): + + def setUp(self): + self._recursionlimit = sys.getrecursionlimit() + + def test_recursion_limit(self): + # Using self._recursionlimit * 10 tends to cause CPython to core dump. + r = range(self._recursionlimit * 9) + s = 'x = ' + ' + '.join(str(n) for n in r) + self.flakes(s) + + def tearDown(self): + sys.setrecursionlimit(self._recursionlimit)