Skip to content

Commit

Permalink
New option use_default
Browse files Browse the repository at this point in the history
  • Loading branch information
horejsek committed Feb 1, 2021
1 parent 64a635e commit 858e8a4
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Add JsonSchemaValueException (replacement for JsonSchemaException)
* JsonSchemaException is base exception
* JsonSchemaDefinitionException no longer inherits from JsonSchemaValueException
* New option `use_default` to have option turn off default feature (`True` by default to be compatible)


=== 2.14.5 (2020-08-17)
Expand Down
39 changes: 27 additions & 12 deletions fastjsonschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,18 @@
from .ref_resolver import RefResolver
from .version import VERSION

__all__ = ('VERSION', 'JsonSchemaException', 'JsonSchemaValueException', 'JsonSchemaDefinitionException', 'validate', 'compile', 'compile_to_code')


def validate(definition, data, handlers={}, formats={}):
__all__ = (
'VERSION',
'JsonSchemaException',
'JsonSchemaValueException',
'JsonSchemaDefinitionException',
'validate',
'compile',
'compile_to_code',
)


def validate(definition, data, handlers={}, formats={}, use_default=True):
"""
Validation function for lazy programmers or for use cases, when you need
to call validation only once, so you do not have to compile it first.
Expand All @@ -102,11 +110,12 @@ def validate(definition, data, handlers={}, formats={}):
Preferred is to use :any:`compile` function.
"""
return compile(definition, handlers, formats)(data)
return compile(definition, handlers, formats, use_default)(data)


#TODO: Change use_default to False when upgrading to version 3.
# pylint: disable=redefined-builtin,dangerous-default-value,exec-used
def compile(definition, handlers={}, formats={}):
def compile(definition, handlers={}, formats={}, use_default=True):
"""
Generates validation function for validating JSON schema passed in ``definition``.
Example:
Expand All @@ -118,7 +127,8 @@ def compile(definition, handlers={}, formats={}):
validate = fastjsonschema.compile({'type': 'string'})
validate('hello')
This implementation support keyword ``default``:
This implementation supports keyword ``default`` (can be turned off
by passing `use_default=False`):
.. code-block:: python
Expand Down Expand Up @@ -163,15 +173,15 @@ def compile(definition, handlers={}, formats={}):
Exception :any:`JsonSchemaValueException` is raised from generated function when
validation fails (data do not follow the definition).
"""
resolver, code_generator = _factory(definition, handlers, formats)
resolver, code_generator = _factory(definition, handlers, formats, use_default)
global_state = code_generator.global_state
# Do not pass local state so it can recursively call itself.
exec(code_generator.func_code, global_state)
return global_state[resolver.get_scope_name()]


# pylint: disable=dangerous-default-value
def compile_to_code(definition, handlers={}, formats={}):
def compile_to_code(definition, handlers={}, formats={}, use_default=True):
"""
Generates validation code for validating JSON schema passed in ``definition``.
Example:
Expand All @@ -194,17 +204,22 @@ def compile_to_code(definition, handlers={}, formats={}):
Exception :any:`JsonSchemaDefinitionException` is raised when generating the
code fails (bad definition).
"""
_, code_generator = _factory(definition, handlers, formats)
_, code_generator = _factory(definition, handlers, formats, use_default)
return (
'VERSION = "' + VERSION + '"\n' +
code_generator.global_state_code + '\n' +
code_generator.func_code
)


def _factory(definition, handlers, formats={}):
def _factory(definition, handlers, formats={}, use_default=True):
resolver = RefResolver.from_schema(definition, handlers=handlers)
code_generator = _get_code_generator_class(definition)(definition, resolver=resolver, formats=formats)
code_generator = _get_code_generator_class(definition)(
definition,
resolver=resolver,
formats=formats,
use_default=use_default,
)
return resolver, code_generator


Expand Down
15 changes: 8 additions & 7 deletions fastjsonschema/draft04.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ class CodeGeneratorDraft04(CodeGenerator):
'uri': r'^\w+:(\/?\/?)[^\s]+\Z',
}

