140 lines
4.9 KiB
Python
140 lines
4.9 KiB
Python
|
from pathlib import Path
|
||
|
from json import loads, dumps
|
||
|
from typing import Any, Callable, Optional, Union
|
||
|
|
||
|
from .text import Text
|
||
|
from .highlighter import JSONHighlighter, NullHighlighter
|
||
|
|
||
|
|
||
|
class JSON:
|
||
|
"""A renderable which pretty prints JSON.
|
||
|
|
||
|
Args:
|
||
|
json (str): JSON encoded data.
|
||
|
indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.
|
||
|
highlight (bool, optional): Enable highlighting. Defaults to True.
|
||
|
skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
|
||
|
ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
|
||
|
check_circular (bool, optional): Check for circular references. Defaults to True.
|
||
|
allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
|
||
|
default (Callable, optional): A callable that converts values that can not be encoded
|
||
|
in to something that can be JSON encoded. Defaults to None.
|
||
|
sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
|
||
|
"""
|
||
|
|
||
|
def __init__(
|
||
|
self,
|
||
|
json: str,
|
||
|
indent: Union[None, int, str] = 2,
|
||
|
highlight: bool = True,
|
||
|
skip_keys: bool = False,
|
||
|
ensure_ascii: bool = False,
|
||
|
check_circular: bool = True,
|
||
|
allow_nan: bool = True,
|
||
|
default: Optional[Callable[[Any], Any]] = None,
|
||
|
sort_keys: bool = False,
|
||
|
) -> None:
|
||
|
data = loads(json)
|
||
|
json = dumps(
|
||
|
data,
|
||
|
indent=indent,
|
||
|
skipkeys=skip_keys,
|
||
|
ensure_ascii=ensure_ascii,
|
||
|
check_circular=check_circular,
|
||
|
allow_nan=allow_nan,
|
||
|
default=default,
|
||
|
sort_keys=sort_keys,
|
||
|
)
|
||
|
highlighter = JSONHighlighter() if highlight else NullHighlighter()
|
||
|
self.text = highlighter(json)
|
||
|
self.text.no_wrap = True
|
||
|
self.text.overflow = None
|
||
|
|
||
|
@classmethod
|
||
|
def from_data(
|
||
|
cls,
|
||
|
data: Any,
|
||
|
indent: Union[None, int, str] = 2,
|
||
|
highlight: bool = True,
|
||
|
skip_keys: bool = False,
|
||
|
ensure_ascii: bool = False,
|
||
|
check_circular: bool = True,
|
||
|
allow_nan: bool = True,
|
||
|
default: Optional[Callable[[Any], Any]] = None,
|
||
|
sort_keys: bool = False,
|
||
|
) -> "JSON":
|
||
|
"""Encodes a JSON object from arbitrary data.
|
||
|
|
||
|
Args:
|
||
|
data (Any): An object that may be encoded in to JSON
|
||
|
indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.
|
||
|
highlight (bool, optional): Enable highlighting. Defaults to True.
|
||
|
default (Callable, optional): Optional callable which will be called for objects that cannot be serialized. Defaults to None.
|
||
|
skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
|
||
|
ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
|
||
|
check_circular (bool, optional): Check for circular references. Defaults to True.
|
||
|
allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
|
||
|
default (Callable, optional): A callable that converts values that can not be encoded
|
||
|
in to something that can be JSON encoded. Defaults to None.
|
||
|
sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
|
||
|
|
||
|
Returns:
|
||
|
JSON: New JSON object from the given data.
|
||
|
"""
|
||
|
json_instance: "JSON" = cls.__new__(cls)
|
||
|
json = dumps(
|
||
|
data,
|
||
|
indent=indent,
|
||
|
skipkeys=skip_keys,
|
||
|
ensure_ascii=ensure_ascii,
|
||
|
check_circular=check_circular,
|
||
|
allow_nan=allow_nan,
|
||
|
default=default,
|
||
|
sort_keys=sort_keys,
|
||
|
)
|
||
|
highlighter = JSONHighlighter() if highlight else NullHighlighter()
|
||
|
json_instance.text = highlighter(json)
|
||
|
json_instance.text.no_wrap = True
|
||
|
json_instance.text.overflow = None
|
||
|
return json_instance
|
||
|
|
||
|
def __rich__(self) -> Text:
|
||
|
return self.text
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
import argparse
|
||
|
import sys
|
||
|
|
||
|
parser = argparse.ArgumentParser(description="Pretty print json")
|
||
|
parser.add_argument(
|
||
|
"path",
|
||
|
metavar="PATH",
|
||
|
help="path to file, or - for stdin",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-i",
|
||
|
"--indent",
|
||
|
metavar="SPACES",
|
||
|
type=int,
|
||
|
help="Number of spaces in an indent",
|
||
|
default=2,
|
||
|
)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
from rich.console import Console
|
||
|
|
||
|
console = Console()
|
||
|
error_console = Console(stderr=True)
|
||
|
|
||
|
try:
|
||
|
if args.path == "-":
|
||
|
json_data = sys.stdin.read()
|
||
|
else:
|
||
|
json_data = Path(args.path).read_text()
|
||
|
except Exception as error:
|
||
|
error_console.print(f"Unable to read {args.path!r}; {error}")
|
||
|
sys.exit(-1)
|
||
|
|
||
|
console.print(JSON(json_data, indent=args.indent), soft_wrap=True)
|