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

89 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 self.generate_func_code_block( 

130 property_names_definition, 

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

132 self._variable_name, 

133 clear_variables=True, 

134 ) 

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

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

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

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

139 

140 def generate_contains(self): 

141 """ 

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

143 

144 .. code-block:: python 

145 

146 { 

147 'contains': { 

148 'type': 'number', 

149 }, 

150 } 

151 

152 Valid array is any with at least one number. 

153 """ 

154 self.create_variable_is_list() 

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

156 contains_definition = self._definition['contains'] 

157 

158 if contains_definition is False: 

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

160 elif contains_definition is True: 

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

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

163 else: 

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

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

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

167 self.generate_func_code_block( 

168 contains_definition, 

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

170 self._variable_name, 

171 clear_variables=True, 

172 ) 

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

174 self.l('break') 

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

176 

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

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

179 

180 def generate_const(self): 

181 """ 

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

183 

184 .. code-block:: python 

185 

186 { 

187 'const': 42, 

188 } 

189 

190 Only valid value is 42 in this example. 

191 """ 

192 const = self._definition['const'] 

193 if isinstance(const, str): 

194 const = '"{}"'.format(self.e(const)) 

195 with self.l('if {variable} != {}:', const): 

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