python-harness 0.0.5__tar.gz → 0.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_harness-0.0.5/python_harness.egg-info → python_harness-0.0.6}/PKG-INFO +1 -1
- {python_harness-0.0.5 → python_harness-0.0.6}/pyproject.toml +1 -1
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/cli.py +10 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/hard_evaluator.py +6 -2
- {python_harness-0.0.5 → python_harness-0.0.6/python_harness.egg-info}/PKG-INFO +1 -1
- python_harness-0.0.6/tests/test_hard_evaluator.py +95 -0
- python_harness-0.0.5/tests/test_hard_evaluator.py +0 -29
- {python_harness-0.0.5 → python_harness-0.0.6}/LICENSE +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/README.md +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/__init__.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/evaluator.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/qc_evaluator.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/soft_evaluator.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/SOURCES.txt +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/dependency_links.txt +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/entry_points.txt +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/requires.txt +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/top_level.txt +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/setup.cfg +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_cli.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_evaluator.py +0 -0
- {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_soft_evaluator.py +0 -0
|
@@ -130,6 +130,16 @@ def measure(path: str = typer.Argument(".", help="The path to evaluate")) -> Non
|
|
|
130
130
|
f" - {issue['file']}: {issue['type']} '{issue['name']}' "
|
|
131
131
|
f"has CC {issue['complexity']}"
|
|
132
132
|
)
|
|
133
|
+
|
|
134
|
+
# If radon failed for another reason
|
|
135
|
+
# (e.g. radon not installed or syntax error)
|
|
136
|
+
if not issues and hard_results["radon_cc"].get("error_message"):
|
|
137
|
+
err_msg = hard_results['radon_cc'].get('error_message')
|
|
138
|
+
console.print(f"[red]Radon CC Error:[/red] {err_msg}")
|
|
139
|
+
elif not issues:
|
|
140
|
+
console.print(
|
|
141
|
+
"[red]Radon CC failed but no specific issues were parsed.[/red]"
|
|
142
|
+
)
|
|
133
143
|
sys.exit(1)
|
|
134
144
|
|
|
135
145
|
console.print("[bold green]Hard Evaluation Passed![/bold green]")
|
|
@@ -123,14 +123,18 @@ class HardEvaluator:
|
|
|
123
123
|
"complexity": block.get('complexity')
|
|
124
124
|
})
|
|
125
125
|
|
|
126
|
-
if
|
|
126
|
+
if result.returncode != 0:
|
|
127
|
+
# E.g. syntax error in target code preventing radon from parsing
|
|
128
|
+
status = "failed"
|
|
129
|
+
elif issues:
|
|
127
130
|
status = "failed"
|
|
128
131
|
|
|
129
132
|
return {
|
|
130
133
|
"status": status,
|
|
131
134
|
"issues": issues,
|
|
132
135
|
"return_code": result.returncode,
|
|
133
|
-
"output": result.stdout
|
|
136
|
+
"output": result.stdout,
|
|
137
|
+
"error_message": result.stderr if result.returncode != 0 else ""
|
|
134
138
|
}
|
|
135
139
|
except Exception as e:
|
|
136
140
|
return {"status": "error", "error_message": str(e)}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for hard evaluation logic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from python_harness.hard_evaluator import HardEvaluator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_hard_evaluator_methods() -> None:
|
|
12
|
+
"""
|
|
13
|
+
Test methods of HardEvaluator.
|
|
14
|
+
"""
|
|
15
|
+
evaluator = HardEvaluator(".")
|
|
16
|
+
|
|
17
|
+
ruff_result = evaluator.run_ruff()
|
|
18
|
+
assert "status" in ruff_result
|
|
19
|
+
|
|
20
|
+
mypy_result = evaluator.run_mypy()
|
|
21
|
+
assert "status" in mypy_result
|
|
22
|
+
|
|
23
|
+
ty_result = evaluator.run_ty()
|
|
24
|
+
assert "status" in ty_result
|
|
25
|
+
|
|
26
|
+
# pytest_result = evaluator.run_pytest() # Causes infinite loop when run in test
|
|
27
|
+
# assert "status" in pytest_result
|
|
28
|
+
|
|
29
|
+
eval_result = evaluator.evaluate()
|
|
30
|
+
assert "ruff" in eval_result
|
|
31
|
+
assert "mypy" in eval_result
|
|
32
|
+
assert "ty" in eval_result
|
|
33
|
+
|
|
34
|
+
def test_ty_fallback_behavior(monkeypatch: Any) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Test that run_ty gracefully falls back to a warning when 'ty' is not installed.
|
|
37
|
+
"""
|
|
38
|
+
# Create a mock subprocess.run that always raises FileNotFoundError
|
|
39
|
+
# to simulate 'ty' not being on the system PATH
|
|
40
|
+
def mock_run(*args: Any, **kwargs: Any) -> Any:
|
|
41
|
+
raise FileNotFoundError("[Errno 2] No such file or directory: 'ty'")
|
|
42
|
+
|
|
43
|
+
monkeypatch.setattr("subprocess.run", mock_run)
|
|
44
|
+
|
|
45
|
+
evaluator = HardEvaluator(".")
|
|
46
|
+
result = evaluator.run_ty()
|
|
47
|
+
|
|
48
|
+
assert result["status"] == "warning"
|
|
49
|
+
assert "ty executable not found" in result["error_message"]
|
|
50
|
+
|
|
51
|
+
def test_ty_fallback_behavior_oserror(monkeypatch: Any) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Test that run_ty gracefully falls back to a warning when a generic Exception
|
|
54
|
+
containing the Errno 2 string is thrown.
|
|
55
|
+
"""
|
|
56
|
+
def mock_run(*args: Any, **kwargs: Any) -> Any:
|
|
57
|
+
raise Exception("[Errno 2] No such file or directory: 'ty'")
|
|
58
|
+
|
|
59
|
+
monkeypatch.setattr("subprocess.run", mock_run)
|
|
60
|
+
|
|
61
|
+
evaluator = HardEvaluator(".")
|
|
62
|
+
result = evaluator.run_ty()
|
|
63
|
+
|
|
64
|
+
assert result["status"] == "warning"
|
|
65
|
+
assert "ty executable not found" in result["error_message"]
|
|
66
|
+
|
|
67
|
+
def test_radon_cc_syntax_error(monkeypatch: Any, tmp_path: Path) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Test that run_radon_cc correctly captures and reports stderr when radon
|
|
70
|
+
fails (e.g. due to syntax errors in the target codebase).
|
|
71
|
+
"""
|
|
72
|
+
# Create a mock subprocess.run that simulates radon exiting with code 1
|
|
73
|
+
# and writing an error to stderr (which happens when there are syntax errors)
|
|
74
|
+
import subprocess
|
|
75
|
+
original_run = subprocess.run
|
|
76
|
+
|
|
77
|
+
def mock_run(args: Any, **kwargs: Any) -> Any:
|
|
78
|
+
if args and args[0] == "radon" and args[1] == "cc":
|
|
79
|
+
# Simulate radon failing on syntax error
|
|
80
|
+
class MockResult:
|
|
81
|
+
returncode = 1
|
|
82
|
+
stdout = ""
|
|
83
|
+
stderr = "ERROR: SyntaxError in bad.py"
|
|
84
|
+
return MockResult()
|
|
85
|
+
return original_run(args, **kwargs)
|
|
86
|
+
|
|
87
|
+
monkeypatch.setattr("subprocess.run", mock_run)
|
|
88
|
+
|
|
89
|
+
evaluator = HardEvaluator(str(tmp_path))
|
|
90
|
+
result = evaluator.run_radon_cc()
|
|
91
|
+
|
|
92
|
+
assert result["status"] == "failed"
|
|
93
|
+
assert len(result["issues"]) == 0
|
|
94
|
+
# Radon should output to stderr because of the syntax error
|
|
95
|
+
assert "SyntaxError" in result["error_message"] or result["return_code"] != 0
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for hard evaluation logic.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from python_harness.hard_evaluator import HardEvaluator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def test_hard_evaluator_methods() -> None:
|
|
9
|
-
"""
|
|
10
|
-
Test methods of HardEvaluator.
|
|
11
|
-
"""
|
|
12
|
-
evaluator = HardEvaluator(".")
|
|
13
|
-
|
|
14
|
-
ruff_result = evaluator.run_ruff()
|
|
15
|
-
assert "status" in ruff_result
|
|
16
|
-
|
|
17
|
-
mypy_result = evaluator.run_mypy()
|
|
18
|
-
assert "status" in mypy_result
|
|
19
|
-
|
|
20
|
-
ty_result = evaluator.run_ty()
|
|
21
|
-
assert "status" in ty_result
|
|
22
|
-
|
|
23
|
-
# pytest_result = evaluator.run_pytest() # Causes infinite loop when run in test
|
|
24
|
-
# assert "status" in pytest_result
|
|
25
|
-
|
|
26
|
-
eval_result = evaluator.evaluate()
|
|
27
|
-
assert "ruff" in eval_result
|
|
28
|
-
assert "mypy" in eval_result
|
|
29
|
-
assert "ty" in eval_result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|