Skip to content

Commit 1690d4f

Browse files
committed
Add PackSlice JSON CLI output
1 parent 213f5e1 commit 1690d4f

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

projects/packslice/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ packslice split examples/sample_pack examples/split_demo --group-by signature --
3636
packslice summarize examples/split_demo
3737
packslice markdown examples/split_demo examples/split_demo/REPORT.md
3838
packslice split examples/sample_pack examples/failure_only --group-by signature --failure-only --chronological
39+
packslice summarize examples/split_demo --json
3940
```
4041

4142
## Example output
@@ -48,6 +49,12 @@ Splits:
4849
- train: 2 cases
4950
- eval: 2 cases
5051
- test: 2 cases
52+
53+
$ packslice summarize examples/split_demo --json
54+
{
55+
"group_by": "signature",
56+
"total_cases": 6
57+
}
5158
```
5259

5360
## CLI
@@ -56,9 +63,12 @@ Splits:
5663
packslice split path/to/tracepack path/to/output_dir --group-by signature --include-label status:failure
5764
packslice split path/to/tracepack path/to/output_dir --group-by signature --chronological --order-by source_path
5865
packslice summarize path/to/output_dir
66+
packslice split path/to/tracepack path/to/output_dir --json
5967
packslice markdown path/to/output_dir path/to/report.md
6068
```
6169

70+
Both `split` and `summarize` support `--json` for CI pipelines and scripted dataset workflows.
71+
6272
## Output layout
6373

6474
```text

projects/packslice/src/packslice/cli.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import argparse
4+
import json
45
from pathlib import Path
56
import sys
67

@@ -15,6 +16,14 @@ def _positive_int(value: str) -> int:
1516
return parsed
1617

1718

19+
def _print_json(data: dict[str, object]) -> None:
20+
print(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False))
21+
22+
23+
def _add_json_flag(parser: argparse.ArgumentParser) -> None:
24+
parser.add_argument("--json", action="store_true", help="print machine-readable JSON output")
25+
26+
1827
def _cmd_split(args: argparse.Namespace) -> int:
1928
if args.success_only and args.failure_only:
2029
raise SystemExit("--success-only and --failure-only cannot be used together")
@@ -28,13 +37,19 @@ def _cmd_split(args: argparse.Namespace) -> int:
2837
chronological=args.chronological,
2938
order_by=args.order_by,
3039
)
40+
if args.json:
41+
_print_json(summary)
42+
return 0
3143
print(f"Wrote split pack to {args.output}")
3244
print(summarize_splits(summary))
3345
return 0
3446

3547

3648
def _cmd_summarize(args: argparse.Namespace) -> int:
3749
payload = load_summary(args.path)
50+
if args.json:
51+
_print_json(payload)
52+
return 0
3853
print(summarize_splits(payload))
3954
return 0
4055

@@ -62,10 +77,12 @@ def build_parser() -> argparse.ArgumentParser:
6277
split.add_argument("--train-ratio", type=_positive_int, default=70)
6378
split.add_argument("--eval-ratio", type=_positive_int, default=15)
6479
split.add_argument("--test-ratio", type=_positive_int, default=15)
80+
_add_json_flag(split)
6581
split.set_defaults(func=_cmd_split)
6682

6783
summarize = subparsers.add_parser("summarize", help="summarize an existing split output")
6884
summarize.add_argument("path")
85+
_add_json_flag(summarize)
6986
summarize.set_defaults(func=_cmd_summarize)
7087

7188
markdown = subparsers.add_parser("markdown", help="render a markdown report for an existing split output")

projects/packslice/tests/test_packslice.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
import json
44
import tempfile
55
import unittest
6+
from contextlib import redirect_stdout
7+
from io import StringIO
68
from pathlib import Path
79

10+
from packslice.cli import main as cli_main
811
from packslice.report import markdown_splits, summarize_splits
912
from packslice.splitter import split_pack
1013

@@ -138,6 +141,35 @@ def test_chronological_split_keeps_ordered_groups(self):
138141
["assert-b", "billing-b"],
139142
)
140143

144+
def test_split_cli_can_emit_json(self):
145+
with tempfile.TemporaryDirectory() as tmpdir:
146+
root = Path(tmpdir) / "sample_pack"
147+
root.mkdir()
148+
self._write_pack(root)
149+
out = Path(tmpdir) / "splits"
150+
output = StringIO()
151+
with redirect_stdout(output):
152+
code = cli_main(["split", str(root), str(out), "--group-by", "signature", "--json"])
153+
payload = json.loads(output.getvalue())
154+
self.assertEqual(code, 0)
155+
self.assertEqual(payload["total_cases"], 6)
156+
self.assertEqual(payload["splits"][0]["name"], "train")
157+
158+
def test_summarize_cli_can_emit_json(self):
159+
with tempfile.TemporaryDirectory() as tmpdir:
160+
root = Path(tmpdir) / "sample_pack"
161+
root.mkdir()
162+
self._write_pack(root)
163+
out = Path(tmpdir) / "splits"
164+
split_pack(root, out, ratios=(2, 1, 1))
165+
output = StringIO()
166+
with redirect_stdout(output):
167+
code = cli_main(["summarize", str(out), "--json"])
168+
payload = json.loads(output.getvalue())
169+
self.assertEqual(code, 0)
170+
self.assertEqual(payload["group_by"], "signature")
171+
self.assertEqual(payload["total_cases"], 6)
172+
141173

142174
if __name__ == "__main__":
143175
unittest.main()

0 commit comments

Comments
 (0)