blob: 182c61a47395a10dd5a2236c0b2fcaa26e409861 [file] [edit]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Formats JSON files using standard indentation and sorted keys."""
import argparse
import json
import os
import shutil
import sys
import tempfile
from pathlib import Path
def _write_results(
results: list[dict[str, object]], output: Path | None
) -> None:
# The results include the full formatted contents of each unformatted file,
# which can exceed shac's limit on the size of a subprocess's stdout.
if output:
output.write_text(json.dumps(results), encoding="utf-8")
else:
print(json.dumps(results))
def main():
parser = argparse.ArgumentParser(description="Pretty-print JSON files.")
parser.add_argument(
"file",
type=Path,
nargs="+",
help="JSON file to format in-place.",
)
parser.add_argument(
"--sort-keys",
default=True,
action=argparse.BooleanOptionalAction,
dest="sort_keys",
help="Sort object keys.",
)
parser.add_argument(
"--quiet",
default=False,
action=argparse.BooleanOptionalAction,
dest="quiet",
help="Suppress output.",
)
parser.add_argument(
"--check",
default=False,
action="store_true",
help="Check files without modifying disk and print JSON results to stdout.",
)
parser.add_argument(
"--output",
type=Path,
help="With --check, write the JSON results to this file instead of stdout.",
)
args = parser.parse_args()
if args.check:
results = []
for json_path in args.file:
try:
original = json_path.read_text(encoding="utf-8")
data = json.loads(original)
except json.JSONDecodeError:
results.append({"filepath": str(json_path)})
continue
formatted = json.dumps(
data,
indent=4,
sort_keys=args.sort_keys,
separators=(",", ": "),
)
new_content = formatted + "\n"
if original != new_content:
results.append(
{
"filepath": str(json_path),
"replacements": [new_content],
}
)
_write_results(results, args.output)
return
for json_path in args.file:
try:
with open(json_path, "r", encoding="utf-8") as f:
original = f.read()
data = json.loads(original)
formatted = json.dumps(
data,
indent=4,
sort_keys=args.sort_keys,
separators=(",", ": "),
)
new_content = formatted + "\n"
if original != new_content:
temp_file = None
try:
with tempfile.NamedTemporaryFile(
"w",
encoding="utf-8",
dir=json_path.parent,
delete=False,
) as tf:
temp_file = tf.name
tf.write(new_content)
shutil.copymode(json_path, temp_file)
os.replace(temp_file, json_path)
finally:
if temp_file and os.path.exists(temp_file):
os.unlink(temp_file)
except json.JSONDecodeError as e:
if not args.quiet:
print(f"JSON decode error in {json_path}: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()