Skip to content

Commit bf41b09

Browse files
committed
Manage recursion limit preventing RuntimeError
pyflakes has traditionally recursed with a handler for every level of the ast. The ast depth can become very large, especially for an expression containing many binary operators. Python has a maximum recursion limit, defaulting to a low number like 1000, which resulted in a RuntimeError for the ast of: x = 1 + 2 + 3 + ... + 1001 To workaround this problem, pyflakes now increases the recursion limit at runtime when it knows it will be exceeded. Fixes lp:1507827
1 parent 4b2d720 commit bf41b09

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

pyflakes/checker.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ def __init__(self, tree, filename='(none)', builtins=None,
494494
self.scopeStack = [ModuleScope()]
495495
self.exceptHandlers = [()]
496496
self.root = tree
497+
self._recursion_limit = sys.getrecursionlimit()
497498
self.handleChildren(tree)
498499
self.runDeferred(self._deferredFunctions)
499500
# Set _deferredFunctions to None so that deferFunction will fail
@@ -820,6 +821,15 @@ def on_conditional_branch():
820821
self.report(messages.UndefinedName, node, name)
821822

822823
def handleChildren(self, tree, omit=None):
824+
# The recursion limit needs to be at least double nodeDepth
825+
# as the recursion cycles between handleChildren and handleNode.
826+
# Set it to triple nodeDepth to account for other items on the stack,
827+
# and to reduce the frequency of changes to the limit.
828+
acceptable_recursion_limit = self.nodeDepth * 3
829+
if self._recursion_limit <= acceptable_recursion_limit:
830+
sys.setrecursionlimit(acceptable_recursion_limit)
831+
self._recursion_limit = acceptable_recursion_limit
832+
823833
for node in iter_child_nodes(tree, omit=omit):
824834
self.handleNode(node, tree)
825835

pyflakes/test/test_other.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Tests for various Pyflakes behavior.
33
"""
4+
import sys
45

56
from sys import version_info
67

@@ -1993,3 +1994,18 @@ def test_raise_notimplemented(self):
19931994
self.flakes('''
19941995
raise NotImplemented
19951996
''', m.RaiseNotImplemented)
1997+
1998+
1999+
class TestMaximumRecursion(TestCase):
2000+
2001+
def setUp(self):
2002+
self._recursionlimit = sys.getrecursionlimit()
2003+
2004+
def test_recursion_limit(self):
2005+
# Using self._recursionlimit * 10 tends to cause CPython to core dump.
2006+
r = range(self._recursionlimit * 9)
2007+
s = 'x = ' + ' + '.join(str(n) for n in r)
2008+
self.flakes(s)
2009+
2010+
def tearDown(self):
2011+
sys.setrecursionlimit(self._recursionlimit)

0 commit comments

Comments
 (0)