#!/usr/bin/env python3 from __future__ import annotations import argparse import json import sys from pathlib import Path from typing import Any, TextIO def format_json(text: str, *, indent: int = 2, sort_keys: bool = False) -> str: """Parse JSON text and return a pretty-printed version.""" data: Any = json.loads(text) return json.dumps(data, indent=indent, sort_keys=sort_keys, ensure_ascii=False) + "\n" def read_input(path: str | None, stdin: TextIO) -> str: # Missing path or "-" means stdin, matching common command-line tools. if path is None or path == "-": return stdin.read() return Path(path).read_text(encoding="utf-8") def run_tests() -> None: assert format_json('{"b":2,"a":[1,true,null]}') == ( '{\n' ' "b": 2,\n' ' "a": [\n' ' 1,\n' ' true,\n' ' null\n' ' ]\n' '}\n' ) assert format_json('{"b":2,"a":1}', sort_keys=True) == ( '{\n' ' "a": 1,\n' ' "b": 2\n' '}\n' ) def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Pretty-print JSON from a file or stdin.") parser.add_argument("path", nargs="?", help="JSON file to read, or '-' for stdin") parser.add_argument("-i", "--indent", type=int, default=2, help="spaces per indent level") parser.add_argument("-s", "--sort-keys", action="store_true", help="sort object keys") parser.add_argument("--test", action="store_true", help="run basic self-tests") return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) if args.test: run_tests() print("tests passed", file=sys.stderr) return 0 try: text = read_input(args.path, sys.stdin) sys.stdout.write(format_json(text, indent=args.indent, sort_keys=args.sort_keys)) except (OSError, json.JSONDecodeError, ValueError) as exc: print(f"error: {exc}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())