Skip to content

Commit adfe418

Browse files
feat(goal): configuration management system
changes: - file: output_formatters.py area: cli added: [_toon_row, _print_toon_unsupported_section, _ordered_unsupported_items] modified: [output_batch_toon, _print_toon_file_section] removed: [_format_toon_issue, _format_unsupported_summary] - file: test_batch_toon_output.py area: test modified: [test_output_batch_toon_is_compact_and_groups_sections] dependencies: flow: "test_batch_toon_output→output_formatters" - test_batch_toon_output.py -> output_formatters.py stats: lines: "+228/-166 (net +62)" files: 3 complexity: "+120% complexity (monitor)"
1 parent d30be4d commit adfe418

File tree

7 files changed

+239
-169
lines changed

7 files changed

+239
-169
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
124124
- **Tree-sitter for all** — syntax validation for 165+ languages
125125
- **Example 07** — comprehensive multi-language demo with 8 languages
126126

127+
## [0.1.67] - 2026-03-26
128+
129+
### Test
130+
- Update tests/test_batch_toon_output.py
131+
132+
### Other
133+
- Update project/validation.toon.yaml
134+
127135
## [0.1.66] - 2026-03-26
128136

129137
### Docs

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.66
1+
0.1.67

project/validation.toon.yaml

Lines changed: 172 additions & 122 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "vallm"
7-
version = "0.1.66"
7+
version = "0.1.67"
88
description = "A complete toolkit for validating LLM-generated code"
99
readme = "README.md"
1010
license = "Apache-2.0"

src/vallm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
"detect_language",
1717
]
1818

19-
__version__ = "0.1.66"
19+
__version__ = "0.1.67"

src/vallm/cli/output_formatters.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
import csv
6+
import io
57
import json
68
from collections import Counter
79
from datetime import date
@@ -16,12 +18,6 @@
1618
from vallm.scoring import PipelineResult, ValidationResult
1719

1820
console = Console()
19-
20-
TOON_ISSUE_LABELS = {
21-
"error": "[err]",
22-
"warning": "[warn]",
23-
"info": "[info]",
24-
}
2521
TOON_UNSUPPORTED_ORDER = ("*.md", "Dockerfile*", "*.txt", "*.yml", "*.example", "other")
2622

2723

@@ -246,6 +242,13 @@ def _toon_today() -> str:
246242
return date.today().isoformat()
247243

248244

