diff --git a/.travis.yml b/.travis.yml index e1d4ee978..25fc932d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,11 @@ language: python sudo: false -addons: - apt: - sources: - - deadsnakes - packages: - - python3.5 python: - "2.7" + - "3.5" + - "pypy" + - "pypy3" install: - - pip install tox + - pip install tox-travis script: - tox diff --git a/README.rst b/README.rst index c8bf33afc..9989a1f1c 100644 --- a/README.rst +++ b/README.rst @@ -14,22 +14,22 @@ for Python (supporting 2.7+ including Python 3). .. code-block:: python - >>> from jsonschema import validate + >>> from jsonschema import Schema >>> # A sample schema, like what we'd get from json.load() - >>> schema = { + >>> schema = Schema({ ... "type" : "object", ... "properties" : { ... "price" : {"type" : "number"}, ... "name" : {"type" : "string"}, ... }, - ... } + ... }) >>> # If no exception is raised by validate(), the instance is valid. - >>> validate({"name" : "Eggs", "price" : 34.99}, schema) + >>> schema.validate({"name" : "Eggs", "price" : 34.99}) - >>> validate( - ... {"name" : "Eggs", "price" : "Invalid"}, schema + >>> schema.validate( + ... {"name" : "Eggs", "price" : "Invalid"} ... ) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... diff --git a/docs/index.rst b/docs/index.rst index 93dc05801..675ffed76 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,22 +11,22 @@ for Python (supporting 2.7+ including Python 3). .. code-block:: python - >>> from jsonschema import validate + >>> from jsonschema import Schema >>> # A sample schema, like what we'd get from json.load() - >>> schema = { + >>> schema = Schema({ ... "type" : "object", ... "properties" : { ... "price" : {"type" : "number"}, ... "name" : {"type" : "string"}, ... }, - ... } + ... }) >>> # If no exception is raised by validate(), the instance is valid. - >>> validate({"name" : "Eggs", "price" : 34.99}, schema) + >>> schema.validate({"name" : "Eggs", "price" : 34.99}) - >>> validate( - ... {"name" : "Eggs", "price" : "Invalid"}, schema + >>> schema.validate( + ... {"name" : "Eggs", "price" : "Invalid"} ... ) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... diff --git a/jsonschema/__init__.py b/jsonschema/__init__.py index baf1d89b3..6b6f46776 100644 --- a/jsonschema/__init__.py +++ b/jsonschema/__init__.py @@ -16,7 +16,7 @@ FormatChecker, draft3_format_checker, draft4_format_checker, ) from jsonschema.validators import ( - Draft3Validator, Draft4Validator, RefResolver, validate + Draft3Validator, Draft4Validator, RefResolver, validate, Schema ) from jsonschema._version import __version__ diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index da3e85274..87b8e0041 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -85,7 +85,7 @@ def __unicode__(self): __str__ = __unicode__ else: def __str__(self): - return unicode(self).encode("utf-8") + return self.__unicode__().encode("utf-8") @classmethod def create_from(cls, other): @@ -158,7 +158,7 @@ def __unicode__(self): __str__ = __unicode__ else: def __str__(self): - return unicode(self).encode("utf-8") + return self.__unicode__().encode("utf-8") class FormatError(Exception): @@ -174,7 +174,7 @@ def __unicode__(self): __str__ = __unicode__ else: def __str__(self): - return self.message.encode("utf-8") + return self.__unicode__().encode("utf-8") class ErrorTree(object): diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 9a9d1cfb4..c260e1d70 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -7,6 +7,7 @@ from jsonschema.validators import ( RefResolutionError, UnknownType, Draft3Validator, Draft4Validator, RefResolver, create, extend, validator_for, validate, + Schema, ) @@ -759,6 +760,14 @@ def test_draft4_validator_is_the_default(self): chk_schema.assert_called_once_with({}) +class TestSchemaCls(unittest.TestCase): + def test_draft4_validator_is_the_default_for_schema_cls(self): + with mock.patch.object(Draft4Validator, "check_schema") as chk_schema: + schema = Schema({}) + schema.validate({}) + chk_schema.assert_called_once_with({}) + + class TestRefResolver(unittest.TestCase): base_uri = "" diff --git a/jsonschema/validators.py b/jsonschema/validators.py index ee1ec94b5..80c6e3c54 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -539,3 +539,36 @@ def validate(instance, schema, cls=None, *args, **kwargs): cls = validator_for(schema) cls.check_schema(schema) cls(schema, *args, **kwargs).validate(instance) + + +class Schema(object): + """ + Similar to :func:`validate`, except the :class:`IValidator` is initialized + only once when this class is initialized. This can be useful for improving + the performance of validation in loops. + + :argument schema: the schema to validate with + :argument validator_cls: an :class:`IValidator` class that will be used by + :meth:`validate` to validate the instance. + + :raises: + :exc:`SchemaError` if the schema itself is invalid + """ + + def __init__(self, schema, validator_cls=None, *args, **kwargs): + if validator_cls is None: + validator_cls = validator_for(schema) + validator_cls.check_schema(schema) + self.validator = validator_cls(schema, *args, **kwargs) + + def validate(self, instance): + """ + Validate an instance with the schema and :class:`IValidator` class used + in this instance of :class:`Schema`. + + :argument instance: the instance to validate + + :raises: + :exc:`ValidationError` if the instance is invalid + """ + self.validator.validate(instance) diff --git a/tox.ini b/tox.ini index b13111918..03eaf3c0f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,9 @@ [tox] envlist = py{27,35,py,py3}, docs, style +[tox:travis] +2.7 = py27, style +3.5 = py35, docs, style [testenv] setenv =