Skip to content

Commit 34fc7c3

Browse files
authored
Merge pull request #135 from FSoft-AI4Code/fix/parse-timeout-swallowed
fix: stop analyzers from swallowing the per-file parse timeout
2 parents 12474de + 1a7d690 commit 34fc7c3

2 files changed

Lines changed: 50 additions & 2 deletions

File tree

‎codewiki/src/be/dependency_analyzer/analysis/call_graph_analyzer.py‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,15 @@
2828
logger = logging.getLogger(__name__)
2929

3030

31-
class TimeoutError(Exception):
32-
"""Raised when file parsing exceeds timeout."""
31+
class TimeoutError(BaseException):
32+
"""Raised when file parsing exceeds timeout.
33+
34+
A ``BaseException`` on purpose: the language analyzers wrap many small
35+
steps in ``except Exception`` blocks. If the alarm fired inside one of
36+
them, the error was swallowed, the alarm was already spent, and a slow
37+
file (e.g. a multi-megabyte vendored JS bundle) ran forever. Only
38+
``_analyze_code_file`` catches it.
39+
"""
3340

3441

3542
@contextmanager

‎tests/test_parse_timeout.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""The per-file parse timeout must not be swallowed by an analyzer's own
2+
``except Exception`` blocks."""
3+
4+
import signal
5+
import time
6+
7+
import pytest
8+
9+
from codewiki.src.be.dependency_analyzer.analysis import call_graph_analyzer
10+
from codewiki.src.be.dependency_analyzer.analysis.call_graph_analyzer import (
11+
CallGraphAnalyzer,
12+
)
13+
14+
pytestmark = pytest.mark.skipif(
15+
not hasattr(signal, "SIGALRM"), reason="the timeout relies on SIGALRM"
16+
)
17+
18+
19+
def _swallowing_analyzer(self, file_path, content, repo_dir):
20+
# Mimics a language analyzer that guards each small step with
21+
# ``except Exception`` and loops for a very long time.
22+
deadline = time.monotonic() + 30
23+
while time.monotonic() < deadline:
24+
try:
25+
time.sleep(0.01)
26+
except Exception: # noqa: BLE001
27+
pass
28+
29+
30+
def test_timeout_escapes_except_exception(tmp_path, monkeypatch):
31+
(tmp_path / "slow.js").write_text("f();\n", encoding="utf-8")
32+
monkeypatch.setattr(CallGraphAnalyzer, "_analyze_javascript_file", _swallowing_analyzer)
33+
34+
real_timeout = call_graph_analyzer.timeout
35+
monkeypatch.setattr(call_graph_analyzer, "timeout", lambda seconds: real_timeout(1))
36+
37+
started = time.monotonic()
38+
CallGraphAnalyzer()._analyze_code_file(
39+
str(tmp_path), {"path": "slow.js", "language": "javascript"}
40+
)
41+
assert time.monotonic() - started < 10

0 commit comments

Comments
 (0)