Coverage for genschema / cli.py: 0%

88 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-14 22:23 +0000

1import argparse 

2import json 

3import sys 

4import time 

5 

6from rich.console import Console 

7 

8from . import Converter, PseudoArrayHandler 

9from .comparators import ( 

10 DeleteElement, 

11 EmptyComparator, 

12 FormatComparator, 

13 RequiredComparator, 

14 SchemaVersionComparator, 

15) 

16 

17console = Console() 

18 

19 

20def main() -> None: 

21 parser = argparse.ArgumentParser( 

22 description="Generate JSON Schema from JSON input using genschema.", 

23 formatter_class=argparse.RawDescriptionHelpFormatter, 

24 epilog=""" 

25Examples: 

26 genschema input.json -o schema.json 

27 genschema input1.json input2.json --base-of oneOf 

28 cat input.json | genschema - 

29 genschema --base-of anyOf < input.json 

30 genschema dir/file1.json dir/file2.json -o schema.json 

31 """, 

32 ) 

33 parser.add_argument( 

34 "inputs", 

35 nargs="*", 

36 help="Paths to input JSON files. Use '-' for stdin. " 

37 "If no arguments are provided, show this help message.", 

38 ) 

39 parser.add_argument( 

40 "-o", 

41 "--output", 

42 help="Path to output JSON Schema file. If not specified, output to stdout.", 

43 ) 

44 parser.add_argument( 

45 "--base-of", 

46 choices=["anyOf", "oneOf"], 

47 default="anyOf", 

48 help="Combinator for differing types (default: anyOf).", 

49 ) 

50 parser.add_argument( 

51 "--no-pseudo-array", action="store_true", help="Disable pseudo-array handling." 

52 ) 

53 parser.add_argument("--no-format", action="store_true", help="Disable FormatComparator.") 

54 parser.add_argument("--no-required", action="store_true", help="Disable RequiredComparator.") 

55 parser.add_argument("--no-empty", action="store_true", help="Disable EmptyComparator.") 

56 parser.add_argument( 

57 "--no-schema-version", 

58 action="store_true", 

59 help="Disable SchemaVersionComparator.", 

60 ) 

61 parser.add_argument( 

62 "--no-delete-element", action="store_true", help="Disable DeleteElement comparators." 

63 ) 

64 

65 # If no arguments, show help and exit 

66 if len(sys.argv) == 1: 

67 parser.print_help(sys.stderr) 

68 sys.exit(1) 

69 

70 args = parser.parse_args() 

71 

72 # Collect input data 

73 datas = [] 

74 if not args.inputs: 

75 # This case shouldn't happen due to the check above, but for safety 

76 try: 

77 data = json.load(sys.stdin) 

78 datas.append(data) 

79 except json.JSONDecodeError as e: 

80 console.print(f"[red]Error reading JSON from stdin: {e}[/red]") 

81 sys.exit(1) 

82 else: 

83 for input_path in args.inputs: 

84 if input_path == "-": 

85 try: 

86 data = json.load(sys.stdin) 

87 datas.append(data) 

88 except json.JSONDecodeError as e: 

89 console.print(f"[red]Error reading JSON from stdin: {e}[/red]") 

90 sys.exit(1) 

91 else: 

92 try: 

93 with open(input_path, "r", encoding="utf-8") as f: 

94 data = json.load(f) 

95 datas.append(data) 

96 except FileNotFoundError: 

97 console.print(f"[red]File not found: {input_path}[/red]") 

98 sys.exit(1) 

99 except json.JSONDecodeError as e: 

100 console.print(f"[red]Invalid JSON in file {input_path}: {e}[/red]") 

101 sys.exit(1) 

102 

103 if not datas: 

104 console.print("[red]No valid JSON provided.[/red]") 

105 sys.exit(1) 

106 

107 # Converter setup 

108 pseudo_handler = None if args.no_pseudo_array else PseudoArrayHandler() 

109 conv = Converter(pseudo_handler=pseudo_handler, base_of=args.base_of) 

110 

111 for data in datas: 

112 conv.add_json(data) 

113 

114 # Register comparators conditionally 

115 if not args.no_format: 

116 conv.register(FormatComparator()) 

117 if not args.no_schema_version: 

118 conv.register(SchemaVersionComparator()) 

119 if not args.no_required: 

120 conv.register(RequiredComparator()) 

121 if not args.no_empty: 

122 conv.register(EmptyComparator()) 

123 if not args.no_delete_element: 

124 conv.register(DeleteElement()) 

125 conv.register(DeleteElement("isPseudoArray")) 

126 

127 # Generate schema 

128 start_time = time.time() 

129 try: 

130 result = conv.run() 

131 except Exception as e: 

132 console.print(f"[red]Error generating schema: {e}[/red]") 

133 sys.exit(1) 

134 elapsed = round(time.time() - start_time, 4) 

135 

136 # Output result 

137 if args.output: 

138 try: 

139 with open(args.output, "w", encoding="utf-8") as f: 

140 json.dump(result, f, indent=2, ensure_ascii=False) 

141 console.print(f"[green]Schema successfully written to {args.output}[/green]") 

142 except Exception as e: 

143 console.print(f"[red]Error writing file {args.output}: {e}[/red]") 

144 sys.exit(1) 

145 else: 

146 console.print(result) 

147 

148 # Execution info 

149 instances_word = "instance" if len(datas) == 1 else "instances" 

150 console.print(f"Generated from {len(datas)} JSON {instances_word}.") 

151 console.print(f"Elapsed time: {elapsed} sec.") 

152 

153 

154if __name__ == "__main__": 

155 main()