summaryrefslogtreecommitdiff
path: root/jsonschema
diff options
context:
space:
mode:
authorJulian Berman <Julian@GrayVines.com>2013-05-20 10:18:22 -0400
committerJulian Berman <Julian@GrayVines.com>2013-05-20 10:30:47 -0400
commit4d750d6845381092afca86e3d667671398e37943 (patch)
tree0007496bc2953afc71a66579ccf2f7c630a0d831 /jsonschema
parent7221a3229b378a3cdb08f9bd5c4a94688af8d559 (diff)
downloadjsonschema-4d750d6845381092afca86e3d667671398e37943.tar.gz
Make everything 5x slower.
Add jsonschema.validators.create, and reimplement ValidatorMixin in terms of it. This makes all of the validators much much slower, but stay tuned for magic.
Diffstat (limited to 'jsonschema')
-rw-r--r--jsonschema/tests/test_validators.py42
-rw-r--r--jsonschema/validators.py177
2 files changed, 135 insertions, 84 deletions
diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py
index 2fd1d91..219404a 100644
--- a/jsonschema/tests/test_validators.py
+++ b/jsonschema/tests/test_validators.py
@@ -9,10 +9,50 @@ from jsonschema.compat import PY3
from jsonschema.tests.compat import mock, unittest
from jsonschema.validators import (
RefResolutionError, UnknownType, ValidationError, ErrorTree,
- Draft3Validator, Draft4Validator, RefResolver, ValidatorMixin, validate,
+ Draft3Validator, Draft4Validator, RefResolver, ValidatorMixin,
+ create, validate,
)
+class TestCreate(unittest.TestCase):
+ def setUp(self):
+ self.meta_schema = {"properties" : {"smelly" : {}}}
+ self.smelly = mock.MagicMock()
+ self.validators = {"smelly" : self.smelly}
+ self.types = {"dict" : dict}
+ self.Validator = create(
+ meta_schema=self.meta_schema,
+ validators=self.validators,
+ default_types=self.types,
+ )
+
+ self.validator_value = 12
+ self.schema = {"smelly" : self.validator_value}
+ self.validator = self.Validator(self.schema)
+
+ def test_attrs(self):
+ self.assertEqual(self.Validator.VALIDATORS, self.validators)
+ self.assertEqual(self.Validator.META_SCHEMA, self.meta_schema)
+ self.assertEqual(self.Validator.DEFAULT_TYPES, self.types)
+
+ def test_init(self):
+ self.assertEqual(self.validator.schema, self.schema)
+
+ def test_iter_errors(self):
+ instance = "hello"
+
+ self.smelly.return_value = []
+ self.assertEqual(list(self.validator.iter_errors(instance)), [])
+
+ error = mock.Mock()
+ self.smelly.return_value = [error]
+ self.assertEqual(list(self.validator.iter_errors(instance)), [error])
+
+ self.smelly.assert_called_with(
+ self.validator_value, instance, self.schema,
+ )
+
+
class TestIterErrors(unittest.TestCase):
def setUp(self):
self.validator = Draft3Validator({})
diff --git a/jsonschema/validators.py b/jsonschema/validators.py
index 36601d5..e513aa3 100644
--- a/jsonschema/validators.py
+++ b/jsonschema/validators.py
@@ -136,99 +136,110 @@ def validates(version):
return _validates
-class ValidatorMixin(object):
- """
- Concrete implementation of :class:`IValidator`.
-
- Provides default implementations of each method. Validation of schema
- properties is dispatched to ``validate_property`` methods. E.g., to
- implement a validator for a ``maximum`` property, create a
- ``validate_maximum`` method. Validator methods should yield zero or more
- :exc:`ValidationError``\s to signal failed validation.
+def create(meta_schema, validators=(), default_types=None): # noqa
+ if default_types is None:
+ default_types = {
+ "array" : list, "boolean" : bool, "integer" : int_types,
+ "null" : type(None), "number" : numbers.Number, "object" : dict,
+ "string" : str_types,
+ }
+
+ class Validator(object):
+ VALIDATORS = dict(validators)
+ META_SCHEMA = dict(meta_schema)
+ DEFAULT_TYPES = dict(default_types)
+
+ def __init__(
+ self, schema, types=(), resolver=None, format_checker=None,
+ ):
+ self._types = dict(self.DEFAULT_TYPES)
+ self._types.update(types)
+
+ if resolver is None:
+ resolver = RefResolver.from_schema(schema)
+
+ self.resolver = resolver
+ self.format_checker = format_checker
+ self.schema = schema
+
+ @classmethod
+ def check_schema(cls, schema):
+ for error in cls(cls.META_SCHEMA).iter_errors(schema):
+ raise SchemaError.create_from(error)
+
+ def iter_errors(self, instance, _schema=None):
+ if _schema is None:
+ _schema = self.schema
+
+ with self.resolver.in_scope(_schema.get("id", "")):
+ ref = _schema.get("$ref")
+ if ref is not None:
+ validators = [("$ref", ref)]
+ else:
+ validators = iteritems(_schema)
+
+ for k, v in validators:
+ validator = self.VALIDATORS.get(k)
+ if validator is None:
+ continue
+
+ errors = validator(v, instance, _schema) or ()
+ for error in errors:
+ # set details if not already set by the called fn
+ error._set(
+ validator=k,
+ validator_value=v,
+ instance=instance,
+ schema=_schema,
+ )
+ if k != "$ref":
+ error.schema_path.appendleft(k)
+ yield error
- """
+ def descend(self, instance, schema, path=None, schema_path=None):
+ for error in self.iter_errors(instance, schema):
+ if path is not None:
+ error.path.appendleft(path)
+ if schema_path is not None:
+ error.schema_path.appendleft(schema_path)
+ yield error
- DEFAULT_TYPES = {
- "array" : list, "boolean" : bool, "integer" : int_types,
- "null" : type(None), "number" : numbers.Number, "object" : dict,
- "string" : str_types,
- }
+ def validate(self, *args, **kwargs):
+ for error in self.iter_errors(*args, **kwargs):
+ raise error
- def __init__(self, schema, types=(), resolver=None, format_checker=None):
- self._types = dict(self.DEFAULT_TYPES)
- self._types.update(types)
+ def is_type(self, instance, type):
+ if type not in self._types:
+ raise UnknownType(type)
+ pytypes = self._types[type]
- if resolver is None:
- resolver = RefResolver.from_schema(schema)
+ # bool inherits from int, so ensure bools aren't reported as ints
+ if isinstance(instance, bool):
+ pytypes = _utils.flatten(pytypes)
+ is_number = any(
+ issubclass(pytype, numbers.Number) for pytype in pytypes
+ )
+ if is_number and bool not in pytypes:
+ return False
+ return isinstance(instance, pytypes)
- self.resolver = resolver
- self.format_checker = format_checker
- self.schema = schema
+ def is_valid(self, instance, _schema=None):
+ error = next(self.iter_errors(instance, _schema), None)
+ return error is None
- def is_type(self, instance, type):
- if type not in self._types:
- raise UnknownType(type)
- pytypes = self._types[type]
+ return Validator
- # bool inherits from int, so ensure bools aren't reported as integers
- if isinstance(instance, bool):
- pytypes = _utils.flatten(pytypes)
- num = any(issubclass(pytype, numbers.Number) for pytype in pytypes)
- if num and bool not in pytypes:
- return False
- return isinstance(instance, pytypes)
- def is_valid(self, instance, _schema=None):
- error = next(self.iter_errors(instance, _schema), None)
- return error is None
+class ValidatorMixin(create(meta_schema={})):
+ def __init__(self, *args, **kwargs):
+ super(ValidatorMixin, self).__init__(*args, **kwargs)
- @classmethod
- def check_schema(cls, schema):
- for error in cls(cls.META_SCHEMA).iter_errors(schema):
- raise SchemaError.create_from(error)
-
- def iter_errors(self, instance, _schema=None):
- if _schema is None:
- _schema = self.schema
-
- with self.resolver.in_scope(_schema.get("id", "")):
- ref = _schema.get("$ref")
- if ref is not None:
- validators = [("$ref", ref)]
- else:
- validators = iteritems(_schema)
-
- for k, v in validators:
- validator_attr = "validate_%s" % (k.lstrip("$"),)
- validator = getattr(self, validator_attr, None)
-
- if validator is None:
- continue
-
- errors = validator(v, instance, _schema) or ()
- for error in errors:
- # set details if they weren't already set by the called fn
- error._set(
- validator=k,
- validator_value=v,
- instance=instance,
- schema=_schema,
- )
- if k != "$ref":
- error.schema_path.appendleft(k)
- yield error
+ class _VALIDATORS(dict):
+ def __missing__(this, key, dflt=None):
+ return getattr(self, "validate_" + str(key).lstrip("$"), dflt)
+ get = __missing__
- def descend(self, instance, schema, path=None, schema_path=None):
- for error in self.iter_errors(instance, schema):
- if path is not None:
- error.path.appendleft(path)
- if schema_path is not None:
- error.schema_path.appendleft(schema_path)
- yield error
-
- def validate(self, *args, **kwargs):
- for error in self.iter_errors(*args, **kwargs):
- raise error
+ self.VALIDATORS = _VALIDATORS()
class _Draft34CommonMixin(object):