Skip to content

Commit 37f4735

Browse files
Lukas Geigerclaude
andcommitted
fix: gate linter module detection on returncode == 0
subprocess.run does not raise on non-zero exit, so python -m flake8 on a system without flake8 exits 1 silently, wrongly setting has_flake8 = True and making the AST fallback unreachable. Now checks proc.returncode before setting the flag. Added 3 unit tests with mocked subprocess to verify detection logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e2ddfc3 commit 37f4735

2 files changed

Lines changed: 58 additions & 15 deletions

File tree

PythonBox_v8.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,26 +1238,26 @@ def _check_available_linters(self):
12381238
"""Prüft welche Linter verfügbar sind"""
12391239
self.has_pylint = shutil.which("pylint") is not None
12401240
self.has_flake8 = shutil.which("flake8") is not None
1241+
self._flake8_via_module = False
12411242
if not self.has_flake8:
12421243
try:
1243-
subprocess.run([sys.executable, "-m", "flake8", "--version"],
1244-
capture_output=True, timeout=5)
1245-
self.has_flake8 = True
1246-
self._flake8_via_module = True
1244+
proc = subprocess.run([sys.executable, "-m", "flake8", "--version"],
1245+
capture_output=True, timeout=5)
1246+
if proc.returncode == 0:
1247+
self.has_flake8 = True
1248+
self._flake8_via_module = True
12471249
except Exception:
1248-
self._flake8_via_module = False
1249-
else:
1250-
self._flake8_via_module = False
1250+
pass
1251+
self._pylint_via_module = False
12511252
if not self.has_pylint:
12521253
try:
1253-
subprocess.run([sys.executable, "-m", "pylint", "--version"],
1254-
capture_output=True, timeout=5)
1255-
self.has_pylint = True
1256-
self._pylint_via_module = True
1254+
proc = subprocess.run([sys.executable, "-m", "pylint", "--version"],
1255+
capture_output=True, timeout=5)
1256+
if proc.returncode == 0:
1257+
self.has_pylint = True
1258+
self._pylint_via_module = True
12571259
except Exception:
1258-
self._pylint_via_module = False
1259-
else:
1260-
self._pylint_via_module = False
1260+
pass
12611261

12621262
def run_linter(self, code: str, file_path: Optional[str] = None) -> List[Dict]:
12631263
"""Führt Linter aus und gibt Ergebnisse zurück"""

tests/test_cli_lint.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
import tempfile
55
import unittest
66
from pathlib import Path
7+
from unittest.mock import patch, MagicMock
78

89
ROOT = Path(__file__).resolve().parents[1]
910
SCRIPT = ROOT / "PythonBox_v8.py"
1011
sys.path.insert(0, str(ROOT))
11-
from PythonBox_v8 import parse_cli_args
12+
from PythonBox_v8 import parse_cli_args, LinterRunner
1213

1314

1415
def run_lint(target: str, timeout: int = 30) -> subprocess.CompletedProcess:
@@ -128,5 +129,47 @@ def test_lint_with_real_flake8(self):
128129
os.unlink(path)
129130

130131

132+
class TestLinterDetection(unittest.TestCase):
133+
134+
@patch("shutil.which", return_value=None)
135+
@patch("subprocess.run")
136+
def test_missing_flake8_not_detected(self, mock_run, mock_which):
137+
mock_result = MagicMock()
138+
mock_result.returncode = 1
139+
mock_run.return_value = mock_result
140+
runner = LinterRunner.__new__(LinterRunner)
141+
runner.has_flake8 = False
142+
runner.has_pylint = False
143+
runner._check_available_linters()
144+
self.assertFalse(runner.has_flake8)
145+
self.assertFalse(getattr(runner, '_flake8_via_module', False))
146+
147+
@patch("shutil.which", return_value=None)
148+
@patch("subprocess.run")
149+
def test_missing_pylint_not_detected(self, mock_run, mock_which):
150+
mock_result = MagicMock()
151+
mock_result.returncode = 1
152+
mock_run.return_value = mock_result
153+
runner = LinterRunner.__new__(LinterRunner)
154+
runner.has_flake8 = False
155+
runner.has_pylint = False
156+
runner._check_available_linters()
157+
self.assertFalse(runner.has_pylint)
158+
self.assertFalse(getattr(runner, '_pylint_via_module', False))
159+
160+
@patch("shutil.which", return_value=None)
161+
@patch("subprocess.run")
162+
def test_module_flake8_detected_on_success(self, mock_run, mock_which):
163+
mock_result = MagicMock()
164+
mock_result.returncode = 0
165+
mock_run.return_value = mock_result
166+
runner = LinterRunner.__new__(LinterRunner)
167+
runner.has_flake8 = False
168+
runner.has_pylint = False
169+
runner._check_available_linters()
170+
self.assertTrue(runner.has_flake8)
171+
self.assertTrue(runner._flake8_via_module)
172+
173+
131174
if __name__ == "__main__":
132175
unittest.main()

0 commit comments

Comments
 (0)