summaryrefslogtreecommitdiff
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
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.
-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):