diff --git a/mccabe.py b/mccabe.py index cc10d7c..0f5c636 100644 --- a/mccabe.py +++ b/mccabe.py @@ -199,10 +199,23 @@ def _subgraph_parse(self, node, pathnode, extra_blocks): self.tail = pathnode self.dispatch_list(extra.body) loose_ends.append(self.tail) - if node.orelse: - self.tail = pathnode - self.dispatch_list(node.orelse) - loose_ends.append(self.tail) + # Handle elif chains iteratively to avoid RecursionError + while node.orelse: + if (len(node.orelse) == 1 + and isinstance(node.orelse[0], ast.If)): + # elif branch: process inline instead of recursing + node = node.orelse[0] + name = "If %d" % node.lineno + elif_node = self.appendPathNode(name) + self.tail = elif_node + self.dispatch_list(node.body) + loose_ends.append(self.tail) + else: + # else branch + self.tail = pathnode + self.dispatch_list(node.orelse) + loose_ends.append(self.tail) + break else: loose_ends.append(pathnode) if pathnode: diff --git a/test_mccabe.py b/test_mccabe.py index fe6e8d3..f32ad82 100644 --- a/test_mccabe.py +++ b/test_mccabe.py @@ -238,6 +238,18 @@ class _options(object): def test_get_module_complexity(self): self.assertEqual(0, mccabe.get_module_complexity("mccabe.py")) + def test_long_elif_chain(self): + """Ensure bug #71 does not regress (RecursionError on long elif).""" + lines = ['def func(x):'] + lines.append(' if x == 0:') + lines.append(' return 0') + for i in range(1, 200): + lines.append(' elif x == %d:' % i) + lines.append(' return %d' % i) + code = '\n'.join(lines) + '\n' + complexity = get_code_complexity(code, threshold=0) + self.assertEqual(complexity, 1) + # This test uses the Hypothesis and Hypothesmith libraries to generate random # syntatically-valid Python source code and applies McCabe on it.