Skip to content

Commit

Permalink
Add detailed_exception parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
horejsek committed Nov 27, 2024
1 parent 356bfa2 commit b0e2bcc
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Improved regexp for date format to comfort test suite
* Improved regexp for ipv4 format to comfort test suite
* Added partial support of idn-hostname format
* Added `detailed_exceptions` flag (default to True to avoid breaking change) - when used, results are twice as fast

=== 2.20.0 (2024-06-15)

Expand Down
48 changes: 27 additions & 21 deletions fastjsonschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,22 @@
.. code-block:: bash
$ make performance
fast_compiled valid ==> 0.0464646
fast_compiled invalid ==> 0.0030227
fast_file valid ==> 0.0461219
fast_file invalid ==> 0.0030608
fast_not_compiled valid ==> 11.4627202
fast_not_compiled invalid ==> 2.5726230
jsonschema valid ==> 7.5844927
jsonschema invalid ==> 1.9204665
jsonschema_compiled valid ==> 0.6938364
jsonschema_compiled invalid ==> 0.0359244
jsonspec valid ==> 9.0715843
jsonspec invalid ==> 2.1650488
validictory valid ==> 0.4874793
validictory invalid ==> 0.0232244
fast_compiled valid ==> 0.0993900
fast_compiled invalid ==> 0.0041089
fast_compiled_without_exc valid ==> 0.0465258
fast_compiled_without_exc invalid ==> 0.0023688
fast_file valid ==> 0.0989483
fast_file invalid ==> 0.0041104
fast_not_compiled valid ==> 11.9572681
fast_not_compiled invalid ==> 2.9512092
jsonschema valid ==> 5.2233240
jsonschema invalid ==> 1.3227916
jsonschema_compiled valid ==> 0.4447982
jsonschema_compiled invalid ==> 0.0231333
jsonspec valid ==> 4.1450569
jsonspec invalid ==> 1.0485777
validictory valid ==> 0.2730411
validictory invalid ==> 0.0183669
This library follows and implements `JSON schema draft-04, draft-06, and draft-07
<http://json-schema.org>`_. Sometimes it's not perfectly clear, so I recommend also
Expand Down Expand Up @@ -123,7 +125,7 @@
)


def validate(definition, data, handlers={}, formats={}, use_default=True, use_formats=True):
def validate(definition, data, handlers={}, formats={}, use_default=True, use_formats=True, detailed_exceptions=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 @@ -139,12 +141,12 @@ def validate(definition, data, handlers={}, formats={}, use_default=True, use_fo
Preferred is to use :any:`compile` function.
"""
return compile(definition, handlers, formats, use_default, use_formats)(data)
return compile(definition, handlers, formats, use_default, use_formats, detailed_exceptions)(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, use_formats=True):
def compile(definition, handlers={}, formats={}, use_default=True, use_formats=True, detailed_exceptions=True):
"""
Generates validation function for validating JSON schema passed in ``definition``.
Example:
Expand Down Expand Up @@ -200,13 +202,16 @@ def compile(definition, handlers={}, formats={}, use_default=True, use_formats=T
off by passing `use_formats=False`. When disabled, custom formats are
disabled as well. (Added in 2.19.0.)
If you don't need detailed exceptions, you can turn the details off and gain
additional performance by passing `detailed_exceptions=False`.
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, use_formats)
resolver, code_generator = _factory(definition, handlers, formats, use_default, use_formats, detailed_exceptions)
global_state = code_generator.global_state
# Do not pass local state so it can recursively call itself.
exec(code_generator.func_code, global_state)
Expand All @@ -217,7 +222,7 @@ def compile(definition, handlers={}, formats={}, use_default=True, use_formats=T


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


def _factory(definition, handlers, formats={}, use_default=True, use_formats=True):
def _factory(definition, handlers, formats={}, use_default=True, use_formats=True, detailed_exceptions=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,
detailed_exceptions=detailed_exceptions,
)
return resolver, code_generator

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

def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True):
super().__init__(definition, resolver)
def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True, detailed_exceptions=True):
super().__init__(definition, resolver, detailed_exceptions)
self._custom_formats = formats
self._use_formats = use_formats
self._use_default = use_default
Expand Down
4 changes: 2 additions & 2 deletions fastjsonschema/draft06.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class CodeGeneratorDraft06(CodeGeneratorDraft04):
),
})

def __init__(self, definition, resolver=None, formats={}, use_default=True, use_formats=True):
super().__init__(definition, resolver, formats, use_default, use_formats)
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),
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={}, use_default=True, use_formats=True):
super().__init__(definition, resolver, formats, use_default, use_formats)
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)
# pylint: disable=duplicate-code
self._json_keywords_to_function.update((
('if', self.generate_if_then_else),
Expand Down
7 changes: 6 additions & 1 deletion fastjsonschema/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ class CodeGenerator:

INDENT = 4 # spaces

def __init__(self, definition, resolver=None):
def __init__(self, definition, resolver=None, detailed_exceptions=True):
self._code = []
self._compile_regexps = {}
self._custom_formats = {}
self._detailed_exceptions = detailed_exceptions

# Any extra library should be here to be imported only once.
# Lines are imports to be printed in the file and objects
Expand Down Expand Up @@ -266,6 +267,10 @@ def exc(self, msg, *args, append_to_msg=None, rule=None):
"""
Short-cut for creating raising exception in the code.
"""
if not self._detailed_exceptions:
self.l('raise JsonSchemaValueException("'+msg+'")', *args)
return

arg = '"'+msg+'"'
if append_to_msg:
arg += ' + (' + append_to_msg + ')'
Expand Down
11 changes: 10 additions & 1 deletion performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@


fastjsonschema_validate = fastjsonschema.compile(JSON_SCHEMA)
fastjsonschema_validate_without_exc = fastjsonschema.compile(JSON_SCHEMA, detailed_exceptions=False)


def fast_compiled(value, _):
Expand All @@ -84,6 +85,10 @@ def fast_not_compiled(value, json_schema):
fastjsonschema.compile(json_schema)(value)


def fast_compiled_without_exc(value, _):
fastjsonschema_validate_without_exc(value)


validator_class = jsonschema.validators.validator_for(JSON_SCHEMA)
validator = validator_class(JSON_SCHEMA)

Expand Down Expand Up @@ -118,6 +123,7 @@ def t(func, valid_values=True):
jsonschema,
jsonspec,
fast_compiled,
fast_compiled_without_exc,
fast_file,
fast_not_compiled,
jsonschema_compiled,
Expand All @@ -139,14 +145,17 @@ def t(func, valid_values=True):
""".format(func))

res = timeit.timeit(code, setup, number=NUMBER)
print('{:<20} {:<10} ==> {:10.7f}'.format(module, 'valid' if valid_values else 'invalid', res))
print('{:<30} {:<10} ==> {:10.7f}'.format(module, 'valid' if valid_values else 'invalid', res))


print('Number: {}'.format(NUMBER))

t('fast_compiled')
t('fast_compiled', valid_values=False)

t('fast_compiled_without_exc')
t('fast_compiled_without_exc', valid_values=False)

t('fast_file')
t('fast_file', valid_values=False)

Expand Down

0 comments on commit b0e2bcc

Please sign in to comment.