From 38e0116c3d3ccb50f8f6e9412d2caa99bb5ecd91 Mon Sep 17 00:00:00 2001 From: Michal Horejsek Date: Tue, 14 Nov 2023 21:03:05 +0100 Subject: [PATCH] Added use_formats parameter --- CHANGELOG.txt | 4 ++++ fastjsonschema/__init__.py | 19 ++++++++++++------- fastjsonschema/draft04.py | 5 ++++- fastjsonschema/draft06.py | 4 ++-- fastjsonschema/draft07.py | 4 ++-- fastjsonschema/version.py | 2 +- tests/conftest.py | 6 +++--- tests/test_format.py | 5 +++++ 8 files changed, 33 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 46fa062..526335c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +=== 2.19.0 (2023-11-14) + +* Added `use_formats` parameter to allow disable automatic assertions + === 2.18.1 (2023-10-01) * Lazy import of urllib to improve import performance diff --git a/fastjsonschema/__init__.py b/fastjsonschema/__init__.py index 0d2d388..b0b8849 100644 --- a/fastjsonschema/__init__.py +++ b/fastjsonschema/__init__.py @@ -123,7 +123,7 @@ ) -def validate(definition, data, handlers={}, formats={}, use_default=True): +def validate(definition, data, handlers={}, formats={}, use_default=True, use_formats=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. @@ -139,12 +139,12 @@ def validate(definition, data, handlers={}, formats={}, use_default=True): Preferred is to use :any:`compile` function. """ - return compile(definition, handlers, formats, use_default)(data) + return compile(definition, handlers, formats, use_default, use_formats)(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={}, use_default=True): +def compile(definition, handlers={}, formats={}, use_default=True, use_formats=True): """ Generates validation function for validating JSON schema passed in ``definition``. Example: @@ -196,13 +196,17 @@ def compile(definition, handlers={}, formats={}, use_default=True): 'bar': lambda value: value in ('foo', 'bar'), }) + Note that formats are automatically used as assertions. It can be turned + off by passing `use_formats=False`. When disabled, custom formats are + disabled as well. (Added in 2.19.0.) + Exception :any:`JsonSchemaDefinitionException` is raised when generating the code fails (bad definition). Exception :any:`JsonSchemaValueException` is raised from generated function when validation fails (data do not follow the definition). """ - resolver, code_generator = _factory(definition, handlers, formats, use_default) + resolver, code_generator = _factory(definition, handlers, formats, use_default, use_formats) global_state = code_generator.global_state # Do not pass local state so it can recursively call itself. exec(code_generator.func_code, global_state) @@ -213,7 +217,7 @@ def compile(definition, handlers={}, formats={}, use_default=True): # pylint: disable=dangerous-default-value -def compile_to_code(definition, handlers={}, formats={}, use_default=True): +def compile_to_code(definition, handlers={}, formats={}, use_default=True, use_formats=True): """ Generates validation code for validating JSON schema passed in ``definition``. Example: @@ -236,7 +240,7 @@ def compile_to_code(definition, handlers={}, formats={}, use_default=True): Exception :any:`JsonSchemaDefinitionException` is raised when generating the code fails (bad definition). """ - _, code_generator = _factory(definition, handlers, formats, use_default) + _, code_generator = _factory(definition, handlers, formats, use_default, use_formats) return ( 'VERSION = "' + VERSION + '"\n' + code_generator.global_state_code + '\n' + @@ -244,13 +248,14 @@ def compile_to_code(definition, handlers={}, formats={}, use_default=True): ) -def _factory(definition, handlers, formats={}, use_default=True): +def _factory(definition, handlers, formats={}, use_default=True, use_formats=True): resolver = RefResolver.from_schema(definition, handlers=handlers, store={}) code_generator = _get_code_generator_class(definition)( definition, resolver=resolver, formats=formats, use_default=use_default, + use_formats=use_formats, ) return resolver, code_generator diff --git a/fastjsonschema/draft04.py b/fastjsonschema/draft04.py index 25cb374..e2d9c8a 100644 --- a/fastjsonschema/draft04.py +++ b/fastjsonschema/draft04.py @@ -34,9 +34,10 @@ class CodeGeneratorDraft04(CodeGenerator): 'uri': r'^\w+:(\/?\/?)[^\s]+\Z', } - def __init__(self, definition, resolver=None, formats={}, use_default=True): + def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True): super().__init__(definition, resolver) self._custom_formats = formats + self._use_formats = use_formats self._use_default = use_default self._json_keywords_to_function.update(( ('type', self.generate_type), @@ -254,6 +255,8 @@ def generate_format(self): Valid value for this definition is user@example.com but not @username """ + if not self._use_formats: + return with self.l('if isinstance({variable}, str):'): format_ = self._definition['format'] # Checking custom formats - user is allowed to override default formats. diff --git a/fastjsonschema/draft06.py b/fastjsonschema/draft06.py index f5aca06..2e679d0 100644 --- a/fastjsonschema/draft06.py +++ b/fastjsonschema/draft06.py @@ -16,8 +16,8 @@ class CodeGeneratorDraft06(CodeGeneratorDraft04): ), }) - def __init__(self, definition, resolver=None, formats={}, use_default=True): - super().__init__(definition, resolver, formats, use_default) + def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True): + super().__init__(definition, resolver, formats, use_default, use_formats) self._json_keywords_to_function.update(( ('exclusiveMinimum', self.generate_exclusive_minimum), ('exclusiveMaximum', self.generate_exclusive_maximum), diff --git a/fastjsonschema/draft07.py b/fastjsonschema/draft07.py index 470e23b..99403ae 100644 --- a/fastjsonschema/draft07.py +++ b/fastjsonschema/draft07.py @@ -17,8 +17,8 @@ class CodeGeneratorDraft07(CodeGeneratorDraft06): ), }) - def __init__(self, definition, resolver=None, formats={}, use_default=True): - super().__init__(definition, resolver, formats, use_default) + def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True): + super().__init__(definition, resolver, formats, use_default, use_formats) # pylint: disable=duplicate-code self._json_keywords_to_function.update(( ('if', self.generate_if_then_else), diff --git a/fastjsonschema/version.py b/fastjsonschema/version.py index 3335adc..301c56c 100644 --- a/fastjsonschema/version.py +++ b/fastjsonschema/version.py @@ -1 +1 @@ -VERSION = '2.18.1' +VERSION = '2.19.0' diff --git a/tests/conftest.py b/tests/conftest.py index 43d29f1..5960b08 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,16 +20,16 @@ def pytest_configure(config): @pytest.fixture def asserter(): - def f(definition, value, expected, formats={}): + def f(definition, value, expected, formats={}, use_formats=True): # When test fails, it will show up code. - code_generator = CodeGeneratorDraft07(definition, formats=formats) + code_generator = CodeGeneratorDraft07(definition, formats=formats, use_formats=use_formats) print(code_generator.func_code) pprint(code_generator.global_state) # By default old tests are written for draft-04. definition.setdefault('$schema', 'http://json-schema.org/draft-04/schema') - validator = compile(definition, formats=formats) + validator = compile(definition, formats=formats, use_formats=use_formats) if isinstance(expected, JsonSchemaValueException): with pytest.raises(JsonSchemaValueException) as exc: validator(value) diff --git a/tests/test_format.py b/tests/test_format.py index c43abc5..00a228f 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -56,3 +56,8 @@ def test_custom_format_override(asserter): asserter({'format': 'date-time'}, 'a', 'a', formats={ 'date-time': r'^[ab]$', }) + + +def test_disable_formats(asserter): + asserter({'format': 'date-time'}, 'bla', 'bla', use_formats=False) +