Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True): 

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

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 True: 

44 self.l('pass') 

45 if self._definition is False: 

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

47 

48 def generate_type(self): 

49 """ 

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

51 

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

53 

54 .. code-block:: python 

55 

56 {'type': 'string'} 

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

58 """ 

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

60 try: 

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

62 except KeyError as exc: 

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

64 

65 extra = '' 

66 

67 if 'integer' in types: 

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

69 variable=self._variable, 

70 ) 

71 

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

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

74 

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

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

77 

78 def generate_exclusive_minimum(self): 

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

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

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

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

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

84 

85 def generate_exclusive_maximum(self): 

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

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

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

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

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

91 

92 def generate_property_names(self): 

93 """ 

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

95 

96 .. code-block:: python 

97 

98 { 

99 'propertyNames': { 

100 'maxLength': 3, 

101 }, 

102 } 

103 

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

105 """ 

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

107 if property_names_definition is True: 

108 pass 

109 elif property_names_definition is False: 

110 self.create_variable_keys() 

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

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

113 else: 

114 self.create_variable_is_dict() 

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

116 self.create_variable_with_length() 

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

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

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

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

121 self.generate_func_code_block( 

122 property_names_definition, 

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

124 self._variable_name, 

125 clear_variables=True, 

126 ) 

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

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

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

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

131 

132 def generate_contains(self): 

133 """ 

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

135 

136 .. code-block:: python 

137 

138 { 

139 'contains': { 

140 'type': 'number', 

141 }, 

142 } 

143 

144 Valid array is any with at least one number. 

145 """ 

146 self.create_variable_is_list() 

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

148 contains_definition = self._definition['contains'] 

149 

150 if contains_definition is False: 

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

152 elif contains_definition is True: 

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

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

155 else: 

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

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

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

159 self.generate_func_code_block( 

160 contains_definition, 

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

162 self._variable_name, 

163 clear_variables=True, 

164 ) 

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

166 self.l('break') 

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

168 

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

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

171 

172 def generate_const(self): 

173 """ 

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

175 

176 .. code-block:: python 

177 

178 { 

179 'const': 42, 

180 } 

181 

182 Only valid value is 42 in this example. 

183 """ 

184 const = self._definition['const'] 

185 if isinstance(const, str): 

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

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

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