json.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. from json import loads, dumps
  2. from typing import Any, Callable, Optional, Union
  3. from .text import Text
  4. from .highlighter import JSONHighlighter, NullHighlighter
  5. class JSON:
  6. """A renderable which pretty prints JSON.
  7. Args:
  8. json (str): JSON encoded data.
  9. indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.
  10. highlight (bool, optional): Enable highlighting. Defaults to True.
  11. skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
  12. ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
  13. check_circular (bool, optional): Check for circular references. Defaults to True.
  14. allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
  15. default (Callable, optional): A callable that converts values that can not be encoded
  16. in to something that can be JSON encoded. Defaults to None.
  17. sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
  18. """
  19. def __init__(
  20. self,
  21. json: str,
  22. indent: Union[None, int, str] = 2,
  23. highlight: bool = True,
  24. skip_keys: bool = False,
  25. ensure_ascii: bool = True,
  26. check_circular: bool = True,
  27. allow_nan: bool = True,
  28. default: Optional[Callable[[Any], Any]] = None,
  29. sort_keys: bool = False,
  30. ) -> None:
  31. data = loads(json)
  32. json = dumps(
  33. data,
  34. indent=indent,
  35. skipkeys=skip_keys,
  36. ensure_ascii=ensure_ascii,
  37. check_circular=check_circular,
  38. allow_nan=allow_nan,
  39. default=default,
  40. sort_keys=sort_keys,
  41. )
  42. highlighter = JSONHighlighter() if highlight else NullHighlighter()
  43. self.text = highlighter(json)
  44. self.text.no_wrap = True
  45. self.text.overflow = None
  46. @classmethod
  47. def from_data(
  48. cls,
  49. data: Any,
  50. indent: Union[None, int, str] = 2,
  51. highlight: bool = True,
  52. skip_keys: bool = False,
  53. ensure_ascii: bool = True,
  54. check_circular: bool = True,
  55. allow_nan: bool = True,
  56. default: Optional[Callable[[Any], Any]] = None,
  57. sort_keys: bool = False,
  58. ) -> "JSON":
  59. """Encodes a JSON object from arbitrary data.
  60. Args:
  61. data (Any): An object that may be encoded in to JSON
  62. indent (Union[None, int, str], optional): Number of characters to indent by. Defaults to 2.
  63. highlight (bool, optional): Enable highlighting. Defaults to True.
  64. default (Callable, optional): Optional callable which will be called for objects that cannot be serialized. Defaults to None.
  65. skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
  66. ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
  67. check_circular (bool, optional): Check for circular references. Defaults to True.
  68. allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
  69. default (Callable, optional): A callable that converts values that can not be encoded
  70. in to something that can be JSON encoded. Defaults to None.
  71. sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
  72. Returns:
  73. JSON: New JSON object from the given data.
  74. """
  75. json_instance: "JSON" = cls.__new__(cls)
  76. json = dumps(
  77. data,
  78. indent=indent,
  79. skipkeys=skip_keys,
  80. ensure_ascii=ensure_ascii,
  81. check_circular=check_circular,
  82. allow_nan=allow_nan,
  83. default=default,
  84. sort_keys=sort_keys,
  85. )
  86. highlighter = JSONHighlighter() if highlight else NullHighlighter()
  87. json_instance.text = highlighter(json)
  88. json_instance.text.no_wrap = True
  89. json_instance.text.overflow = None
  90. return json_instance
  91. def __rich__(self) -> Text:
  92. return self.text
  93. if __name__ == "__main__":
  94. import argparse
  95. import sys
  96. parser = argparse.ArgumentParser(description="Pretty print json")
  97. parser.add_argument(
  98. "path",
  99. metavar="PATH",
  100. help="path to file, or - for stdin",
  101. )
  102. parser.add_argument(
  103. "-i",
  104. "--indent",
  105. metavar="SPACES",
  106. type=int,
  107. help="Number of spaces in an indent",
  108. default=2,
  109. )
  110. args = parser.parse_args()
  111. from pip._vendor.rich.console import Console
  112. console = Console()
  113. error_console = Console(stderr=True)
  114. try:
  115. if args.path == "-":
  116. json_data = sys.stdin.read()
  117. else:
  118. with open(args.path, "rt") as json_file:
  119. json_data = json_file.read()
  120. except Exception as error:
  121. error_console.print(f"Unable to read {args.path!r}; {error}")
  122. sys.exit(-1)
  123. console.print(JSON(json_data, indent=args.indent), soft_wrap=True)