| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import decimal
- from .draft04 import CodeGeneratorDraft04, JSON_TYPE_TO_PYTHON_TYPE
- from .exceptions import JsonSchemaDefinitionException
- from .generator import enforce_list
- class CodeGeneratorDraft06(CodeGeneratorDraft04):
- FORMAT_REGEXS = dict(CodeGeneratorDraft04.FORMAT_REGEXS, **{
- 'json-pointer': r'^(/(([^/~])|(~[01]))*)*\Z',
- 'uri-reference': r'^(\w+:(\/?\/?))?[^#\\\s]*(#[^\\\s]*)?\Z',
- 'uri-template': (
- r'^(?:(?:[^\x00-\x20\"\'<>%\\^`{|}]|%[0-9a-f]{2})|'
- r'\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+'
- r'(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+'
- r'(?::[1-9][0-9]{0,3}|\*)?)*\})*\Z'
- ),
- })
- def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True, detailed_exceptions=True):
- super().__init__(definition, resolver, formats, use_default, use_formats, detailed_exceptions)
- self._json_keywords_to_function.update((
- ('exclusiveMinimum', self.generate_exclusive_minimum),
- ('exclusiveMaximum', self.generate_exclusive_maximum),
- ('propertyNames', self.generate_property_names),
- ('contains', self.generate_contains),
- ('const', self.generate_const),
- ))
- def _generate_func_code_block(self, definition):
- if isinstance(definition, bool):
- self.generate_boolean_schema()
- elif '$ref' in definition:
- # needed because ref overrides any sibling keywords
- self.generate_ref()
- else:
- self.run_generate_functions(definition)
- def generate_boolean_schema(self):
- """
- Means that schema can be specified by boolean.
- True means everything is valid, False everything is invalid.
- """
- if self._definition is True:
- self.l('pass')
- if self._definition is False:
- self.exc('{name} must not be there')
- def generate_type(self):
- """
- Validation of type. Can be one type or list of types.
- Since draft 06 a float without fractional part is an integer.
- .. code-block:: python
- {'type': 'string'}
- {'type': ['string', 'number']}
- """
- types = enforce_list(self._definition['type'])
- try:
- python_types = ', '.join(JSON_TYPE_TO_PYTHON_TYPE[t] for t in types)
- except KeyError as exc:
- raise JsonSchemaDefinitionException('Unknown type: {}'.format(exc))
- extra = ''
- if 'integer' in types:
- extra += ' and not (isinstance({variable}, float) and {variable}.is_integer())'.format(
- variable=self._variable,
- )
- if ('number' in types or 'integer' in types) and 'boolean' not in types:
- extra += ' or isinstance({variable}, bool)'.format(variable=self._variable)
- with self.l('if not isinstance({variable}, ({})){}:', python_types, extra):
- self.exc('{name} must be {}', ' or '.join(types), rule='type')
- def generate_exclusive_minimum(self):
- with self.l('if isinstance({variable}, (int, float, Decimal)):'):
- if not isinstance(self._definition['exclusiveMinimum'], (int, float, decimal.Decimal)):
- raise JsonSchemaDefinitionException('exclusiveMinimum must be an integer, a float or a decimal')
- with self.l('if {variable} <= {exclusiveMinimum}:'):
- self.exc('{name} must be bigger than {exclusiveMinimum}', rule='exclusiveMinimum')
- def generate_exclusive_maximum(self):
- with self.l('if isinstance({variable}, (int, float, Decimal)):'):
- if not isinstance(self._definition['exclusiveMaximum'], (int, float, decimal.Decimal)):
- raise JsonSchemaDefinitionException('exclusiveMaximum must be an integer, a float or a decimal')
- with self.l('if {variable} >= {exclusiveMaximum}:'):
- self.exc('{name} must be smaller than {exclusiveMaximum}', rule='exclusiveMaximum')
- def generate_property_names(self):
- """
- Means that keys of object must to follow this definition.
- .. code-block:: python
- {
- 'propertyNames': {
- 'maxLength': 3,
- },
- }
- Valid keys of object for this definition are foo, bar, ... but not foobar for example.
- """
- property_names_definition = self._definition.get('propertyNames', {})
- if property_names_definition is True:
- pass
- elif property_names_definition is False:
- self.create_variable_keys()
- with self.l('if {variable}_keys:'):
- self.exc('{name} must not be there', rule='propertyNames')
- else:
- self.create_variable_is_dict()
- with self.l('if {variable}_is_dict:'):
- self.create_variable_with_length()
- with self.l('if {variable}_len != 0:'):
- self.l('{variable}_property_names = True')
- with self.l('for {variable}_key in {variable}:'):
- with self.l('try:'):
- self.generate_func_code_block(
- property_names_definition,
- '{}_key'.format(self._variable),
- self._variable_name,
- clear_variables=True,
- )
- with self.l('except JsonSchemaValueException:'):
- self.l('{variable}_property_names = False')
- with self.l('if not {variable}_property_names:'):
- self.exc('{name} must be named by propertyName definition', rule='propertyNames')
- def generate_contains(self):
- """
- Means that array must contain at least one defined item.
- .. code-block:: python
- {
- 'contains': {
- 'type': 'number',
- },
- }
- Valid array is any with at least one number.
- """
- self.create_variable_is_list()
- with self.l('if {variable}_is_list:'):
- contains_definition = self._definition['contains']
- if contains_definition is False:
- self.exc('{name} is always invalid', rule='contains')
- elif contains_definition is True:
- with self.l('if not {variable}:'):
- self.exc('{name} must not be empty', rule='contains')
- else:
- self.l('{variable}_contains = False')
- with self.l('for {variable}_key in {variable}:'):
- with self.l('try:'):
- self.generate_func_code_block(
- contains_definition,
- '{}_key'.format(self._variable),
- self._variable_name,
- clear_variables=True,
- )
- self.l('{variable}_contains = True')
- self.l('break')
- self.l('except JsonSchemaValueException: pass')
- with self.l('if not {variable}_contains:'):
- self.exc('{name} must contain one of contains definition', rule='contains')
- def generate_const(self):
- """
- Means that value is valid when is equeal to const definition.
- .. code-block:: python
- {
- 'const': 42,
- }
- Only valid value is 42 in this example.
- """
- const = self._definition['const']
- if isinstance(const, str):
- const = '"{}"'.format(self.e(const))
- with self.l('if {variable} != {}:', const):
- self.exc('{name} must be same as const definition: {definition_rule}', rule='const')
|