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
« 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
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 })
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 ))
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)
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')
46 def generate_type(self):
47 """
48 Validation of type. Can be one type or list of types.
50 Since draft 06 a float without fractional part is an integer.
52 .. code-block:: python
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))
63 extra = ''
65 if 'integer' in types:
66 extra += ' and not (isinstance({variable}, float) and {variable}.is_integer())'.format(
67 variable=self._variable,
68 )
70 if ('number' in types or 'integer' in types) and 'boolean' not in types:
71 extra += ' or isinstance({variable}, bool)'.format(variable=self._variable)
73 with self.l('if not isinstance({variable}, ({})){}:', python_types, extra):
74 self.exc('{name} must be {}', ' or '.join(types), rule='type')
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')
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')
90 def generate_property_names(self):
91 """
92 Means that keys of object must to follow this definition.
94 .. code-block:: python
96 {
97 'propertyNames': {
98 'maxLength': 3,
99 },
100 }
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')
130 def generate_contains(self):
131 """
132 Means that array must contain at least one defined item.
134 .. code-block:: python
136 {
137 'contains': {
138 'type': 'number',
139 },
140 }
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']
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')
167 with self.l('if not {variable}_contains:'):
168 self.exc('{name} must contain one of contains definition', rule='contains')
170 def generate_const(self):
171 """
172 Means that value is valid when is equeal to const definition.
174 .. code-block:: python
176 {
177 'const': 42,
178 }
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')