#!/usr/bin/env python3 """Local-only CSV-to-JSON sample. Python 3.9+; standard library only. Copyright 2026 Automaton. Licensed under the MIT License; see csv-sample-license.txt. """ import argparse import csv import io import json import sys MAX_BYTES = 10_000_000 def convert(path): with open(path, "rb") as stream: raw = stream.read(MAX_BYTES + 1) if len(raw) > MAX_BYTES: raise ValueError("input exceeds 10 MB (10,000,000 bytes)") text = raw.decode("utf-8-sig") if "\x00" in text: raise ValueError("NUL characters are not supported") csv.field_size_limit(MAX_BYTES) reader = csv.reader(io.StringIO(text, newline=""), strict=True) headers = next(reader, None) if not headers or len(headers) > 20: raise ValueError("require a header with 1 to 20 columns") if any(not name.strip() for name in headers) or len(set(headers)) != len(headers): raise ValueError("headers must be nonblank and unique") rows = [] for row in reader: if not row: # Empty physical records are deliberately ignored. continue if len(row) != len(headers): raise ValueError(f"record ending on line {reader.line_num} has the wrong column count") rows.append(dict(zip(headers, row))) return rows def main(): parser = argparse.ArgumentParser(description="Convert a UTF-8 CSV to JSON; keep every value a string.") parser.add_argument("input", help="CSV file, at most 10 MB; output goes to stdout") args = parser.parse_args() try: output = json.dumps(convert(args.input), ensure_ascii=False, indent=2) except (OSError, UnicodeError, csv.Error, ValueError) as error: print(f"error: {error}", file=sys.stderr) return 1 sys.stdout.buffer.write((output + "\n").encode("utf-8")) return 0 if __name__ == "__main__": raise SystemExit(main())