Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/fastjsonschema/draft06.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

90 statements  

1import decimal 

2from .draft04 import CodeGeneratorDraft04, JSON_TYPE_TO_PYTHON_TYPE 

3from .exceptions import JsonSchemaDefinitionException 

4from .generator import enforce_list 

5 

6 

7class CodeGeneratorDraft06(CodeGeneratorDraft04): 

8 FORMAT_REGEXS = dict(CodeGeneratorDraft04.FORMAT_REGEXS, **{ 

9 'json-pointer': r'^(/(([^/~])|(~[01]))*)*\Z', 

10 'uri-reference': r'^(\w+:(\/?\/?))?[^#\\\s]*(#[^\\\s]*)?\Z', 

11 'uri-template': ( 

12 r'^(?:(?:[^\x00-\x20\"\'<>%\\^`{|}]|%[0-9a-f]{2})|' 

13 r'\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+' 

14 r'(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+' 

15 r'(?::[1-9][0-9]{0,3}|\*)?)*\})*\Z' 

16 ), 

17 }) 

18 

19 def __init__( 

20 self, 

21 definition, 

22 resolver=None, 

23 formats={}, 

24 use_default=True, 

25 use_formats=True, 

26 detailed_exceptions=True, 

27 fast_fail=True, 

28 ): 

29 super().__init__(definition, resolver, formats, use_default, use_formats, detailed_exceptions, fast_fail) 

30 self._json_keywords_to_function.update(( 

31 ('exclusiveMinimum', self.generate_exclusive_minimum), 

32 ('exclusiveMaximum', self.generate_exclusive_maximum), 

33 ('propertyNames', self.generate_property_names), 

34 ('contains', self.generate_contains), 

35 ('const', self.generate_const), 

36 )) 

37 

38 def _generate_func_code_block(self, definition): 

39 if isinstance(definition, bool): 

40 return self.generate_boolean_schema() 

41 elif '$ref' in definition: 

42 # needed because ref overrides any sibling keywords 

43 return self.generate_ref() 

44 return self.run_generate_functions(definition) 

45 

46 def generate_boolean_schema(self): 

47 """ 

48 Means that schema can be specified by boolean. 

49 True means everything is valid, False everything is invalid. 

50 """ 

51 if self._definition is True: 

52 self.l('pass') 

53 if self._definition is False: 

54 self.exc('{name} must not be there') 

55 

56 def generate_type(self): 

57 """ 

58 Validation of type. Can be one type or list of types. 

59 

60 Since draft 06 a float without fractional part is an integer. 

61 

62 .. code-block:: python 

63 

64 {'type': 'string'} 

65 {'type': ['string', 'number']} 

66 """ 

67 types = enforce_list(self._definition['type']) 

68 try: 

69 python_types = ', '.join(JSON_TYPE_TO_PYTHON_TYPE[t] for t in types) 

70 except KeyError as exc: 

71 raise JsonSchemaDefinitionException('Unknown type') from exc 

72 

73 extra = '' 

74 

75 if 'integer' in types: 

76 extra += ' and not (isinstance({variable}, float) and {variable}.is_integer())'.format( 

77 variable=self._variable, 

78 ) 

79 

80 if ('number' in types or 'integer' in types) and 'boolean' not in types: 

81 extra += ' or isinstance({variable}, bool)'.format(variable=self._variable) 

82 

83 with self.l('if not isinstance({variable}, ({})){}:', python_types, extra): 

84 self.exc('{name} must be {}', ' or '.join(types), rule='type') 

85 

86 def generate_exclusive_minimum(self): 

87 with self.l('if isinstance({variable}, (int, float, Decimal)):'): 

88 if not isinstance(self._definition['exclusiveMinimum'], (int, float, decimal.Decimal)): 

89 raise JsonSchemaDefinitionException('exclusiveMinimum must be an integer, a float or a decimal') 

90 with self.l('if {variable} <= {exclusiveMinimum}:'): 

