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

87 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

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__(self, definition, resolver=None, formats={}, use_default=True): 

20 super().__init__(definition, resolver, formats, use_default) 

21 self._json_keywords_to_function.update(( 

22 ('exclusiveMinimum', self.generate_exclusive_minimum), 

23 ('exclusiveMaximum', self.generate_exclusive_maximum), 

24 ('propertyNames', self.generate_property_names), 

25 ('contains', self.generate_contains), 

26 ('const', self.generate_const), 

27 )) 

28 

29 def _generate_func_code_block(self, definition): 

30 if isinstance(definition, bool): 

31 self.generate_boolean_schema() 

32 elif '$ref' in definition: 

33 # needed because ref overrides any sibling keywords 

34 self.generate_ref() 

35 else: 

36 self.run_generate_functions(definition) 

37 

38 def generate_boolean_schema(self): 

39 """ 

40 Means that schema can be specified by boolean. 

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

42 """ 

43 if self._definition is False: 

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

45 

46 def generate_type(self): 

47 """ 

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

49 

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

51 

52 .. code-block:: python 

53 

54 {'type': 'string'} 

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

56 """ 

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

58 try: 

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

60 except KeyError as exc: 

61 raise JsonSchemaDefinitionException('Unknown type: {}'.format(exc)) 

62 

63 extra = '' 

64 

65 if 'integer' in types: 

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

67 variable=self._variable, 

68 ) 

69 

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

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

72 

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

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

75 

76 def generate_exclusive_minimum(self): 

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

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

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

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

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

82 

83 def generate_exclusive_maximum(self): 

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

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

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

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

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

89 

90 def generate_property_names(self): 

91 """ 

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

93 

94 .. code-block:: python 

95 

96 { 

97 'propertyNames': { 

98 'maxLength': 3, 

99 }, 

100 } 

101 

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

103 """ 

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

105 if property_names_definition is True: 

106 pass 

107 elif property_names_definition is False: 

108 self.create_variable_keys() 

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

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

111 else: 

112 self.create_variable_is_dict() 

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

114 self.create_variable_with_length() 

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

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

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

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

119 self.generate_func_code_block( 

120 property_names_definition, 

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

122 self._variable_name, 

123 clear_variables=True, 

124 ) 

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

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

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

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

129 

130 def generate_contains(self): 

131 """ 

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

133 

134 .. code-block:: python 

135 

136 { 

137 'contains': { 

138 'type': 'number', 

139 }, 

140 } 

141 

142 Valid array is any with at least one number. 

143 """ 

144 self.create_variable_is_list() 

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

146 contains_definition = self._definition['contains'] 

147 

148 if contains_definition is False: 

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

150 elif contains_definition is True: 

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

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

153 else: 

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

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

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

157 self.generate_func_code_block( 

158 contains_definition, 

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

160 self._variable_name, 

161 clear_variables=True, 

162 ) 

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

164 self.l('break') 

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

166 

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

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

169 

170 def generate_const(self): 

171 """ 

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

173 

174 .. code-block:: python 

175 

176 { 

177 'const': 42, 

178 } 

179 

180 Only valid value is 42 in this example. 

181 """ 

182 const = self._definition['const'] 

183 if isinstance(const, str): 

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

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

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