llm-code-validator 0.1.0__py3-none-any.whl
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.
- llm_code_validator/__init__.py +3 -0
- llm_code_validator/benchmark.py +141 -0
- llm_code_validator/cli.py +105 -0
- llm_code_validator/core.py +359 -0
- llm_code_validator/diagnostics.py +61 -0
- llm_code_validator/fixes.py +66 -0
- llm_code_validator/formatting.py +43 -0
- llm_code_validator/library_signatures.json +842 -0
- llm_code_validator/signatures.py +163 -0
- llm_code_validator/versioning.py +153 -0
- llm_code_validator-0.1.0.dist-info/METADATA +220 -0
- llm_code_validator-0.1.0.dist-info/RECORD +16 -0
- llm_code_validator-0.1.0.dist-info/WHEEL +5 -0
- llm_code_validator-0.1.0.dist-info/entry_points.txt +2 -0
- llm_code_validator-0.1.0.dist-info/licenses/LICENSE +21 -0
- llm_code_validator-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .core import check_file
|
|
7
|
+
from .diagnostics import Diagnostic
|
|
8
|
+
from .versioning import VersionContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class FixResult:
|
|
13
|
+
path: str
|
|
14
|
+
changed: bool
|
|
15
|
+
previews: list[str]
|
|
16
|
+
skipped: list[str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _line_replacement(line: str, diagnostic: Diagnostic) -> str | None:
|
|
20
|
+
if diagnostic.fix.safety != "safe_fix" or not diagnostic.replacement:
|
|
21
|
+
return None
|
|
22
|
+
if line.lstrip().startswith("from ") and diagnostic.replacement.startswith("from "):
|
|
23
|
+
indent = line[: len(line) - len(line.lstrip())]
|
|
24
|
+
return indent + diagnostic.replacement
|
|
25
|
+
if diagnostic.symbol in line:
|
|
26
|
+
return line.replace(diagnostic.symbol, diagnostic.replacement, 1)
|
|
27
|
+
old_token = diagnostic.symbol.split(".")[-1]
|
|
28
|
+
if old_token not in line:
|
|
29
|
+
return None
|
|
30
|
+
return line.replace(old_token, diagnostic.replacement, 1)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fix_file(path: str | Path, write: bool = False, version_context: VersionContext | None = None) -> FixResult:
|
|
34
|
+
file_path = Path(path)
|
|
35
|
+
source = file_path.read_text(encoding="utf-8")
|
|
36
|
+
keep_final_newline = source.endswith("\n")
|
|
37
|
+
lines = source.splitlines()
|
|
38
|
+
result = check_file(file_path, version_context)
|
|
39
|
+
previews: list[str] = []
|
|
40
|
+
skipped: list[str] = []
|
|
41
|
+
changed = False
|
|
42
|
+
|
|
43
|
+
for diagnostic in result.diagnostics:
|
|
44
|
+
if diagnostic.line < 1 or diagnostic.line > len(lines):
|
|
45
|
+
skipped.append(f"{diagnostic.path}:{diagnostic.line} {diagnostic.code} cannot locate line")
|
|
46
|
+
continue
|
|
47
|
+
old_line = lines[diagnostic.line - 1]
|
|
48
|
+
new_line = _line_replacement(old_line, diagnostic)
|
|
49
|
+
if new_line is None:
|
|
50
|
+
skipped.append(
|
|
51
|
+
f"{diagnostic.path}:{diagnostic.line} {diagnostic.code} skipped "
|
|
52
|
+
f"({diagnostic.fix.safety})"
|
|
53
|
+
)
|
|
54
|
+
continue
|
|
55
|
+
previews.append(f"{diagnostic.path}:{diagnostic.line}\n old: {old_line}\n new: {new_line}")
|
|
56
|
+
if write:
|
|
57
|
+
lines[diagnostic.line - 1] = new_line
|
|
58
|
+
changed = True
|
|
59
|
+
|
|
60
|
+
if write and changed:
|
|
61
|
+
updated = "\n".join(lines)
|
|
62
|
+
if keep_final_newline:
|
|
63
|
+
updated += "\n"
|
|
64
|
+
file_path.write_text(updated, encoding="utf-8")
|
|
65
|
+
|
|
66
|
+
return FixResult(str(file_path), changed, previews, skipped)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from .diagnostics import CheckResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_text(result: CheckResult) -> str:
|
|
9
|
+
lines: list[str] = []
|
|
10
|
+
for diagnostic in result.diagnostics:
|
|
11
|
+
qualified_symbol = (
|
|
12
|
+
diagnostic.symbol
|
|
13
|
+
if diagnostic.symbol.startswith(f"{diagnostic.library}.")
|
|
14
|
+
else f"{diagnostic.library}.{diagnostic.symbol}"
|
|
15
|
+
)
|
|
16
|
+
lines.append(
|
|
17
|
+
f"{diagnostic.path}:{diagnostic.line} {diagnostic.code} "
|
|
18
|
+
f"{diagnostic.severity} {qualified_symbol} {diagnostic.message}"
|
|
19
|
+
)
|
|
20
|
+
if diagnostic.version_assumption:
|
|
21
|
+
lines.append(f" version: {diagnostic.version_assumption}")
|
|
22
|
+
if diagnostic.replacement:
|
|
23
|
+
lines.append(f" fix: {diagnostic.replacement}")
|
|
24
|
+
if not result.diagnostics:
|
|
25
|
+
lines.append(f"OK: checked {result.checked_files} file(s), no diagnostics")
|
|
26
|
+
for warning in result.warnings:
|
|
27
|
+
lines.append(f"warning: {warning}")
|
|
28
|
+
return "\n".join(lines)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def format_json(result: CheckResult) -> str:
|
|
32
|
+
return json.dumps(result.to_dict(), indent=2, sort_keys=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def format_github(result: CheckResult) -> str:
|
|
36
|
+
lines: list[str] = []
|
|
37
|
+
for diagnostic in result.diagnostics:
|
|
38
|
+
message = diagnostic.message.replace("\n", " ")
|
|
39
|
+
lines.append(
|
|
40
|
+
f"::{diagnostic.severity} file={diagnostic.path},line={diagnostic.line},"
|
|
41
|
+
f"col={diagnostic.column},title={diagnostic.code}::{message}"
|
|
42
|
+
)
|
|
43
|
+
return "\n".join(lines)
|