Skip to content

Commit 1f756ee

Browse files
committed
Detect version from definition
1 parent 69aa835 commit 1f756ee

File tree

10 files changed

+71
-54
lines changed

10 files changed

+71
-54
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all venv lint test test-lf benchmark benchmark-save performance doc upload deb clean
1+
.PHONY: all venv lint jsonschemasuitcases test test-lf benchmark benchmark-save performance printcode doc upload deb clean
22
SHELL=/bin/bash
33

44
VENV_NAME?=venv

docs/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
# General information about the project.
2525
project = u'fastjsonschema'
26-
copyright = u'2016-{}, Seznam.cz'.format(time.strftime("%Y"))
26+
copyright = u'2016-{}, Michal Horejsek'.format(time.strftime("%Y"))
2727

2828
# The version info for the project you're documenting, acts as replacement for
2929
# |version| and |release|, also used in various other places throughout the

fastjsonschema/__init__.py

+47-38
Original file line numberDiff line numberDiff line change
@@ -23,48 +23,47 @@
2323
.. code-block:: bash
2424
2525
$ make performance
26-
fast_compiled valid ==> 0.026877017982769758
27-
fast_compiled invalid ==> 0.0015628149849362671
28-
fast_file valid ==> 0.025493122986517847
29-
fast_file invalid ==> 0.0012430319911800325
30-
fast_not_compiled valid ==> 4.790547857992351
31-
fast_not_compiled invalid ==> 1.2642899919883348
32-
jsonschema valid ==> 5.036152001994196
33-
jsonschema invalid ==> 1.1929481109953485
34-
jsonspec valid ==> 7.196442283981014
35-
jsonspec invalid ==> 1.7245555499684997
36-
validictory valid ==> 0.36818933801259845
37-
validictory invalid ==> 0.022672351042274386
38-
39-
This library follows and implements `JSON schema draft-04 <http://json-schema.org>`_. Sometimes
40-
it's not perfectly clear so I recommend also check out this `understaning json schema
41-
<https://spacetelescope.github.io/understanding-json-schema>`_.
26+
fast_compiled valid ==> 0.030474655970465392
27+
fast_compiled invalid ==> 0.0017561429995112121
28+
fast_file valid ==> 0.028758891974575818
29+
fast_file invalid ==> 0.0017655809642747045
30+
fast_not_compiled valid ==> 4.597834145999514
31+
fast_not_compiled invalid ==> 1.139162228035275
32+
jsonschema valid ==> 5.014410221017897
33+
jsonschema invalid ==> 1.1362981660058722
34+
jsonspec valid ==> 8.1144932230236
35+
jsonspec invalid ==> 2.0143173419637606
36+
validictory valid ==> 0.4084212710149586
37+
validictory invalid ==> 0.026061681972350925
38+
39+
This library follows and implements `JSON schema draft-04, draft-06 and draft-07
40+
<http://json-schema.org>`_. Sometimes it's not perfectly clear so I recommend also
41+
check out this `understaning json schema <https://spacetelescope.github.io/understanding-json-schema>`_.
4242
4343
Note that there are some differences compared to JSON schema standard:
4444
4545
* Regular expressions are full Python ones, not only what JSON schema allows. It's easier
4646
to allow everything and also it's faster to compile without limits. So keep in mind that when
47-
you will use more advanced regular expression, it may not work with other library.
47+
you will use more advanced regular expression, it may not work with other library or in
48+
other language.
4849
* JSON schema says you can use keyword ``default`` for providing default values. This implementation
4950
uses that and always returns transformed input data.
5051
5152
Support only for Python 3.3 and higher.
5253
"""
5354

54-
from os.path import exists
55-
56-
from .exceptions import JsonSchemaException
5755
from .draft04 import CodeGeneratorDraft04
5856
from .draft06 import CodeGeneratorDraft06
5957
from .draft07 import CodeGeneratorDraft07
58+
from .exceptions import JsonSchemaException
6059
from .ref_resolver import RefResolver
6160
from .version import VERSION
6261

6362
__all__ = ('VERSION', 'JsonSchemaException', 'compile', 'compile_to_code')
6463

6564

6665
# pylint: disable=redefined-builtin,dangerous-default-value,exec-used
67-
def compile(definition, version=7, handlers={}):
66+
def compile(definition, handlers={}):
6867
"""
6968
Generates validation function for validating JSON schema by ``definition``. Example:
7069
@@ -89,22 +88,31 @@ def compile(definition, version=7, handlers={}):
8988
data = validate({})
9089
assert data == {'a': 42}
9190
92-
Args:
93-
definition (dict): Json schema definition
94-
handlers (dict): A mapping from URI schemes to functions
95-
that should be used to retrieve them.
91+
Supported implementations are draft-04, draft-06 and draft-07. Which version
92+
should be used is determined by `$draft` in your ``definition``. When not
93+
specified, the latest implementation is used (draft-07).
94+
95+
.. code-block:: python
96+
97+
validate = fastjsonschema.compile({
98+
'$schema': 'http://json-schema.org/draft-04/schema',
99+
'type': 'number',
100+
})
101+
102+
You can pass mapping from URI to function that should be used to retrieve
103+
remote schemes used in your ``definition`` in parameter ``handlers``.
96104
97105
Exception :any:`JsonSchemaException` is thrown when validation fails.
98106
"""
99-
resolver, code_generator = _factory(definition, version, handlers)
107+
resolver, code_generator = _factory(definition, handlers)
100108
global_state = code_generator.global_state
101109
# Do not pass local state so it can recursively call itself.
102110
exec(code_generator.func_code, global_state)
103111
return global_state[resolver.get_scope_name()]
104112

105113

106114
# pylint: disable=dangerous-default-value
107-
def compile_to_code(definition, version=7, handlers={}):
115+
def compile_to_code(definition, handlers={}):
108116
"""
109117
Generates validation function for validating JSON schema by ``definition``
110118
and returns compiled code. Example:
@@ -126,25 +134,26 @@ def compile_to_code(definition, version=7, handlers={}):
126134
127135
Exception :any:`JsonSchemaException` is thrown when validation fails.
128136
"""
129-
_, code_generator = _factory(definition, version, handlers)
137+
_, code_generator = _factory(definition, handlers)
130138
return (
131139
'VERSION = "' + VERSION + '"\n' +
132140
code_generator.global_state_code + '\n' +
133141
code_generator.func_code
134142
)
135143

136144

137-
def _factory(definition, version, handlers):
145+
def _factory(definition, handlers):
138146
resolver = RefResolver.from_schema(definition, handlers=handlers)
139-
code_generator = _get_code_generator_class(version)(definition, resolver=resolver)
147+
code_generator = _get_code_generator_class(definition)(definition, resolver=resolver)
140148
return resolver, code_generator
141149

142150

143-
def _get_code_generator_class(version):
144-
if version == 4:
145-
return CodeGeneratorDraft04
146-
if version == 6:
147-
return CodeGeneratorDraft06
148-
if version == 7:
149-
return CodeGeneratorDraft07
150-
raise JsonSchemaException('Unsupported JSON schema version. Supported are 4, 6 and 7.')
151+
def _get_code_generator_class(schema):
152+
# Schema in from draft-06 can be just the boolean value.
153+
if isinstance(schema, dict):
154+
schema_version = schema.get('$schema', '')
155+
if 'draft-04' in schema_version:
156+
return CodeGeneratorDraft04
157+
if 'draft-06' in schema_version:
158+
return CodeGeneratorDraft06
159+
return CodeGeneratorDraft07

fastjsonschema/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '1.6'
1+
VERSION = '2.0dev'

performance.py

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
NUMBER = 1000
1212

1313
JSON_SCHEMA = {
14+
'$schema': 'http://json-schema.org/draft-04/schema#',
1415
'type': 'array',
1516
'items': [
1617
{

tests/conftest.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ def f(definition, value, expected):
2121
print(code_generator.func_code)
2222
pprint(code_generator.global_state)
2323

24-
validator = compile(definition, version=4)
24+
# By default old tests are written for draft-04.
25+
definition.setdefault('$schema', 'http://json-schema.org/draft-04/schema')
26+
27+
validator = compile(definition)
2528
if isinstance(expected, JsonSchemaException):
2629
with pytest.raises(JsonSchemaException) as exc:
2730
validator(value)

tests/json_schema/test_draft04.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55

66
def pytest_generate_tests(metafunc):
77
param_values, param_ids = resolve_param_values_and_ids(
8-
version=4,
8+
schema_version='http://json-schema.org/draft-04/schema',
99
suite_dir='JSON-Schema-Test-Suite/tests/draft4',
1010
ignored_suite_files=[
1111
# Optional.
1212
'ecmascript-regex.json',
1313
],
14-
ignore_tests=[],
1514
)
16-
metafunc.parametrize(['version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
15+
metafunc.parametrize(['schema_version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
1716

1817

1918
# Real test function to be used with parametrization by previous hook function.

tests/json_schema/test_draft06.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
def pytest_generate_tests(metafunc):
77
param_values, param_ids = resolve_param_values_and_ids(
8-
version=6,
8+
schema_version='http://json-schema.org/draft-06/schema',
99
suite_dir='JSON-Schema-Test-Suite/tests/draft6',
1010
ignored_suite_files=[
1111
# Optional.
1212
'ecmascript-regex.json',
1313
],
1414
)
15-
metafunc.parametrize(['version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
15+
metafunc.parametrize(['schema_version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
1616

1717

1818
# Real test function to be used with parametrization by previous hook function.

tests/json_schema/test_draft07.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
def pytest_generate_tests(metafunc):
77
param_values, param_ids = resolve_param_values_and_ids(
8-
version=7,
8+
schema_version='http://json-schema.org/draft-07/schema',
99
suite_dir='JSON-Schema-Test-Suite/tests/draft7',
1010
ignored_suite_files=[
1111
# Optional.
1212
'ecmascript-regex.json',
1313
'idn-hostname.json',
1414
],
1515
)
16-
metafunc.parametrize(['version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
16+
metafunc.parametrize(['schema_version', 'schema', 'data', 'is_valid'], param_values, ids=param_ids)
1717

1818

1919
# Real test function to be used with parametrization by previous hook function.

tests/json_schema/utils.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def remotes_handler(uri):
2929
return requests.get(uri).json()
3030

3131

32-
def resolve_param_values_and_ids(version, suite_dir, ignored_suite_files=[], ignore_tests=[]):
32+
def resolve_param_values_and_ids(schema_version, suite_dir, ignored_suite_files=[], ignore_tests=[]):
3333
suite_dir_path = Path(suite_dir).resolve()
3434
test_file_paths = sorted(set(suite_dir_path.glob("**/*.json")))
3535

@@ -41,7 +41,7 @@ def resolve_param_values_and_ids(version, suite_dir, ignored_suite_files=[], ign
4141
for test_case in test_cases:
4242
for test_data in test_case['tests']:
4343
param_values.append(pytest.param(
44-
version,
44+
schema_version,
4545
test_case['schema'],
4646
test_data['data'],
4747
test_data['valid'],
@@ -58,16 +58,21 @@ def resolve_param_values_and_ids(version, suite_dir, ignored_suite_files=[], ign
5858
return param_values, param_ids
5959

6060

61-
def template_test(version, schema, data, is_valid):
61+
def template_test(schema_version, schema, data, is_valid):
6262
"""
6363
Test function to be used (imported) in final test file to run the tests
6464
which are generated by `pytest_generate_tests` hook.
6565
"""
6666
# For debug purposes. When test fails, it will print stdout.
6767
resolver = RefResolver.from_schema(schema, handlers={'http': remotes_handler})
68-
print(_get_code_generator_class(version)(schema, resolver=resolver).func_code)
68+
print(_get_code_generator_class(schema_version)(schema, resolver=resolver).func_code)
6969

70-
validate = compile(schema, version=version, handlers={'http': remotes_handler})
70+
# JSON schema test suits do not contain schema version.
71+
# Our library needs to know that or it would use always the latest implementation.
72+
if isinstance(schema, dict):
73+
schema.setdefault('$schema', schema_version)
74+
75+
validate = compile(schema, handlers={'http': remotes_handler})
7176
try:
7277
result = validate(data)
7378
print('Validate result:', result)

0 commit comments

Comments
 (0)