245+
def _toon_row(values: list[object]) -> str:
246+
buffer = io.StringIO()
247+
writer = csv.writer(buffer)
248+
writer.writerow(["" if value is None else value for value in values])
249+
return buffer.getvalue().rstrip("\r\n")
250+
251+
249252
def _split_toon_results(files_details: list[dict]) -> tuple[list[dict], list[dict]]:
250253
warnings = []
251254
errors = []
@@ -263,17 +266,17 @@ def _split_toon_results(files_details: list[dict]) -> tuple[list[dict], list[dic
263266
return warnings, errors
264267

265268

266-
def _format_toon_issue(issue: dict) -> str:
267-
severity = issue.get("severity", "info")
268-
label = TOON_ISSUE_LABELS.get(severity, f"[{severity}]")
269-
location = ""
270-
line = issue.get("line")
271-
column = issue.get("column")
272-
if line is not None:
273-
location = f"@{line}"
274-
if column is not None:
275-
location += f":{column}"
276-
return f" {label} {issue.get('rule', 'unknown')}: {issue.get('message', '')}{location}"
269+
def _ordered_unsupported_items(unsupported_counts: Counter[str]) -> list[tuple[str, int]]:
270+
ordered: list[tuple[str, int]] = []
271+
for bucket in TOON_UNSUPPORTED_ORDER:
272+
count = unsupported_counts.get(bucket)
273+
if count:
274+
ordered.append((bucket, count))
275+
276+
for bucket in sorted(bucket for bucket in unsupported_counts if bucket not in TOON_UNSUPPORTED_ORDER):
277+
ordered.append((bucket, unsupported_counts[bucket]))
278+
279+
return ordered
277280

278281

279282
def _unsupported_bucket(file_path: str) -> str:
@@ -300,25 +303,30 @@ def _build_unsupported_summary(failed_files: list) -> Counter[str]:
300303
return unsupported
301304

302305

303-
def _format_unsupported_summary(unsupported_counts: Counter[str]) -> str:
304-
ordered: list[str] = []
305-
for bucket in TOON_UNSUPPORTED_ORDER:
306-
if unsupported_counts.get(bucket):
307-
ordered.append(f"{bucket} ({unsupported_counts[bucket]})")
308-
309-
for bucket in sorted(bucket for bucket in unsupported_counts if bucket not in TOON_UNSUPPORTED_ORDER):
310-
ordered.append(f"{bucket} ({unsupported_counts[bucket]})")
311-
312-
return " ".join(ordered)
313-
314-
315306
def _print_toon_file_section(title: str, files: list[dict]) -> None:
316-
print(f"{title}[{len(files)}]:")
317-
width = min(max(len(file_data["path"]) for file_data in files), 48)
307+
print(f"{title}[{len(files)}]{{path,score}}:")
318308
for file_data in files:
319-
print(f" {file_data['path']:<{width}} {file_data['score']:.2f}")
320-
for issue in file_data["issues"]:
321-
print(_format_toon_issue(issue))
309+
score = f"{file_data['score']:.2f}"
310+
print(f" {_toon_row([file_data['path'], score])}")
311+
issues = file_data["issues"]
312+
if issues:
313+
print(f" issues[{len(issues)}]{{rule,severity,message,line}}:")
314+
for issue in issues:
315+
print(
316+
f" {_toon_row([
317+
issue.get('rule', 'unknown'),
318+
issue.get('severity', 'info'),
319+
issue.get('message', ''),
320+
issue.get('line'),
321+
])}"
322+
)
323+
324+
325+
def _print_toon_unsupported_section(unsupported_counts: Counter[str]) -> None:
326+
ordered = _ordered_unsupported_items(unsupported_counts)
327+
print(f"UNSUPPORTED[{len(ordered)}]{{bucket,count}}:")
328+
for bucket, count in ordered:
329+
print(f" {_toon_row([bucket, count])}")
322330

323331

324332
def output_batch_json(
@@ -435,8 +443,7 @@ def output_batch_toon(
435443
_print_toon_file_section("ERRORS", errors)
436444
print()
437445
if unsupported_count:
438-
print(f"UNSUPPORTED[{unsupported_count}]:")
439-
print(f" {_format_unsupported_summary(unsupported_counts)}")
446+
_print_toon_unsupported_section(unsupported_counts)
440447

441448

442449
def print_summary_header() -> None:

tests/test_batch_toon_output.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,19 @@ def test_output_batch_toon_is_compact_and_groups_sections(capsys, monkeypatch):
9797
assert "# vallm batch | 8f | 2✓ 1⚠ 1✗ | 2026-03-26" in output
9898
assert "SUMMARY:" in output
9999
assert "scanned: 8 passed: 2 (25.0%) warnings: 1 errors: 1 unsupported: 5" in output
100-
assert "WARNINGS[1]:" in output
101-
assert "src/warn.py" in output
100+
assert "WARNINGS[1]{path,score}:" in output
101+
assert "src/warn.py,0.97" in output
102102
assert "src/pass.py" not in output
103-
assert "[warn] complexity.cyclomatic" in output
104-
assert "ERRORS[1]:" in output
105-
assert "src/fail.py" in output
106-
assert "[err] python.import.resolvable" in output
107-
assert "UNSUPPORTED[5]:" in output
108-
assert "*.md (1) Dockerfile* (1) *.txt (1) *.example (1) other (1)" in output
103+
assert "issues[1]{rule,severity,message,line}:" in output
104+
assert "complexity.cyclomatic,warning,validate_code CC=22 (max:15),185" in output
105+
assert "ERRORS[1]{path,score}:" in output
106+
assert "src/fail.py,0.91" in output
107+
assert "python.import.resolvable,error,Module 'missing.module' not found,12" in output
108+
assert "UNSUPPORTED[5]{bucket,count}:" in output
109+
assert "*.md,1" in output
110+
assert "Dockerfile*,1" in output
111+
assert "*.txt,1" in output
112+
assert "*.example,1" in output
113+
assert "other,1" in output
109114
assert "FILES:" not in output
110115
assert "FAILED:" not in output

0 commit comments

Comments
 (0)