def __init__(self, definition, resolver=None, formats={}):
def __init__(self, definition, resolver=None, formats={}, use_default=True):
super().__init__(definition, resolver)
self._custom_formats = formats
self._use_default = use_default
self._json_keywords_to_function.update((
('type', self.generate_type),
('enum', self.generate_enum),
Expand Down Expand Up @@ -390,7 +391,7 @@ def generate_items(self):
'{}__{}'.format(self._variable, idx),
'{}[{}]'.format(self._variable_name, idx),
)
if isinstance(item_definition, dict) and 'default' in item_definition:
if self._use_default and isinstance(item_definition, dict) and 'default' in item_definition:
self.l('else: {variable}.append({})', repr(item_definition['default']))

if 'additionalItems' in self._definition:
Expand Down Expand Up @@ -470,7 +471,7 @@ def generate_properties(self):
'{}.{}'.format(self._variable_name, self.e(key)),
clear_variables=True,
)
if isinstance(prop_definition, dict) and 'default' in prop_definition:
if self._use_default and isinstance(prop_definition, dict) and 'default' in prop_definition:
self.l('else: {variable}["{}"] = {}', self.e(key), repr(prop_definition['default']))

def generate_pattern_properties(self):
Expand Down Expand Up @@ -526,7 +527,7 @@ def generate_additional_properties(self):
add_prop_definition = self._definition["additionalProperties"]
if add_prop_definition is True or add_prop_definition == {}:
return
elif add_prop_definition:
if add_prop_definition:
properties_keys = list(self._definition.get("properties", {}).keys())
with self.l('for {variable}_key in {variable}_keys:'):
with self.l('if {variable}_key not in {}:', properties_keys):
Expand Down Expand Up @@ -560,11 +561,11 @@ def generate_dependencies(self):
"""
self.create_variable_is_dict()
with self.l('if {variable}_is_dict:'):
isEmpty = True
is_empty = True
for key, values in self._definition["dependencies"].items():
if values == [] or values is True:
continue
isEmpty = False
is_empty = False
with self.l('if "{}" in {variable}:', self.e(key)):
if values is False:
self.exc('{} in {name} must not be there', key, rule='dependencies')
Expand All @@ -574,5 +575,5 @@ def generate_dependencies(self):
self.exc('{name} missing dependency {} for {}', self.e(value), self.e(key), rule='dependencies')
else:
self.generate_func_code_block(values, self._variable, self._variable_name, clear_variables=True)
if isEmpty:
if is_empty:
self.l('pass')
4 changes: 2 additions & 2 deletions fastjsonschema/draft06.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class CodeGeneratorDraft06(CodeGeneratorDraft04):
),
})

def __init__(self, definition, resolver=None, formats={}):
super().__init__(definition, resolver, formats)
def __init__(self, definition, resolver=None, formats={}, use_default=True):
super().__init__(definition, resolver, formats, use_default)
self._json_keywords_to_function.update((
('exclusiveMinimum', self.generate_exclusive_minimum),
('exclusiveMaximum', self.generate_exclusive_maximum),
Expand Down
4 changes: 2 additions & 2 deletions fastjsonschema/draft07.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class CodeGeneratorDraft07(CodeGeneratorDraft06):
),
})

def __init__(self, definition, resolver=None, formats={}):
super().__init__(definition, resolver, formats)
def __init__(self, definition, resolver=None, formats={}, use_default=True):
super().__init__(definition, resolver, formats, use_default)
# pylint: disable=duplicate-code
self._json_keywords_to_function.update((
('if', self.generate_if_then_else),
Expand Down
1 change: 1 addition & 0 deletions fastjsonschema/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def e(self, string):

def exc(self, msg, *args, rule=None):
"""
Short-cut for creating raising exception in the code.
"""
msg = 'raise JsonSchemaValueException("'+msg+'", value={variable}, name="{name}", definition={definition}, rule={rule})'
definition_rule = self.e(self._definition.get(rule) if isinstance(self._definition, dict) else None)
Expand Down
12 changes: 11 additions & 1 deletion tests/test_default.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from fastjsonschema import JsonSchemaValueException
from fastjsonschema import JsonSchemaValueException, validate


@pytest.mark.parametrize('value, expected', [
Expand Down Expand Up @@ -36,3 +36,13 @@ def test_default_in_array(asserter, value, expected):
{'type': 'number', 'default': 42},
],
}, value, expected)


def test_default_turned_off():
output = validate({
'type': 'object',
'properties': {
'a': {'type': 'string', 'default': ''},
},
}, {}, use_default=False)
assert output == {}

0 comments on commit 858e8a4

Please sign in to comment.