Hello community, here is the log from the commit of package python3-jsonschema for openSUSE:Factory checked in at 2014-05-02 14:02:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-jsonschema (Old) and /work/SRC/openSUSE:Factory/.python3-jsonschema.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python3-jsonschema" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-jsonschema/python3-jsonschema.changes 2013-06-25 18:00:14.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-jsonschema.new/python3-jsonschema.changes 2014-05-02 14:02:37.000000000 +0200 @@ -1,0 +2,13 @@ +Mon Apr 28 10:11:29 UTC 2014 - andrea@opensuse.org + +- Update to version 2.3.0: + * Added by_relevance and best_match (#91) + * Fixed ``format`` to allow adding formats for non-strings (#125) + * Fixed the ``uri`` format to reject URI references (#131) + * Compile the host name regex (#127) + * Allow arbitrary objects to be types (#129) + * Support RFC 3339 datetimes in conformance with the spec + * Fixed error paths for additionalItems + items (#122) + * Fixed wording for min / maxProperties (#117) + +------------------------------------------------------------------- Old: ---- jsonschema-2.0.0.tar.gz New: ---- jsonschema-2.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-jsonschema.spec ++++++ --- /var/tmp/diff_new_pack.5CkoQw/_old 2014-05-02 14:02:38.000000000 +0200 +++ /var/tmp/diff_new_pack.5CkoQw/_new 2014-05-02 14:02:38.000000000 +0200 @@ -17,7 +17,7 @@ Name: python3-jsonschema -Version: 2.0.0 +Version: 2.3.0 Release: 0 Summary: An implementation of JSON-Schema validation for Python License: MIT ++++++ jsonschema-2.0.0.tar.gz -> jsonschema-2.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/CHANGELOG.rst new/jsonschema-2.3.0/CHANGELOG.rst --- old/jsonschema-2.0.0/CHANGELOG.rst 2013-05-21 15:36:50.000000000 +0200 +++ new/jsonschema-2.3.0/CHANGELOG.rst 2013-11-03 01:52:27.000000000 +0100 @@ -1,3 +1,24 @@ +v2.3.0 +------ + +* Added by_relevance and best_match (#91) +* Fixed ``format`` to allow adding formats for non-strings (#125) +* Fixed the ``uri`` format to reject URI references (#131) + +v2.2.0 +------ + +* Compile the host name regex (#127) +* Allow arbitrary objects to be types (#129) + +v2.1.0 +------ + +* Support RFC 3339 datetimes in conformance with the spec +* Fixed error paths for additionalItems + items (#122) +* Fixed wording for min / maxProperties (#117) + + v2.0.0 ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/PKG-INFO new/jsonschema-2.3.0/PKG-INFO --- old/jsonschema-2.0.0/PKG-INFO 2013-05-21 15:41:34.000000000 +0200 +++ new/jsonschema-2.3.0/PKG-INFO 2013-11-03 02:06:30.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: jsonschema -Version: 2.0.0 +Version: 2.3.0 Summary: An implementation of JSON Schema validation for Python Home-page: http://github.com/Julian/jsonschema Author: Julian Berman @@ -57,21 +57,33 @@ Release Notes ------------- - ``v2.0.0`` adds a better interface for creating and extending validators in the - form of ``jsonschema.validators.create`` and ``jsonschema.validators.extend``. - The documentation is still a bit lacking in this area but it's getting there. - See the tests in ``jsonschema.tests.test_validators`` and the source code if - you'd like to try it out now. ``ValidatorMixin`` has been removed. - - Practically speaking, this affects validators that subclassed a built-in - validator and extended a validator function (presumably with an upcall via - ``super``), as the correct way to do so is now to call - ``TheValidator.VALIDATORS["extended_validator_fn"]`` directly in a new - validator function (and of course to use ``create``). Examples hopefully coming - soon if more clarification is needed. Patches welcome of course. + ``v2.3.0`` removes the (improper) limitation of ``format`` to strings. It also + adds the `jsonschema.exceptions.best_match <https://python-jsonschema.readthedocs.org/en/latest/errors/#best-match-and-by-relevance>`_ + function which can be used to guess at the best matching single validation + error for a given instance. - It also fixes a number of issues with ref resolution, one for array indices - (#95) and one for improper handling of unknown URI schemes (#102). + + .. code-block:: python + + >>> from jsonschema.validators import Draft4Validator + >>> from jsonschema.exceptions import best_match + + >>> schema = { + ... "properties" : { + ... "foo" : {"type" : "string"}, + ... "bar" : {"properties" : {"baz": {"type": "string"}}}, + ... }, + ... } + >>> instance = {"foo" : 12, "bar": {"baz" : 19}} + >>> print(best_match(Draft4Validator(schema).iter_errors(instance)).path) + deque(['foo']) + + + where the error closer to the top of the instance in ``foo`` was selected + as being more relevant. + + Also, URI references are now properly rejected by the URI format validator + (i.e., it now only accepts full URIs, as defined in the specification). Running the Test Suite @@ -95,7 +107,8 @@ Community --------- - There's a `mailing list <https://groups.google.com/forum/#!forum/jsonschema>`_ for this implementation on Google Groups. + There's a `mailing list <https://groups.google.com/forum/#!forum/jsonschema>`_ + for this implementation on Google Groups. Please join, and feel free to send questions there. @@ -113,6 +126,10 @@ You can also generally find me on Freenode (nick: ``tos9``) in various channels, including ``#python``. + If you feel overwhelmingly grateful, you can woo me with beer money on + `Gittip <https://www.gittip.com/Julian/>`_ or via Google Wallet with the email + in my GitHub profile. + Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/README.rst new/jsonschema-2.3.0/README.rst --- old/jsonschema-2.0.0/README.rst 2013-05-21 15:36:23.000000000 +0200 +++ new/jsonschema-2.3.0/README.rst 2013-11-03 01:53:04.000000000 +0100 @@ -49,21 +49,33 @@ Release Notes ------------- -``v2.0.0`` adds a better interface for creating and extending validators in the -form of ``jsonschema.validators.create`` and ``jsonschema.validators.extend``. -The documentation is still a bit lacking in this area but it's getting there. -See the tests in ``jsonschema.tests.test_validators`` and the source code if -you'd like to try it out now. ``ValidatorMixin`` has been removed. - -Practically speaking, this affects validators that subclassed a built-in -validator and extended a validator function (presumably with an upcall via -``super``), as the correct way to do so is now to call -``TheValidator.VALIDATORS["extended_validator_fn"]`` directly in a new -validator function (and of course to use ``create``). Examples hopefully coming -soon if more clarification is needed. Patches welcome of course. +``v2.3.0`` removes the (improper) limitation of ``format`` to strings. It also +adds the `jsonschema.exceptions.best_match <https://python-jsonschema.readthedocs.org/en/latest/errors/#best-match-and-by-relevance>`_ +function which can be used to guess at the best matching single validation +error for a given instance. -It also fixes a number of issues with ref resolution, one for array indices -(#95) and one for improper handling of unknown URI schemes (#102). + +.. code-block:: python + + >>> from jsonschema.validators import Draft4Validator + >>> from jsonschema.exceptions import best_match + + >>> schema = { + ... "properties" : { + ... "foo" : {"type" : "string"}, + ... "bar" : {"properties" : {"baz": {"type": "string"}}}, + ... }, + ... } + >>> instance = {"foo" : 12, "bar": {"baz" : 19}} + >>> print(best_match(Draft4Validator(schema).iter_errors(instance)).path) + deque(['foo']) + + +where the error closer to the top of the instance in ``foo`` was selected +as being more relevant. + +Also, URI references are now properly rejected by the URI format validator +(i.e., it now only accepts full URIs, as defined in the specification). Running the Test Suite @@ -87,7 +99,8 @@ Community --------- -There's a `mailing list <https://groups.google.com/forum/#!forum/jsonschema>`_ for this implementation on Google Groups. +There's a `mailing list <https://groups.google.com/forum/#!forum/jsonschema>`_ +for this implementation on Google Groups. Please join, and feel free to send questions there. @@ -104,3 +117,7 @@ You can also generally find me on Freenode (nick: ``tos9``) in various channels, including ``#python``. + +If you feel overwhelmingly grateful, you can woo me with beer money on +`Gittip <https://www.gittip.com/Julian/>`_ or via Google Wallet with the email +in my GitHub profile. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/README.md new/jsonschema-2.3.0/json/README.md --- old/jsonschema-2.0.0/json/README.md 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/README.md 2013-10-28 02:05:58.000000000 +0100 @@ -52,9 +52,8 @@ Coverage -------- -Currently, draft 3 should have essentially full coverage for the core schema. - -The beginnings of draft 4 are underway. +Draft 3 and 4 should have full coverage. If you see anything missing or think +there is a useful test missing, please send a pull request or open an issue. Who Uses the Test Suite ----------------------- @@ -67,7 +66,11 @@ * [direct-schema (javascript)](https://github.com/IreneKnapp/direct-schema) * [jsonschema (javascript)](https://github.com/tdegrunt/jsonschema) * [JaySchema (javascript)](https://github.com/natesilva/jayschema) + * [z-schema (javascript)](https://github.com/zaggino/z-schema) * [jesse (Erlang)](https://github.com/klarna/jesse) + * [json-schema (PHP)](https://github.com/justinrainbow/json-schema) + * [gojsonschema (Go)](https://github.com/sigu-399/gojsonschema) + * [json_schema (Dart)](https://github.com/patefacio/json_schema) If you use it as well, please fork and send a pull request adding yourself to the list :). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/bin/jsonschema_suite new/jsonschema-2.3.0/json/bin/jsonschema_suite --- old/jsonschema-2.0.0/json/bin/jsonschema_suite 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/bin/jsonschema_suite 2013-10-28 02:05:58.000000000 +0100 @@ -32,6 +32,10 @@ import jsonschema except ImportError: jsonschema = None +else: + validators = getattr( + jsonschema.validators, "validators", jsonschema.validators + ) ROOT_DIR = os.path.join( @@ -138,8 +142,8 @@ @unittest.skipIf(jsonschema is None, "Validation library not present!") def test_all_schemas_are_valid(self): for schema in os.listdir(SUITE_ROOT_DIR): - schema_validator = jsonschema.validators.get(schema) - if schema_validator: + schema_validator = validators.get(schema) + if schema_validator is not None: test_files = collect(os.path.join(SUITE_ROOT_DIR, schema)) for case in cases(test_files): try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/tests/draft3/additionalProperties.json new/jsonschema-2.3.0/json/tests/draft3/additionalProperties.json --- old/jsonschema-2.0.0/json/tests/draft3/additionalProperties.json 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/tests/draft3/additionalProperties.json 2013-10-28 02:05:58.000000000 +0100 @@ -4,6 +4,7 @@ "additionalProperties being false does not allow other properties", "schema": { "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, "additionalProperties": false }, "tests": [ @@ -21,6 +22,11 @@ "description": "ignores non-objects", "data": [1, 2, 3], "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true } ] }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/tests/draft3/optional/format.json new/jsonschema-2.3.0/json/tests/draft3/optional/format.json --- old/jsonschema-2.0.0/json/tests/draft3/optional/format.json 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/tests/draft3/optional/format.json 2013-10-28 02:05:58.000000000 +0100 @@ -28,6 +28,11 @@ "description": "an invalid date-time string", "data": "06/19/1963 08:30:06 PST", "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false } ] }, @@ -76,6 +81,11 @@ "description": "an invalid URI", "data": "\\\\WINDOWS\\fileshare", "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false } ] }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/tests/draft4/additionalProperties.json new/jsonschema-2.3.0/json/tests/draft4/additionalProperties.json --- old/jsonschema-2.0.0/json/tests/draft4/additionalProperties.json 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/tests/draft4/additionalProperties.json 2013-10-28 02:05:58.000000000 +0100 @@ -4,6 +4,7 @@ "additionalProperties being false does not allow other properties", "schema": { "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, "additionalProperties": false }, "tests": [ @@ -21,6 +22,11 @@ "description": "ignores non-objects", "data": [1, 2, 3], "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true } ] }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/json/tests/draft4/optional/format.json new/jsonschema-2.3.0/json/tests/draft4/optional/format.json --- old/jsonschema-2.0.0/json/tests/draft4/optional/format.json 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/json/tests/draft4/optional/format.json 2013-10-28 02:05:58.000000000 +0100 @@ -12,6 +12,11 @@ "description": "an invalid date-time string", "data": "06/19/1963 08:30:06 PST", "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false } ] }, @@ -28,6 +33,11 @@ "description": "an invalid URI", "data": "\\\\WINDOWS\\fileshare", "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false } ] }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/__init__.py new/jsonschema-2.3.0/jsonschema/__init__.py --- old/jsonschema-2.0.0/jsonschema/__init__.py 2013-05-21 15:32:14.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/__init__.py 2013-11-03 01:08:57.000000000 +0100 @@ -10,17 +10,17 @@ """ from jsonschema.exceptions import ( - FormatError, RefResolutionError, SchemaError, ValidationError + ErrorTree, FormatError, RefResolutionError, SchemaError, ValidationError ) from jsonschema._format import ( FormatChecker, draft3_format_checker, draft4_format_checker, ) from jsonschema.validators import ( - ErrorTree, Draft3Validator, Draft4Validator, RefResolver, validate + Draft3Validator, Draft4Validator, RefResolver, validate ) -__version__ = "2.0.0" +__version__ = "2.3.0" # flake8: noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/_format.py new/jsonschema-2.3.0/jsonschema/_format.py --- old/jsonschema-2.0.0/jsonschema/_format.py 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/_format.py 2013-10-30 15:34:34.000000000 +0100 @@ -2,6 +2,7 @@ import re import socket +from jsonschema.compat import str_types from jsonschema.exceptions import FormatError @@ -64,17 +65,19 @@ """ - if format in self.checkers: - func, raises = self.checkers[format] - result, cause = None, None - try: - result = func(instance) - except raises as e: - cause = e - if not result: - raise FormatError( - "%r is not a %r" % (instance, format), cause=cause, - ) + if format not in self.checkers: + return + + func, raises = self.checkers[format] + result, cause = None, None + try: + result = func(instance) + except raises as e: + cause = e + if not result: + raise FormatError( + "%r is not a %r" % (instance, format), cause=cause, + ) def conforms(self, instance, format): """ @@ -115,24 +118,33 @@ @_checks_drafts("email") def is_email(instance): + if not isinstance(instance, str_types): + return True return "@" in instance -_checks_drafts(draft3="ip-address", draft4="ipv4", raises=socket.error)( - socket.inet_aton -) +@_checks_drafts(draft3="ip-address", draft4="ipv4", raises=socket.error) +def is_ipv4(instance): + if not isinstance(instance, str_types): + return True + return socket.inet_aton(instance) if hasattr(socket, "inet_pton"): @_checks_drafts("ipv6", raises=socket.error) def is_ipv6(instance): + if not isinstance(instance, str_types): + return True return socket.inet_pton(socket.AF_INET6, instance) +_host_name_re = re.compile(r"^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$") + @_checks_drafts(draft3="host-name", draft4="hostname") def is_host_name(instance): - pattern = "^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$" - if not re.match(pattern, instance): + if not isinstance(instance, str_types): + return True + if not _host_name_re.match(instance): return False components = instance.split(".") for component in components: @@ -148,28 +160,50 @@ else: @_checks_drafts("uri", raises=ValueError) def is_uri(instance): - return rfc3987.parse(instance, rule="URI_reference") + if not isinstance(instance, str_types): + return True + return rfc3987.parse(instance, rule="URI") try: - import isodate + import strict_rfc3339 except ImportError: - pass + try: + import isodate + except ImportError: + pass + else: + @_checks_drafts("date-time", raises=(ValueError, isodate.ISO8601Error)) + def is_date(instance): + if not isinstance(instance, str_types): + return True + return isodate.parse_datetime(instance) else: - _err = (ValueError, isodate.ISO8601Error) - _checks_drafts("date-time", raises=_err)(isodate.parse_datetime) + @_checks_drafts("date-time") + def is_date(instance): + if not isinstance(instance, str_types): + return True + return strict_rfc3339.validate_rfc3339(instance) -_checks_drafts("regex", raises=re.error)(re.compile) +@_checks_drafts("regex", raises=re.error) +def is_regex(instance): + if not isinstance(instance, str_types): + return True + return re.compile(instance) @_checks_drafts(draft3="date", raises=ValueError) def is_date(instance): + if not isinstance(instance, str_types): + return True return datetime.datetime.strptime(instance, "%Y-%m-%d") @_checks_drafts(draft3="time", raises=ValueError) def is_time(instance): + if not isinstance(instance, str_types): + return True return datetime.datetime.strptime(instance, "%H:%M:%S") @@ -184,7 +218,10 @@ @_checks_drafts(draft3="color", raises=(ValueError, TypeError)) def is_css21_color(instance): - if instance.lower() in webcolors.css21_names_to_hex: + if ( + not isinstance(instance, str_types) or + instance.lower() in webcolors.css21_names_to_hex + ): return True return is_css_color_code(instance) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/_validators.py new/jsonschema-2.3.0/jsonschema/_validators.py --- old/jsonschema-2.0.0/jsonschema/_validators.py 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/_validators.py 2013-10-28 13:04:39.000000000 +0100 @@ -59,8 +59,9 @@ ): return + len_items = len(schema.get("items", [])) if validator.is_type(aI, "object"): - for index, item in enumerate(instance[len(schema.get("items", [])):]): + for index, item in enumerate(instance[len_items:], start=len_items): for error in validator.descend(item, aI, path=index): yield error elif not aI and len(instance) > len(schema.get("items", [])): @@ -149,10 +150,7 @@ def format(validator, format, instance, schema): - if ( - validator.format_checker is not None and - validator.is_type(instance, "string") - ): + if validator.format_checker is not None: try: validator.format_checker.check(instance, format) except FormatError as error: @@ -214,7 +212,7 @@ if not errors: return all_errors.extend(errors) - elif validator.is_type(type, "string"): + else: if validator.is_type(instance, type): return else: @@ -299,14 +297,16 @@ def minProperties_draft4(validator, mP, instance, schema): if validator.is_type(instance, "object") and len(instance) < mP: - yield ValidationError("%r is too short" % (instance,)) + yield ValidationError( + "%r does not have enough properties" % (instance,) + ) def maxProperties_draft4(validator, mP, instance, schema): if not validator.is_type(instance, "object"): return if validator.is_type(instance, "object") and len(instance) > mP: - yield ValidationError("%r is too short" % (instance,)) + yield ValidationError("%r has too many properties" % (instance,)) def allOf_draft4(validator, allOf, instance, schema): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/exceptions.py new/jsonschema-2.3.0/jsonschema/exceptions.py --- old/jsonschema-2.0.0/jsonschema/exceptions.py 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/exceptions.py 2013-10-30 13:09:11.000000000 +0100 @@ -1,4 +1,5 @@ import collections +import itertools import pprint import textwrap @@ -6,6 +7,9 @@ from jsonschema.compat import PY3, iteritems +WEAK_MATCHES = frozenset(["anyOf", "oneOf"]) +STRONG_MATCHES = frozenset() + _unset = _utils.Unset() @@ -24,25 +28,6 @@ self.instance = instance self.schema = schema - @classmethod - def create_from(cls, other): - return cls( - message=other.message, - cause=other.cause, - context=other.context, - path=other.path, - schema_path=other.schema_path, - validator=other.validator, - validator_value=other.validator_value, - instance=other.instance, - schema=other.schema, - ) - - def _set(self, **kwargs): - for k, v in iteritems(kwargs): - if getattr(self, k) is _unset: - setattr(self, k, v) - def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, self.message) @@ -79,6 +64,23 @@ if PY3: __str__ = __unicode__ + @classmethod + def create_from(cls, other): + return cls(**other._contents()) + + def _set(self, **kwargs): + for k, v in iteritems(kwargs): + if getattr(self, k) is _unset: + setattr(self, k, v) + + def _contents(self): + return dict( + (attr, getattr(self, attr)) for attr in ( + "message", "cause", "context", "path", "schema_path", + "validator", "validator_value", "instance", "schema" + ) + ) + class ValidationError(_Error): pass @@ -93,7 +95,29 @@ class UnknownType(Exception): - pass + def __init__(self, type, instance, schema): + self.type = type + self.instance = instance + self.schema = schema + + def __str__(self): + return unicode(self).encode("utf-8") + + def __unicode__(self): + pschema = pprint.pformat(self.schema, width=72) + pinstance = pprint.pformat(self.instance, width=72) + return textwrap.dedent(""" + Unknown type %r for validator with schema: + %s + + While checking instance: + %s + """.rstrip() + ) % (self.type, _utils.indent(pschema), _utils.indent(pinstance)) + + if PY3: + __str__ = __unicode__ + class FormatError(Exception): @@ -110,3 +134,98 @@ if PY3: __str__ = __unicode__ + + +class ErrorTree(object): + """ + ErrorTrees make it easier to check which validations failed. + + """ + + _instance = _unset + + def __init__(self, errors=()): + self.errors = {} + self._contents = collections.defaultdict(self.__class__) + + for error in errors: + container = self + for element in error.path: + container = container[element] + container.errors[error.validator] = error + + self._instance = error.instance + + def __contains__(self, index): + """ + Check whether ``instance[index]`` has any errors. + + """ + + return index in self._contents + + def __getitem__(self, index): + """ + Retrieve the child tree one level down at the given ``index``. + + If the index is not in the instance that this tree corresponds to and + is not known by this tree, whatever error would be raised by + ``instance.__getitem__`` will be propagated (usually this is some + subclass of :class:`LookupError`. + + """ + + if self._instance is not _unset and index not in self: + self._instance[index] + return self._contents[index] + + def __setitem__(self, index, value): + self._contents[index] = value + + def __iter__(self): + """ + Iterate (non-recursively) over the indices in the instance with errors. + + """ + + return iter(self._contents) + + def __len__(self): + """ + Same as :attr:`total_errors`. + + """ + + return self.total_errors + + def __repr__(self): + return "<%s (%s total errors)>" % (self.__class__.__name__, len(self)) + + @property + def total_errors(self): + """ + The total number of errors in the entire tree, including children. + + """ + + child_errors = sum(len(tree) for _, tree in iteritems(self._contents)) + return len(self.errors) + child_errors + + +def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES): + def relevance(error): + validator = error.validator + return -len(error.path), validator not in weak, validator in strong + return relevance + + +def best_match(errors, key=by_relevance()): + errors = iter(errors) + best = next(errors, None) + if best is None: + return + best = max(itertools.chain([best], errors), key=key) + + while best.context: + best = min(best.context, key=key) + return best diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/tests/test_exceptions.py new/jsonschema-2.3.0/jsonschema/tests/test_exceptions.py --- old/jsonschema-2.0.0/jsonschema/tests/test_exceptions.py 1970-01-01 01:00:00.000000000 +0100 +++ new/jsonschema-2.3.0/jsonschema/tests/test_exceptions.py 2013-10-30 15:32:59.000000000 +0100 @@ -0,0 +1,270 @@ +from jsonschema import Draft4Validator, exceptions +from jsonschema.tests.compat import mock, unittest + + +class TestBestMatch(unittest.TestCase): + def best_match(self, errors): + errors = list(errors) + best = exceptions.best_match(errors) + reversed_best = exceptions.best_match(reversed(errors)) + self.assertEqual( + best, + reversed_best, + msg="Didn't return a consistent best match!\n" + "Got: {0}\n\nThen: {1}".format(best, reversed_best), + ) + return best + + def test_shallower_errors_are_better_matches(self): + validator = Draft4Validator( + { + "properties" : { + "foo" : { + "minProperties" : 2, + "properties" : {"bar" : {"type" : "object"}}, + } + } + } + ) + best = self.best_match(validator.iter_errors({"foo" : {"bar" : []}})) + self.assertEqual(best.validator, "minProperties") + + def test_oneOf_and_anyOf_are_weak_matches(self): + """ + A property you *must* match is probably better than one you have to + match a part of. + + """ + + validator = Draft4Validator( + { + "minProperties" : 2, + "anyOf" : [{"type" : "string"}, {"type" : "number"}], + "oneOf" : [{"type" : "string"}, {"type" : "number"}], + } + ) + best = self.best_match(validator.iter_errors({})) + self.assertEqual(best.validator, "minProperties") + + def test_if_the_most_relevant_error_is_anyOf_it_is_traversed(self): + """ + If the most relevant error is an anyOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + + """ + + validator = Draft4Validator( + { + "properties" : { + "foo" : { + "anyOf" : [ + {"type" : "string"}, + {"properties" : {"bar" : {"type" : "array"}}}, + ], + }, + }, + }, + ) + best = self.best_match(validator.iter_errors({"foo" : {"bar" : 12}})) + self.assertEqual(best.validator_value, "array") + + def test_if_the_most_relevant_error_is_oneOf_it_is_traversed(self): + """ + If the most relevant error is an oneOf, then we traverse its context + and select the otherwise *least* relevant error, since in this case + that means the most specific, deep, error inside the instance. + + I.e. since only one of the schemas must match, we look for the most + relevant one. + + """ + + validator = Draft4Validator( + { + "properties" : { + "foo" : { + "oneOf" : [ + {"type" : "string"}, + {"properties" : {"bar" : {"type" : "array"}}}, + ], + }, + }, + }, + ) + best = self.best_match(validator.iter_errors({"foo" : {"bar" : 12}})) + self.assertEqual(best.validator_value, "array") + + def test_if_the_most_relevant_error_is_allOf_it_is_traversed(self): + """ + Now, if the error is allOf, we traverse but select the *most* relevant + error from the context, because all schemas here must match anyways. + + """ + + validator = Draft4Validator( + { + "properties" : { + "foo" : { + "allOf" : [ + {"type" : "string"}, + {"properties" : {"bar" : {"type" : "array"}}}, + ], + }, + }, + }, + ) + best = self.best_match(validator.iter_errors({"foo" : {"bar" : 12}})) + self.assertEqual(best.validator_value, "string") + + def test_nested_context_for_oneOf(self): + validator = Draft4Validator( + { + "properties" : { + "foo" : { + "oneOf" : [ + {"type" : "string"}, + { + "oneOf" : [ + {"type" : "string"}, + { + "properties" : { + "bar" : {"type" : "array"} + }, + }, + ], + }, + ], + }, + }, + }, + ) + best = self.best_match(validator.iter_errors({"foo" : {"bar" : 12}})) + self.assertEqual(best.validator_value, "array") + + def test_one_error(self): + validator = Draft4Validator({"minProperties" : 2}) + error, = validator.iter_errors({}) + self.assertEqual( + exceptions.best_match(validator.iter_errors({})).validator, + "minProperties", + ) + + def test_no_errors(self): + validator = Draft4Validator({}) + self.assertIsNone(exceptions.best_match(validator.iter_errors({}))) + + +class TestByRelevance(unittest.TestCase): + def test_short_paths_are_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=["baz"]) + deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"]) + match = max([shallow, deep], key=exceptions.by_relevance()) + self.assertIs(match, shallow) + + match = max([deep, shallow], key=exceptions.by_relevance()) + self.assertIs(match, shallow) + + def test_global_errors_are_even_better_matches(self): + shallow = exceptions.ValidationError("Oh no!", path=[]) + deep = exceptions.ValidationError("Oh yes!", path=["foo"]) + + errors = sorted([shallow, deep], key=exceptions.by_relevance()) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + errors = sorted([deep, shallow], key=exceptions.by_relevance()) + self.assertEqual( + [list(error.path) for error in errors], + [["foo"], []], + ) + + def test_weak_validators_are_lower_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + + best_match = exceptions.by_relevance(weak="a") + + match = max([weak, normal], key=best_match) + self.assertIs(match, normal) + + match = max([normal, weak], key=best_match) + self.assertIs(match, normal) + + def test_strong_validators_are_higher_priority(self): + weak = exceptions.ValidationError("Oh no!", path=[], validator="a") + normal = exceptions.ValidationError("Oh yes!", path=[], validator="b") + strong = exceptions.ValidationError("Oh fine!", path=[], validator="c") + + best_match = exceptions.by_relevance(weak="a", strong="c") + + match = max([weak, normal, strong], key=best_match) + self.assertIs(match, strong) + + match = max([strong, normal, weak], key=best_match) + self.assertIs(match, strong) + + +class TestErrorTree(unittest.TestCase): + def test_it_knows_how_many_total_errors_it_contains(self): + errors = [mock.MagicMock() for _ in range(8)] + tree = exceptions.ErrorTree(errors) + self.assertEqual(tree.total_errors, 8) + + def test_it_contains_an_item_if_the_item_had_an_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertIn("bar", tree) + + def test_it_does_not_contain_an_item_if_the_item_had_no_error(self): + errors = [exceptions.ValidationError("a message", path=["bar"])] + tree = exceptions.ErrorTree(errors) + self.assertNotIn("foo", tree) + + def test_validators_that_failed_appear_in_errors_dict(self): + error = exceptions.ValidationError("a message", validator="foo") + tree = exceptions.ErrorTree([error]) + self.assertEqual(tree.errors, {"foo" : error}) + + def test_it_creates_a_child_tree_for_each_nested_path(self): + errors = [ + exceptions.ValidationError("a bar message", path=["bar"]), + exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]), + ] + tree = exceptions.ErrorTree(errors) + self.assertIn(0, tree["bar"]) + self.assertNotIn(1, tree["bar"]) + + def test_children_have_their_errors_dicts_built(self): + e1, e2 = ( + exceptions.ValidationError("1", validator="foo", path=["bar", 0]), + exceptions.ValidationError("2", validator="quux", path=["bar", 0]), + ) + tree = exceptions.ErrorTree([e1, e2]) + self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2}) + + def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self): + error = exceptions.ValidationError("123", validator="foo", instance=[]) + tree = exceptions.ErrorTree([error]) + + with self.assertRaises(IndexError): + tree[0] + + def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self): + """ + If a validator is dumb (like :validator:`required` in draft 3) and + refers to a path that isn't in the instance, the tree still properly + returns a subtree for that path. + + """ + + error = exceptions.ValidationError( + "a message", validator="foo", instance={}, path=["foo"], + ) + tree = exceptions.ErrorTree([error]) + self.assertIsInstance(tree["foo"], exceptions.ErrorTree) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/tests/test_jsonschema_test_suite.py new/jsonschema-2.3.0/jsonschema/tests/test_jsonschema_test_suite.py --- old/jsonschema-2.0.0/jsonschema/tests/test_jsonschema_test_suite.py 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/tests/test_jsonschema_test_suite.py 2013-10-28 13:04:13.000000000 +0100 @@ -3,7 +3,7 @@ Tests comprehensive correctness of each draft's validator. -See https://github.com/jsonschema/JSON-Schema-Test-Suite for details. +See https://github.com/json-schema/JSON-Schema-Test-Suite for details. """ @@ -139,13 +139,15 @@ def missing_format(checker): def missing_format(case): format = case["schema"].get("format") - if format not in checker.checkers or ( - # datetime.datetime is overzealous about typechecking in <=1.9 + if format not in checker.checkers: + return "Format checker {0!r} not found.".format(format) + elif ( format == "date-time" and pypy_version_info is not None and pypy_version_info[:2] <= (1, 9) ): - return "Format checker not found." + # datetime.datetime is overzealous about typechecking in <=1.9 + return "datetime.datetime is broken on this version of PyPy." return missing_format @@ -178,6 +180,24 @@ # Make sure original cause is attached self.assertIs(cm.exception.cause, cause) + def test_it_validates_formats_of_any_type(self): + checker = mock.Mock(spec=FormatChecker) + validator = self.validator_class( + {"format" : "foo"}, format_checker=checker, + ) + + validator.validate([1, 2, 3]) + + checker.check.assert_called_once_with([1, 2, 3], "foo") + + cause = ValueError() + checker.check.side_effect = FormatError('aoeu', cause=cause) + + with self.assertRaises(ValidationError) as cm: + validator.validate([1, 2, 3]) + # Make sure original cause is attached + self.assertIs(cm.exception.cause, cause) + @load_json_cases("draft3/*.json", ignore_glob="draft3/refRemote.json") @load_json_cases( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/tests/test_validators.py new/jsonschema-2.3.0/jsonschema/tests/test_validators.py --- old/jsonschema-2.0.0/jsonschema/tests/test_validators.py 2013-05-21 15:33:15.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/tests/test_validators.py 2013-10-30 15:18:15.000000000 +0100 @@ -8,7 +8,7 @@ from jsonschema.compat import PY3 from jsonschema.tests.compat import mock, unittest from jsonschema.validators import ( - RefResolutionError, UnknownType, ErrorTree, Draft3Validator, + RefResolutionError, UnknownType, Draft3Validator, Draft4Validator, RefResolver, create, extend, validator_for, validate, ) @@ -424,6 +424,23 @@ self.assertEqual(list(e5.path), [1, "bar", "baz"]) self.assertEqual(list(e6.path), [1, "foo"]) + self.assertEqual(list(e1.schema_path), ["type"]) + self.assertEqual(list(e2.schema_path), ["items", "type"]) + self.assertEqual( + list(e3.schema_path), ["items", "properties", "bar", "type"], + ) + self.assertEqual( + list(e4.schema_path), + ["items", "properties", "bar", "properties", "bar", "required"], + ) + self.assertEqual( + list(e5.schema_path), + ["items", "properties", "bar", "properties", "baz", "minItems"] + ) + self.assertEqual( + list(e6.schema_path), ["items", "properties", "foo", "enum"], + ) + self.assertEqual(e1.validator, "type") self.assertEqual(e2.validator, "type") self.assertEqual(e3.validator, "type") @@ -483,68 +500,22 @@ self.assertEqual(e1.validator, "type") self.assertEqual(e2.validator, "minimum") + def test_additionalItems_with_items(self): + instance = ["foo", "bar", 1] + schema = { + "items": [{}], + "additionalItems" : {"type": "integer", "minimum": 5} + } -class TestErrorTree(unittest.TestCase): - def setUp(self): - self.validator = Draft3Validator({}) - - def test_it_knows_how_many_total_errors_it_contains(self): - errors = [mock.MagicMock() for _ in range(8)] - tree = ErrorTree(errors) - self.assertEqual(tree.total_errors, 8) - - def test_it_contains_an_item_if_the_item_had_an_error(self): - errors = [ValidationError("a message", path=["bar"])] - tree = ErrorTree(errors) - self.assertIn("bar", tree) - - def test_it_does_not_contain_an_item_if_the_item_had_no_error(self): - errors = [ValidationError("a message", path=["bar"])] - tree = ErrorTree(errors) - self.assertNotIn("foo", tree) - - def test_validators_that_failed_appear_in_errors_dict(self): - error = ValidationError("a message", validator="foo") - tree = ErrorTree([error]) - self.assertEqual(tree.errors, {"foo" : error}) - - def test_it_creates_a_child_tree_for_each_nested_path(self): - errors = [ - ValidationError("a bar message", path=["bar"]), - ValidationError("a bar -> 0 message", path=["bar", 0]), - ] - tree = ErrorTree(errors) - self.assertIn(0, tree["bar"]) - self.assertNotIn(1, tree["bar"]) - - def test_children_have_their_errors_dicts_built(self): - e1, e2 = ( - ValidationError("message 1", validator="foo", path=["bar", 0]), - ValidationError("message 2", validator="quux", path=["bar", 0]), - ) - tree = ErrorTree([e1, e2]) - self.assertEqual(tree["bar"][0].errors, {"foo" : e1, "quux" : e2}) - - def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self): - error = ValidationError("a message", validator="foo", instance=[]) - tree = ErrorTree([error]) - - with self.assertRaises(IndexError): - tree[0] - - def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self): - """ - If a validator is dumb (like :validator:`required` in draft 3) and - refers to a path that isn't in the instance, the tree still properly - returns a subtree for that path. + validator = Draft3Validator(schema) + errors = validator.iter_errors(instance) + e1, e2 = sorted_errors(errors) - """ + self.assertEqual(list(e1.path), [1]) + self.assertEqual(list(e2.path), [2]) - error = ValidationError( - "a message", validator="foo", instance={}, path=["foo"], - ) - tree = ErrorTree([error]) - self.assertIsInstance(tree["foo"], ErrorTree) + self.assertEqual(e1.validator, "type") + self.assertEqual(e2.validator, "minimum") class ValidatorTestMixin(object): @@ -621,11 +592,38 @@ self.assertTrue(self.validator.is_type(True, "boolean")) self.assertTrue(self.validator.is_valid(True, {"type": "any"})) + def test_non_string_custom_types(self): + schema = {'type': [None]} + cls = self.validator_class(schema, types={None: type(None)}) + cls.validate(None, schema) + class TestDraft4Validator(ValidatorTestMixin, unittest.TestCase): validator_class = Draft4Validator +class TestBuiltinFormats(unittest.TestCase): + """ + The built-in (specification-defined) formats do not raise type errors. + + If an instance or value is not a string, it should be ignored. + + """ + + +for format in FormatChecker.checkers: + def test(self, format=format): + v = Draft4Validator({"format": format}, format_checker=FormatChecker()) + v.validate(123) + + name = "test_{0}_ignores_non_strings".format(format) + if not PY3: + name = name.encode("utf-8") + test.__name__ = name + setattr(TestBuiltinFormats, name, test) + del test # Ugh py.test. Stop discovering top level tests. + + class TestValidatorFor(unittest.TestCase): def test_draft_3(self): schema = {"$schema" : "http://json-schema.org/draft-03/schema"} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/jsonschema/validators.py new/jsonschema-2.3.0/jsonschema/validators.py --- old/jsonschema-2.0.0/jsonschema/validators.py 2013-05-21 15:39:23.000000000 +0200 +++ new/jsonschema-2.3.0/jsonschema/validators.py 2013-10-30 22:01:04.000000000 +0100 @@ -1,6 +1,5 @@ from __future__ import division, unicode_literals -import collections import contextlib import json import numbers @@ -15,6 +14,7 @@ PY3, Sequence, urljoin, urlsplit, urldefrag, unquote, urlopen, str_types, int_types, iteritems, ) +from jsonschema.exceptions import ErrorTree # Backwards compatibility # noqa from jsonschema.exceptions import RefResolutionError, SchemaError, UnknownType @@ -118,7 +118,7 @@ def is_type(self, instance, type): if type not in self._types: - raise UnknownType(type) + raise UnknownType(type, instance, self.schema) pytypes = self._types[type] # bool inherits from int, so ensure bools aren't reported as ints @@ -379,82 +379,6 @@ return result -class ErrorTree(object): - """ - ErrorTrees make it easier to check which validations failed. - - """ - - _instance = _unset - - def __init__(self, errors=()): - self.errors = {} - self._contents = collections.defaultdict(self.__class__) - - for error in errors: - container = self - for element in error.path: - container = container[element] - container.errors[error.validator] = error - - self._instance = error.instance - - def __contains__(self, index): - """ - Check whether ``instance[index]`` has any errors. - - """ - - return index in self._contents - - def __getitem__(self, index): - """ - Retrieve the child tree one level down at the given ``index``. - - If the index is not in the instance that this tree corresponds to and - is not known by this tree, whatever error would be raised by - ``instance.__getitem__`` will be propagated (usually this is some - subclass of :class:`LookupError`. - - """ - - if self._instance is not _unset and index not in self: - self._instance[index] - return self._contents[index] - - def __setitem__(self, index, value): - self._contents[index] = value - - def __iter__(self): - """ - Iterate (non-recursively) over the indices in the instance with errors. - - """ - - return iter(self._contents) - - def __len__(self): - """ - Same as :attr:`total_errors`. - - """ - - return self.total_errors - - def __repr__(self): - return "<%s (%s total errors)>" % (self.__class__.__name__, len(self)) - - @property - def total_errors(self): - """ - The total number of errors in the entire tree, including children. - - """ - - child_errors = sum(len(tree) for _, tree in iteritems(self._contents)) - return len(self.errors) + child_errors - - def validator_for(schema, default=_unset): if default is _unset: default = Draft4Validator @@ -462,6 +386,46 @@ def validate(instance, schema, cls=None, *args, **kwargs): + """ + Validate an instance under the given schema. + + >>> validate([2, 3, 4], {"maxItems" : 2}) + Traceback (most recent call last): + ... + ValidationError: [2, 3, 4] is too long + + :func:`validate` will first verify that the provided schema is itself + valid, since not doing so can lead to less obvious error messages and fail + in less obvious or consistent ways. If you know you have a valid schema + already or don't care, you might prefer using the + :meth:`~IValidator.validate` method directly on a specific validator + (e.g. :meth:`Draft4Validator.validate`). + + + :argument instance: the instance to validate + :argument schema: the schema to validate with + :argument cls: an :class:`IValidator` class that will be used to validate + the instance. + + If the ``cls`` argument is not provided, two things will happen in + accordance with the specification. First, if the schema has a + :validator:`$schema` property containing a known meta-schema [#]_ then the + proper validator will be used. The specification recommends that all + schemas contain :validator:`$schema` properties for this reason. If no + :validator:`$schema` property is found, the default validator class is + :class:`Draft4Validator`. + + Any other provided positional and keyword arguments will be passed on when + instantiating the ``cls``. + + :raises: + :exc:`ValidationError` if the instance is invalid + + :exc:`SchemaError` if the schema itself is invalid + + .. rubric:: Footnotes + .. [#] known by a validator registered with :func:`validates` + """ if cls is None: cls = validator_for(schema) cls.check_schema(schema) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonschema-2.0.0/tox.ini new/jsonschema-2.3.0/tox.ini --- old/jsonschema-2.0.0/tox.ini 2013-05-21 00:47:55.000000000 +0200 +++ new/jsonschema-2.3.0/tox.ini 2013-10-28 02:05:58.000000000 +0100 @@ -3,12 +3,21 @@ [testenv] commands = - py.test -s jsonschema + py.test [] -s jsonschema {envpython} -m doctest README.rst deps = {[testenv:notpy33]deps} {[testenv:py33]deps} +[testenv:coverage] +commands = + coverage run --source jsonschema [] {envbindir}/py.test + coverage html +deps = + {[testenv:notpy33]deps} + {[testenv:py33]deps} + coverage + [testenv:docs] basepython = python changedir = docs @@ -16,15 +25,12 @@ lxml sphinx commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html + sphinx-build [] -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv:style] deps = flake8 commands = - flake8 --max-complexity 10 jsonschema - -[flake8] -ignore = E203,E302,E303,E701,F811 + flake8 [] --max-complexity 10 jsonschema [testenv:py26] deps = @@ -35,7 +41,7 @@ [testenv:py33] commands = - py.test -s jsonschema + py.test [] -s jsonschema {envpython} -m doctest README.rst sphinx-build -b doctest docs {envtmpdir}/html deps = @@ -52,8 +58,14 @@ [testenv:all] deps = - isodate lxml pytest sphinx + strict-rfc3339 webcolors + +[flake8] +ignore = E203,E302,E303,E701,F811 + +[pytest] +addopts = -r s -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org