From 846db111ca2504a34899aca055ac6219f1ee5d21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Oct 2025 05:17:44 +0000 Subject: [PATCH 1/3] Initial plan From 3043a2080e78f739372391fa1132a4a5ad63f50d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Oct 2025 05:29:12 +0000 Subject: [PATCH 2/3] Add RecursionError handling for deeply nested AST structures Co-authored-by: jendrikseipp <213955+jendrikseipp@users.noreply.github.com> --- CHANGELOG.md | 1 + tests/test_errors.py | 9 +++++++++ vulture/core.py | 13 ++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40098893..61bfb73c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Drop support for Python 3.8 (Jendrik Seipp, #398). * Handle `while True` loops without `break` statements (kreathon). * Add whitelist for `ssl.SSLContext` (tunnelsociety, #392). +* Handle RecursionError when analyzing deeply nested code structures (GitHub Copilot). # 2.14 (2024-12-08) diff --git a/tests/test_errors.py b/tests/test_errors.py index 17bbffe3..b9bbdc6d 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -33,3 +33,12 @@ def test_invalid_cmdline_args(): call_vulture(["vulture/", "--invalid-argument"]) == ExitCode.InvalidCmdlineArguments ) + + +def test_recursion_error(v): + # Create code with deeply nested binary operations that will + # trigger RecursionError during AST visiting + depth = 500 + code = "result = " + " + ".join(["1"] * depth) + "\n" + v.scan(code, filename="test_deep.py") + assert int(v.report()) == ExitCode.InvalidInput diff --git a/vulture/core.py b/vulture/core.py index 264bd538..a683f95d 100644 --- a/vulture/core.py +++ b/vulture/core.py @@ -260,6 +260,15 @@ def handle_syntax_error(e): self.visit(node) except SyntaxError as err: handle_syntax_error(err) + except RecursionError: + self._log( + f"{utils.format_path(filename)}: " + "RecursionError: maximum recursion depth exceeded while analyzing code. " + "This can happen with extremely nested expressions.", + file=sys.stderr, + force=True, + ) + self.exit_code = ExitCode.InvalidInput # Reset the reachability internals for every module to reduce memory # usage. @@ -362,7 +371,9 @@ def report( else item.get_report(add_size=sort_by_size), force=True, ) - self.exit_code = ExitCode.DeadCode + # Only set DeadCode if no error has occurred + if self.exit_code == ExitCode.NoDeadCode: + self.exit_code = ExitCode.DeadCode return self.exit_code @property From 9522664b1cc791f0d54c829694e203a229f5a67a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Oct 2025 05:34:23 +0000 Subject: [PATCH 3/3] Fix line length in error message for linter compliance Co-authored-by: jendrikseipp <213955+jendrikseipp@users.noreply.github.com> --- vulture/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vulture/core.py b/vulture/core.py index a683f95d..20f5f723 100644 --- a/vulture/core.py +++ b/vulture/core.py @@ -263,7 +263,8 @@ def handle_syntax_error(e): except RecursionError: self._log( f"{utils.format_path(filename)}: " - "RecursionError: maximum recursion depth exceeded while analyzing code. " + "RecursionError: maximum recursion depth exceeded " + "while analyzing code. " "This can happen with extremely nested expressions.", file=sys.stderr, force=True,