91 self.exc('{name} must be bigger than {exclusiveMinimum}', rule='exclusiveMinimum') 

92 

93 def generate_exclusive_maximum(self): 

94 with self.l('if isinstance({variable}, (int, float, Decimal)):'): 

95 if not isinstance(self._definition['exclusiveMaximum'], (int, float, decimal.Decimal)): 

96 raise JsonSchemaDefinitionException('exclusiveMaximum must be an integer, a float or a decimal') 

97 with self.l('if {variable} >= {exclusiveMaximum}:'): 

98 self.exc('{name} must be smaller than {exclusiveMaximum}', rule='exclusiveMaximum') 

99 

100 def generate_property_names(self): 

101 """ 

102 Means that keys of object must to follow this definition. 

103 

104 .. code-block:: python 

105 

106 { 

107 'propertyNames': { 

108 'maxLength': 3, 

109 }, 

110 } 

111 

112 Valid keys of object for this definition are foo, bar, ... but not foobar for example. 

113 """ 

114 property_names_definition = self._definition.get('propertyNames', {}) 

115 if property_names_definition is True: 

116 pass 

117 elif property_names_definition is False: 

118 self.create_variable_keys() 

119 with self.l('if {variable}_keys:'): 

120 self.exc('{name} must not be there', rule='propertyNames') 

121 else: 

122 self.create_variable_is_dict() 

123 with self.l('if {variable}_is_dict:'): 

124 self.create_variable_with_length() 

125 with self.l('if {variable}_len != 0:'): 

126 self.l('{variable}_property_names = True') 

127 with self.l('for {variable}_key in {variable}:'): 

128 with self.l('try:'): 

129 count = self.generate_func_code_block( 

130 property_names_definition, 

131 '{}_key'.format(self._variable), 

132 self._variable_name, 

133 clear_variables=True, 

134 ) 

135 if not count: 

136 self.l('pass') 

137 with self.l('except JsonSchemaValueException:'): 

138 self.l('{variable}_property_names = False') 

139 with self.l('if not {variable}_property_names:'): 

140 self.exc('{name} must be named by propertyName definition', rule='propertyNames') 

141 

142 def generate_contains(self): 

143 """ 

144 Means that array must contain at least one defined item. 

145 

146 .. code-block:: python 

147 

148 { 

149 'contains': { 

150 'type': 'number', 

151 }, 

152 } 

153 

154 Valid array is any with at least one number. 

155 """ 

156 self.create_variable_is_list() 

157 with self.l('if {variable}_is_list:'): 

158 contains_definition = self._definition['contains'] 

159 

160 if contains_definition is False: 

161 self.exc('{name} is always invalid', rule='contains') 

162 elif contains_definition is True: 

163 with self.l('if not {variable}:'): 

164 self.exc('{name} must not be empty', rule='contains') 

165 else: 

166 self.l('{variable}_contains = False') 

167 with self.l('for {variable}_key in {variable}:'): 

168 with self.l('try:'): 

169 self.generate_func_code_block( 

170 contains_definition, 

171 '{}_key'.format(self._variable), 

172 self._variable_name, 

173 clear_variables=True, 

174 ) 

175 self.l('{variable}_contains = True') 

176 self.l('break') 

177 self.l('except JsonSchemaValueException: pass') 

178 

179 with self.l('if not {variable}_contains:'): 

180 self.exc('{name} must contain one of contains definition', rule='contains') 

181 

182 def generate_const(self): 

183 """ 

184 Means that value is valid when is equeal to const definition. 

185 

186 .. code-block:: python 

187 

188 { 

189 'const': 42, 

190 } 

191 

192 Only valid value is 42 in this example. 

193 """ 

194 const = self._definition['const'] 

195 match = self._enum_value_matches(self._variable, const) 

196 with self.l('if not ({}):', match): 

197 self.exc('{name} must be same as const definition: {definition_rule}', rule='const')