Skip to content

Commit 7d75408

Browse files
committed
Added IValidator interface plus tests
Still need to clean up docstrings, and understand zope.interface more to ensure I'm doing this right.
1 parent 806bda7 commit 7d75408

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

jsonschema.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
except ImportError:
3535
requests = None
3636

37+
try:
38+
from zope.interface import Interface, Attribute, implements
39+
HAS_ZOPE = True
40+
except ImportError:
41+
HAS_ZOPE = False
42+
3743
__version__ = "1.4.0-dev"
3844

3945
PY3 = sys.version_info[0] >= 3
@@ -216,6 +222,98 @@ def _validates(cls):
216222
return _validates
217223

218224

225+
if not HAS_ZOPE:
226+
IValidator = None
227+
else:
228+
class IValidator(Interface):
229+
"""
230+
:argument dict schema: the schema that the validator will
231+
validate with. It is assumed to be
232+
valid, and providing an invalid
233+
schema can lead to undefined
234+
behavior. See
235+
:meth:`IValidator.check_schema` to
236+
validate a schema first.
237+
:argument types: Override or extend the list of known types when validating the :validator:`type` property. Should map strings (type names) to class objects that will be checked via :func:`isinstance`. See :ref:`validating-types` for details.
238+
:type types: dict or iterable of 2-tuples
239+
:argument resolver: an instance of :class:`RefResolver` that will be used to resolve :validator:`$ref` properties (JSON references). If unprovided, one will be created.
240+
:argument format_checker: an instance of :class:`FormatChecker` whose :meth:`~conforms` method will be called to check and see if instances conform to each :validator:`format` property present in the schema. If unprovided, no validation will be done for :validator:`format`.
241+
242+
"""
243+
DEFAULT_TYPES = Attribute("""The default mapping of JSON types to Python types used when validating type properties in JSON schemas.""")
244+
META_SCHEMA = Attribute("""An object representing the validator's meta schema (the schema that describes valid schemas in the given version).""")
245+
schema = Attribute("""The schema that was passed in when initializing the validator.""")
246+
247+
def __init__(schema, types, resolver=None, format_checker=None):
248+
pass
249+
250+
#@classmethod
251+
def check_schema(schema):
252+
"""
253+
Validate the given schema against the validator's :attr:`META_SCHEMA`.
254+
255+
:raises: :exc:`SchemaError` if the schema is invalid
256+
257+
"""
258+
259+
def is_type(instance, type):
260+
"""
261+
Check if the instance is of the given (JSON Schema) type.
262+
263+
:type type: str
264+
:rtype: bool
265+
:raises: :exc:`UnknownType` if ``type`` is not a known type.
266+
267+
The special type ``"any"`` is valid for any given instance.
268+
269+
"""
270+
271+
def is_valid(instance):
272+
"""
273+
Check if the instance is valid under the current :attr:`schema`.
274+
275+
:rtype: bool
276+
277+
>>> schema = {"maxItems" : 2}
278+
>>> Draft3Validator(schema).is_valid([2, 3, 4])
279+
False
280+
281+
"""
282+
283+
def iter_errors(instance):
284+
"""
285+
Lazily yield each of the validation errors in the given instance.
286+
287+
:rtype: an iterable of :exc:`ValidationError`\s
288+
289+
>>> schema = {
290+
... "type" : "array",
291+
... "items" : {"enum" : [1, 2, 3]},
292+
... "maxItems" : 2,
293+
... }
294+
>>> v = Draft3Validator(schema)
295+
>>> for error in sorted(v.iter_errors([2, 3, 4]), key=str):
296+
... print(error.message)
297+
4 is not one of [1, 2, 3]
298+
[2, 3, 4] is too long
299+
300+
"""
301+
302+
def validate(instance):
303+
"""
304+
Check if the instance is valid under the current :attr:`schema`.
305+
306+
:raises: :exc:`ValidationError` if the instance is invalid
307+
308+
>>> schema = {"maxItems" : 2}
309+
>>> Draft3Validator(schema).validate([2, 3, 4])
310+
Traceback (most recent call last):
311+
...
312+
ValidationError: [2, 3, 4] is too long
313+
314+
"""
315+
316+
219317
class ValidatorMixin(object):
220318
"""
221319
Concrete implementation of :class:`IValidator`.
@@ -496,6 +594,8 @@ class Draft3Validator(ValidatorMixin, _Draft34CommonMixin, object):
496594
A validator for JSON Schema draft 3.
497595
498596
"""
597+
if HAS_ZOPE:
598+
implements(IValidator)
499599

500600
def validate_type(self, types, instance, schema):
501601
types = _list(types)
@@ -650,6 +750,8 @@ class Draft4Validator(ValidatorMixin, _Draft34CommonMixin, object):
650750
A validator for JSON Schema draft 4.
651751
652752
"""
753+
if HAS_ZOPE:
754+
implements(IValidator)
653755

654756
def validate_type(self, types, instance, schema):
655757
types = _list(types)

tests.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@
2929

3030
from jsonschema import (
3131
PY3, FormatError, RefResolutionError, SchemaError, UnknownType,
32-
ValidationError, ErrorTree, Draft3Validator, Draft4Validator,
32+
ValidationError, ErrorTree,
33+
IValidator, Draft3Validator, Draft4Validator,
3334
FormatChecker, RefResolver, ValidatorMixin, draft3_format_checker,
34-
draft4_format_checker, validate,
35+
draft4_format_checker, validate, HAS_ZOPE
3536
)
3637

38+
if HAS_ZOPE:
39+
from zope.interface import verify
40+
3741

3842
THIS_DIR = os.path.dirname(__file__)
3943
TESTS_DIR = os.path.join(THIS_DIR, "json", "tests")
@@ -1005,3 +1009,13 @@ def key(error):
10051009
[str(e) for e in error.schema_path]
10061010
)
10071011
return sorted(errors, key=key)
1012+
1013+
1014+
@unittest.skipIf(not HAS_ZOPE,
1015+
"Requires zope.interface to run")
1016+
class TestValidatorInterfaces(unittest.TestCase):
1017+
def test_draft_3_validator(self):
1018+
verify.verifyClass(IValidator, Draft3Validator)
1019+
1020+
def test_draft_4_validator(self):
1021+
verify.verifyClass(IValidator, Draft4Validator)

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ deps =
6060
pytest
6161
sphinx
6262
webcolors
63+
zope.interface

0 commit comments

Comments
 (0)