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.
Files changed (22) hide show
  1. {python_harness-0.0.5/python_harness.egg-info → python_harness-0.0.6}/PKG-INFO +1 -1
  2. {python_harness-0.0.5 → python_harness-0.0.6}/pyproject.toml +1 -1
  3. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/cli.py +10 -0
  4. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/hard_evaluator.py +6 -2
  5. {python_harness-0.0.5 → python_harness-0.0.6/python_harness.egg-info}/PKG-INFO +1 -1
  6. python_harness-0.0.6/tests/test_hard_evaluator.py +95 -0
  7. python_harness-0.0.5/tests/test_hard_evaluator.py +0 -29
  8. {python_harness-0.0.5 → python_harness-0.0.6}/LICENSE +0 -0
  9. {python_harness-0.0.5 → python_harness-0.0.6}/README.md +0 -0
  10. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/__init__.py +0 -0
  11. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/evaluator.py +0 -0
  12. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/qc_evaluator.py +0 -0
  13. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness/soft_evaluator.py +0 -0
  14. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/SOURCES.txt +0 -0
  15. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/dependency_links.txt +0 -0
  16. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/entry_points.txt +0 -0
  17. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/requires.txt +0 -0
  18. {python_harness-0.0.5 → python_harness-0.0.6}/python_harness.egg-info/top_level.txt +0 -0
  19. {python_harness-0.0.5 → python_harness-0.0.6}/setup.cfg +0 -0
  20. {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_cli.py +0 -0
  21. {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_evaluator.py +0 -0
  22. {python_harness-0.0.5 → python_harness-0.0.6}/tests/test_soft_evaluator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-harness
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: An agentic codebase evaluation and evolution tool for Python projects.
5
5
  Author-email: Mingli Yuan <mingli.yuan@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-harness"
3
- version = "0.0.5"
3
+ version = "0.0.6"
4
4
  description = "An agentic codebase evaluation and evolution tool for Python projects."
5
5
  requires-python = ">=3.10"
6
6
  readme = "README.md"
@@ -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 issues:
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)}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-harness
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: An agentic codebase evaluation and evolution tool for Python projects.
5
5
  Author-email: Mingli Yuan <mingli.yuan@gmail.com>
6
6
  License: MIT
@@ -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