Skip to content

Commit b7ff168

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 f69d4b4 commit b7ff168

File tree

2 files changed

+23
-0
lines changed

2 files changed

+23
-0
lines changed

pyflakes/checker.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,13 @@ def on_conditional_branch():
734734
self.report(messages.UndefinedName, node, name)
735735

736736
def handleChildren(self, tree, omit=None):
737+
# The recursion limit needs to be at least double nodeDepth
738+
# as the recursion cycles between handleChildren and handleNode.
739+
# Set it to triple nodeDepth to account for other items on the stack,
740+
# and to reduce the frequency of changes to the limit.
741+
acceptable_recursion_limit = self.nodeDepth * 3
742+
if sys.getrecursionlimit() <= acceptable_recursion_limit:
743+
sys.setrecursionlimit(acceptable_recursion_limit)
737744
for node in iter_child_nodes(tree, omit=omit):
738745
self.handleNode(node, tree)
739746

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

@@ -1743,3 +1744,18 @@ def test_matmul(self):
17431744
def foo(a, b):
17441745
return a @ b
17451746
''')
1747+
1748+
1749+
class TestMaximumRecursion(TestCase):
1750+
1751+
def setUp(self):
1752+
self._recursionlimit = sys.getrecursionlimit()
1753+
1754+
def test_recursion_limit(self):
1755+
# Using self._recursionlimit * 10 tends to cause CPython to core dump.
1756+
r = range(self._recursionlimit * 9)
1757+
s = 'x = ' + ' + '.join(str(n) for n in r)
1758+
self.flakes(s)
1759+
1760+
def tearDown(self):
1761+
sys.setrecursionlimit(self._recursionlimit)

0 commit comments

Comments
 (0)