codekingpro/portable-devtools
114k
1from pathlib import Path2from json import loads, dumps3from typing import Any, Callable, Optional, Union4 5from .text import Text6from .highlighter import JSONHighlighter, NullHighlighter7 8 9class JSON:10 """A renderable which pretty prints JSON.11 12 Args:13 json (str): JSON encoded data.14 indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.15 highlight (bool, optional): Enable highlighting. Defaults to True.16 skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.17 ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.18 check_circular (bool, optional): Check for circular references. Defaults to True.19 allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.20 default (Callable, optional): A callable that converts values that can not be encoded21 in to something that can be JSON encoded. Defaults to None.22 sort_keys (bool, optional): Sort dictionary keys. Defaults to False.23 """24 25 def __init__(26 self,27 json: str,28 indent: Union[None, int, str] = 2,29 highlight: bool = True,30 skip_keys: bool = False,31 ensure_ascii: bool = False,32 check_circular: bool = True,33 allow_nan: bool = True,34 default: Optional[Callable[[Any], Any]] = None,35 sort_keys: bool = False,36 ) -> None:37 data = loads(json)38 json = dumps(39 data,40 indent=indent,41 skipkeys=skip_keys,42 ensure_ascii=ensure_ascii,43 check_circular=check_circular,44 allow_nan=allow_nan,45 default=default,46 sort_keys=sort_keys,47 )48 highlighter = JSONHighlighter() if highlight else NullHighlighter()49 self.text = highlighter(json)50 self.text.no_wrap = True51 self.text.overflow = None52 53 @classmethod54 def from_data(55 cls,56 data: Any,57 indent: Union[None, int, str] = 2,58 highlight: bool = True,59 skip_keys: bool = False,60 ensure_ascii: bool = False,61 check_circular: bool = True,62 allow_nan: bool = True,63 default: Optional[Callable[[Any], Any]] = None,64 sort_keys: bool = False,65 ) -> "JSON":66 """Encodes a JSON object from arbitrary data.67 68 Args:69 data (Any): An object that may be encoded in to JSON70 indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.71 highlight (bool, optional): Enable highlighting. Defaults to True.72 default (Callable, optional): Optional callable which will be called for objects that cannot be serialized. Defaults to None.73 skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.74 ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.75 check_circular (bool, optional): Check for circular references. Defaults to True.76 allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.77 default (Callable, optional): A callable that converts values that can not be encoded78 in to something that can be JSON encoded. Defaults to None.79 sort_keys (bool, optional): Sort dictionary keys. Defaults to False.80 81 Returns:82 JSON: New JSON object from the given data.83 """84 json_instance: "JSON" = cls.__new__(cls)85 json = dumps(86 data,87 indent=indent,88 skipkeys=skip_keys,89 ensure_ascii=ensure_ascii,90 check_circular=check_circular,91 allow_nan=allow_nan,92 default=default,93 sort_keys=sort_keys,94 )95 highlighter = JSONHighlighter() if highlight else NullHighlighter()96 json_instance.text = highlighter(json)97 json_instance.text.no_wrap = True98 json_instance.text.overflow = None99 return json_instance100 101 def __rich__(self) -> Text:102 return self.text103 104 105if __name__ == "__main__":106 import argparse107 import sys108 109 parser = argparse.ArgumentParser(description="Pretty print json")110 parser.add_argument(111 "path",112 metavar="PATH",113 help="path to file, or - for stdin",114 )115 parser.add_argument(116 "-i",117 "--indent",118 metavar="SPACES",119 type=int,120 help="Number of spaces in an indent",121 default=2,122 )123 args = parser.parse_args()124 125 from rich.console import Console126 127 console = Console()128 error_console = Console(stderr=True)129 130 try:131 if args.path == "-":132 json_data = sys.stdin.read()133 else:134 json_data = Path(args.path).read_text()135 except Exception as error:136 error_console.print(f"Unable to read {args.path!r}; {error}")137 sys.exit(-1)138 139 console.print(JSON(json_data, indent=args.indent), soft_wrap=True)140 