diff --git a/.coveragerc b/.coveragerc index 0294caff26f97ac3f9383e0e9b63291a0e527313..0f24d2f04c0a62b981a04fb1d789b04329d798a4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,4 @@ [run] branch = True source = jsonschema -omit = */jsonschema/_reflect.py,*/jsonschema/__main__.py +omit = */jsonschema/_reflect.py,*/jsonschema/__main__.py,*/jsonschema/benchmarks/* diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 1e3d93df7f791ab72bba711d4ee235bc577534ba..fc40c007ca5941b171863f01dcbbd77614b123e4 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,5 @@ # These are supported funding model platforms +github: "Julian" +patreon: "JulianWasTaken" tidelift: "pypi/jsonschema" diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..fd524e9476f6631504639ba1e0dd063174ef0c32 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +In general, only the latest released ``jsonschema`` version is supported +and will receive updates. + +## Reporting a Vulnerability + +To report a security vulnerability, please send an email to +``Julian+Security@GrayVines.com`` with subject line ``SECURITY +(jsonschema)``. + +I will do my best to respond within 48 hours to acknowledge the message +and discuss further steps. + +If the vulnerability is accepted, an advisory will be sent out via +GitHub's security advisory functionality. + +For non-sensitive discussion related to this policy itself, feel free to +open an issue on the issue tracker. diff --git a/.travis.yml b/.travis.yml index ede965b8b32e03a39dfebd0dee75bf7af6ab5521..d31d77e842b8b8248fd3baf633df34936dd59ab9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ python: - 3.5 - 3.6 - 3.7 + - 3.8 - pypy - pypy3 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1a77a1e85ec652bf0f1458d0822982b7c30da6a2..8f0a2700ea00ce01cf88a7325645d84bdd921c9e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,21 @@ +v3.2.0 +------ + +* Added a ``format_nongpl`` setuptools extra, which installs only ``format`` + dependencies that are non-GPL (#619). + +v3.1.1 +------ + +* Temporarily revert the switch to ``js-regex`` until #611 and #612 are + resolved. + +v3.1.0 +------ + +* Regular expressions throughout schemas now respect the ECMA 262 dialect, as + recommended by the specification (#609). + v3.0.2 ------ diff --git a/DEMO.ipynb b/DEMO.ipynb index cb66de12dcb3cf9f6dc06822722b3cc07a380945..f008b793f08f28d6d22f3e6967f5cda8ee41c388 100644 --- a/DEMO.ipynb +++ b/DEMO.ipynb @@ -54,17 +54,31 @@ "outputs": [], "source": [ "# If no exception is raised by validate(), the instance is valid.\n", - "validate({\"name\" : \"Eggs\", \"price\" : 34.99}, schema)" + "validate(instance={\"name\" : \"Eggs\", \"price\" : 34.99}, schema=schema)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "ValidationError", + "evalue": "'Invalid' is not of type 'number'\n\nFailed validating 'type' in schema['properties']['price']:\n {'type': 'number'}\n\nOn instance['price']:\n 'Invalid'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m validate(\n\u001b[1;32m 2\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m\"name\"\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0;34m\"Eggs\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"price\"\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0;34m\"Invalid\"\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mschema\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mschema\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m )\n", + "\u001b[0;32m~/Development/jsonschema/jsonschema/validators.py\u001b[0m in \u001b[0;36mvalidate\u001b[0;34m(instance, schema, cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m 899\u001b[0m \u001b[0merror\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbest_match\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalidator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miter_errors\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 900\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0merror\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 901\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 902\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 903\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValidationError\u001b[0m: 'Invalid' is not of type 'number'\n\nFailed validating 'type' in schema['properties']['price']:\n {'type': 'number'}\n\nOn instance['price']:\n 'Invalid'" + ] + } + ], "source": [ "validate(\n", - " {\"name\" : \"Eggs\", \"price\" : \"Invalid\"}, schema\n", + " instance={\"name\" : \"Eggs\", \"price\" : \"Invalid\"},\n", + " schema=schema,\n", ")" ] }, @@ -145,9 +159,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.4" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/README.rst b/README.rst index a3004b438598effb43548c72ae55f8819f64dcca..ccfb55d02d4752ae3dbb05c40318774e6a0e79ee 100644 --- a/README.rst +++ b/README.rst @@ -12,9 +12,9 @@ jsonschema :alt: Supported Python versions :target: https://pypi.org/project/jsonschema/ -.. |Travis| image:: https://travis-ci.org/Julian/jsonschema.svg?branch=master +.. |Travis| image:: https://travis-ci.com/Julian/jsonschema.svg?branch=master :alt: Travis build status - :target: https://travis-ci.org/Julian/jsonschema + :target: https://travis-ci.com/Julian/jsonschema .. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/adtt0aiaihy6muyn/branch/master?svg=true :alt: AppVeyor build status @@ -74,7 +74,7 @@ Features * `Lazy validation `_ that can iteratively report *all* validation errors. -* `Programmatic querying `_ +* `Programmatic querying `_ of which properties or items failed validation. @@ -103,21 +103,16 @@ Online demo Notebook will look similar to this: .. image:: https://user-images.githubusercontent.com/1155573/56820861-5c1c1880-6823-11e9-802a-ce01c5ec574f.gif :alt: Open Live Demo - :width: 50 px - :scale: 10 % - + :width: 480 px Release Notes ------------- -Version 3.0 brings support for Draft 7 (and 6). The interface for redefining -types has also been majorly overhauled to support easier redefinition of the -types a Validator will accept or allow. - -jsonschema is also now tested under Windows via AppVeyor. - -Thanks to all who contributed pull requests along the way. +v3.1 brings support for ECMA 262 dialect regular expressions +throughout schemas, as recommended by the specification. Big +thanks to @Zac-HD for authoring support in a new `js-regex +`_ library. Running the Test Suite @@ -137,19 +132,20 @@ favorite test runner. The tests live in the ``jsonschema.tests`` package. Benchmarks ---------- -``jsonschema``'s benchmarks make use of `perf `_. +``jsonschema``'s benchmarks make use of `pyperf +`_. -Running them can be done via ``tox -e perf``, or by invoking the ``perf`` +Running them can be done via ``tox -e perf``, or by invoking the ``pyperf`` commands externally (after ensuring that both it and ``jsonschema`` itself are installed):: - $ python -m perf jsonschema/benchmarks/test_suite.py --hist --output results.json + $ python -m pyperf jsonschema/benchmarks/test_suite.py --hist --output results.json To compare to a previous run, use:: - $ python -m perf compare_to --table reference.json results.json + $ python -m pyperf compare_to --table reference.json results.json -See the ``perf`` documentation for more details. +See the ``pyperf`` documentation for more details. Community diff --git a/debian/changelog b/debian/changelog index 3b52a9f6daddcfda3452a8c2f3ca56f6e5d04392..a877d776736cfc349a50473ec9a6f96d32d7ca99 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,37 @@ +python-jsonschema (3.2.0-4) UNRELEASED; urgency=low + + * debian/copyright: use spaces rather than tabs to start continuation lines. + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + + -- Debian Janitor Sat, 27 Jun 2020 17:36:29 -0000 + +python-jsonschema (3.2.0-3) unstable; urgency=medium + + * Uploading to unstable. + + -- Thomas Goirand Fri, 08 May 2020 11:04:26 +0200 + +python-jsonschema (3.2.0-2) experimental; urgency=medium + + * d/control: Add {build}dependency python3-importlib-metadata + + -- Michal Arbet Tue, 05 May 2020 11:43:45 +0200 + +python-jsonschema (3.2.0-1) experimental; urgency=medium + + * New upstream version + * d/copyright: Add me to copyright + * d/control: + - Add me to uploaders + - Bump standards to 4.5.0 + - Breaks: python3-json-pointer (<< 1.14) (Closes: #949723) + * d/patches: + - Remove remove-TestBuiltinFormats-hack.patch + - Add fix-lintian-privacy-breach.patch + + -- Michal Arbet Tue, 05 May 2020 09:21:24 +0200 + python-jsonschema (3.0.2-4) unstable; urgency=medium * Add missing dependencies for autopkgtests. diff --git a/debian/control b/debian/control index cb75afecd92be6a1b00f2bb7eebd858f440bdc0a..29607bed5a5b8175b47b34cb816470b2f47eb2a2 100644 --- a/debian/control +++ b/debian/control @@ -4,6 +4,7 @@ Priority: optional Maintainer: Debian OpenStack Uploaders: Thomas Goirand , + Michal Arbet , Build-Depends: autopkgtest, debhelper-compat (= 12), @@ -15,6 +16,7 @@ Build-Depends: python3-sphinx, Build-Depends-Indep: python3-attr, + python3-importlib-metadata, python3-lxml, python3-mock, python3-nose, @@ -23,7 +25,7 @@ Build-Depends-Indep: python3-pytest, python3-six, python3-twisted, -Standards-Version: 4.4.1 +Standards-Version: 4.5.0 Vcs-Git: https://salsa.debian.org/openstack-team/third-party/python-jsonschema.git Vcs-Browser: https://salsa.debian.org/openstack-team/third-party/python-jsonschema Homepage: https://github.com/Julian/jsonschema @@ -48,6 +50,7 @@ Package: python3-jsonschema Architecture: all Depends: python3-attr, + python3-importlib-metadata, python3-pkg-resources, python3-setuptools, python3-six, @@ -55,6 +58,8 @@ Depends: ${python3:Depends}, Suggests: python-jsonschema-doc, +Breaks: + python3-json-pointer (<< 1.14), Description: An(other) implementation of JSON Schema (Draft 3 and 4) - Python 3.x JSON Schema is a specification for a JSON-based format for defining the structure of JSON data. JSON Schema provides a contract for what diff --git a/debian/copyright b/debian/copyright index 2d29d12112d9ccfb83fd6dafc448121dc5e418f6..cc035155f09b264393b664b3a80c61835bba8c5d 100644 --- a/debian/copyright +++ b/debian/copyright @@ -25,8 +25,9 @@ License: MIT Files: debian/* Copyright: 2012 Chuck Short - 2012, Ghe Rivero - 2013, Thomas Goirand + 2012, Ghe Rivero + 2013, Thomas Goirand + 2020, Michal Arbet License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/debian/patches/fix-lintian-privacy-breach.patch b/debian/patches/fix-lintian-privacy-breach.patch new file mode 100644 index 0000000000000000000000000000000000000000..16d46b51fc7599c01515500b865bb8b8077d7b52 --- /dev/null +++ b/debian/patches/fix-lintian-privacy-breach.patch @@ -0,0 +1,62 @@ +Description: Remove external sources from doc. +Author: Michal Arbet +Forwarded: not-needed +Last-Update: 2020-05-05 + +diff --git a/README.rst b/README.rst +index ccfb55d..3f7d111 100644 +--- a/README.rst ++++ b/README.rst +@@ -4,31 +4,6 @@ jsonschema + + |PyPI| |Pythons| |Travis| |AppVeyor| |Codecov| |ReadTheDocs| + +-.. |PyPI| image:: https://img.shields.io/pypi/v/jsonschema.svg +- :alt: PyPI version +- :target: https://pypi.org/project/jsonschema/ +- +-.. |Pythons| image:: https://img.shields.io/pypi/pyversions/jsonschema.svg +- :alt: Supported Python versions +- :target: https://pypi.org/project/jsonschema/ +- +-.. |Travis| image:: https://travis-ci.com/Julian/jsonschema.svg?branch=master +- :alt: Travis build status +- :target: https://travis-ci.com/Julian/jsonschema +- +-.. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/adtt0aiaihy6muyn/branch/master?svg=true +- :alt: AppVeyor build status +- :target: https://ci.appveyor.com/project/Julian/jsonschema +- +-.. |Codecov| image:: https://codecov.io/gh/Julian/jsonschema/branch/master/graph/badge.svg +- :alt: Codecov Code coverage +- :target: https://codecov.io/gh/Julian/jsonschema +- +-.. |ReadTheDocs| image:: https://readthedocs.org/projects/python-jsonschema/badge/?version=stable&style=flat +- :alt: ReadTheDocs status +- :target: https://python-jsonschema.readthedocs.io/en/stable/ +- +- + ``jsonschema`` is an implementation of `JSON Schema `_ + for Python (supporting 2.7+ including Python 3). + +@@ -91,19 +66,7 @@ Installation + Demo + ---- + +-Try ``jsonschema`` interactively in this online demo: +- +-.. image:: https://user-images.githubusercontent.com/1155573/56745335-8b158a00-6750-11e9-8776-83fa675939c4.png +- :target: https://notebooks.ai/demo/gh/Julian/jsonschema +- :alt: Open Live Demo +- +- +-Online demo Notebook will look similar to this: +- +- +-.. image:: https://user-images.githubusercontent.com/1155573/56820861-5c1c1880-6823-11e9-802a-ce01c5ec574f.gif +- :alt: Open Live Demo +- :width: 480 px ++Try ``jsonschema`` interactively in this online `demo `_. + + + Release Notes diff --git a/debian/patches/remove-TestBuiltinFormats-hack.patch b/debian/patches/remove-TestBuiltinFormats-hack.patch deleted file mode 100644 index e80bc01e039a52ea87d3172674a22e2828b3b26e..0000000000000000000000000000000000000000 --- a/debian/patches/remove-TestBuiltinFormats-hack.patch +++ /dev/null @@ -1,45 +0,0 @@ -Description: Remove TestBuiltinFormats hack - What upstream does is a hack that has all the chances to fail. Let's remove - this test. -Author: Thomas Goirand -Forwarded: no -Last-Update: 2019-09-24 - ---- python-jsonschema-3.0.2.orig/jsonschema/tests/test_validators.py -+++ python-jsonschema-3.0.2/jsonschema/tests/test_validators.py -@@ -1348,35 +1348,6 @@ class TestDraft7Validator(ValidatorTestM - invalid = {"type": "integer"}, "foo" - - --class TestBuiltinFormats(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. -- """ -- -- # These tests belong upstream. -- # See https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/246 -- -- --for Validator, checker in ( -- (validators.Draft3Validator, jsonschema.draft3_format_checker), -- (validators.Draft4Validator, jsonschema.draft4_format_checker), -- (validators.Draft6Validator, jsonschema.draft6_format_checker), -- (validators.Draft7Validator, jsonschema.draft7_format_checker), --): -- for format in checker.checkers: -- def test(self, checker=checker, format=format): -- validator = Validator({"format": format}, format_checker=checker) -- validator.validate(123) -- -- name = "test_{}_{}_ignores_non_strings".format( -- Validator.__name__, format, -- ) -- test.__name__ = name -- setattr(TestBuiltinFormats, name, test) -- -- - class TestValidatorFor(SynchronousTestCase): - def test_draft_3(self): - schema = {"$schema": "http://json-schema.org/draft-03/schema"} diff --git a/debian/patches/series b/debian/patches/series index bc7a8047d8a6edabe3ca4a850a97b4dd413d774a..48b887b322b9aa7f1f2349a75655e1c5289f1783 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,2 +1,2 @@ -remove-TestBuiltinFormats-hack.patch remove-sphinxcontrib.spelling-from-doc-conf.py.patch +fix-lintian-privacy-breach.patch diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000000000000000000000000000000000000..9060ad9f9ec7cef1a13b4bf704c99ebba7224676 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/Julian/jsonschema/issues +Bug-Submit: https://github.com/Julian/jsonschema/issues/new +Repository: https://github.com/Julian/jsonschema.git +Repository-Browse: https://github.com/Julian/jsonschema diff --git a/docs/conf.py b/docs/conf.py index 83499f00b57f900154259d49a4e1324d704f07f5..1ec003ce40f811dd847f68e47b0ef72414fd9ba8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,7 @@ from textwrap import dedent import os +import re import sys import jsonschema @@ -238,6 +239,15 @@ texinfo_documents = [ # How to display URL addresses: "footnote", "no", or "inline". # texinfo_show_urls = "footnote" +# -- Options for the linkcheck builder ------------------------------------ + + +def entire_domain(host): + return r"http.?://" + re.escape(host) + r"($|/.*)" + + +linkcheck_ignore = [entire_domain("codecov.io")] + # -- Options for sphinxcontrib-spelling ----------------------------------- spelling_word_list_filename = "spelling-wordlist.txt" diff --git a/docs/errors.rst b/docs/errors.rst index 708425fc9d1c283155ca2b64c322917f6591de77..d3827026ed482166e67d658e2da8821db5b717b6 100644 --- a/docs/errors.rst +++ b/docs/errors.rst @@ -9,8 +9,6 @@ raised or returned, depending on which method or function is used. .. autoexception:: ValidationError - The instance didn't properly validate under the provided schema. - The information carried by an error roughly breaks down into: =============== ================= ======================== @@ -119,8 +117,6 @@ raised. .. autoexception:: SchemaError - The provided schema is malformed. - The same attributes are present as for `ValidationError`\s. @@ -360,37 +356,6 @@ to guess the most relevant error in a given bunch. .. autofunction:: best_match - Try to find an error that appears to be the best match among given errors. - - In general, errors that are higher up in the instance (i.e. for which - `ValidationError.path` is shorter) are considered better matches, - since they indicate "more" is wrong with the instance. - - If the resulting match is either :validator:`oneOf` or :validator:`anyOf`, - the *opposite* assumption is made -- i.e. the deepest error is picked, - since these validators only need to match once, and any other errors may - not be relevant. - - :argument collections.Iterable errors: the errors to select from. Do not - provide a mixture of errors from different validation attempts - (i.e. from different instances or schemas), since it won't - produce sensical output. - :argument callable key: the key to use when sorting errors. See - `relevance` and transitively `by_relevance` for more - details (the default is to sort with the defaults of that function). - Changing the default is only useful if you want to change the function - that rates errors but still want the error context descent done by - this function. - - Returns: - - the best matching error, or ``None`` if the iterable was empty - - .. note:: - - This function is a heuristic. Its return value may change for a given - set of inputs from version to version if better heuristics are added. - .. function:: relevance(validation_error) @@ -432,14 +397,3 @@ to guess the most relevant error in a given bunch. .. autofunction:: by_relevance - - Create a key function that can be used to sort errors by relevance. - - :argument set weak: a collection of validator names to consider to - be "weak". If there are two errors at the same level of the - instance and one is in the set of weak validator names, the - other error will take priority. By default, :validator:`anyOf` - and :validator:`oneOf` are considered weak validators and will - be superseded by other same-level validation errors. - :argument set strong: a collection of validator names to consider to - be "strong" diff --git a/docs/faq.rst b/docs/faq.rst index 889b3065b90311cdc05954b96880aa7fec90b52a..9e6bca93a763da59b17c4a628e99eb3835f86332 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -9,19 +9,21 @@ Why doesn't my schema's default property set the default on my instance? The basic answer is that the specification does not require that :validator:`default` actually do anything. -For an inkling as to *why* it doesn't actually do anything, consider that none -of the other validators modify the instance either. More importantly, having -:validator:`default` modify the instance can produce quite peculiar things. -It's perfectly valid (and perhaps even useful) to have a default that is not -valid under the schema it lives in! So an instance modified by the default -would pass validation the first time, but fail the second! - -Still, filling in defaults is a thing that is useful. `jsonschema` allows -you to `define your own validator classes and callables `, so you can -easily create an `jsonschema.IValidator` that does do default setting. Here's -some code to get you started. (In this code, we add the default properties to -each object *before* the properties are validated, so the default values -themselves will need to be valid under the schema.) +For an inkling as to *why* it doesn't actually do anything, consider +that none of the other validators modify the instance either. More +importantly, having :validator:`default` modify the instance can produce +quite peculiar things. It's perfectly valid (and perhaps even useful) +to have a default that is not valid under the schema it lives in! So an +instance modified by the default would pass validation the first time, +but fail the second! + +Still, filling in defaults is a thing that is useful. `jsonschema` +allows you to `define your own validator classes and callables +`, so you can easily create an `jsonschema.IValidator` that +does do default setting. Here's some code to get you started. (In +this code, we add the default properties to each object *before* the +properties are validated, so the default values themselves will need to +be valid under the schema.) .. code-block:: python @@ -59,8 +61,8 @@ themselves will need to be valid under the schema.) See the above-linked document for more info on how this works, but -basically, it just extends the :validator:`properties` validator on a -`jsonschema.Draft7Validator` to then go ahead and update all the +basically, it just extends the :validator:`properties` validator on +a `jsonschema.Draft7Validator` to then go ahead and update all the defaults. .. note:: @@ -100,8 +102,9 @@ defaults. DefaultValidatingDraft7Validator(schema).validate(obj) assert obj == {'outer-object': {'inner-object': 'INNER-DEFAULT'}} - ...but if you don't provide a default value for your object, - then it won't be instantiated at all, much less populated with default properties. + ...but if you don't provide a default value for your object, then + it won't be instantiated at all, much less populated with default + properties. .. code-block:: python @@ -114,37 +117,45 @@ defaults. How do jsonschema version numbers work? --------------------------------------- -``jsonschema`` tries to follow the `Semantic Versioning `_ -specification. +``jsonschema`` tries to follow the `Semantic Versioning +`_ specification. -This means broadly that no backwards-incompatible changes should be made in -minor releases (and certainly not in dot releases). +This means broadly that no backwards-incompatible changes should be made +in minor releases (and certainly not in dot releases). -The full picture requires defining what constitutes a backwards-incompatible -change. +The full picture requires defining what constitutes a +backwards-incompatible change. -The following are simple examples of things considered public API, and -therefore should *not* be changed without bumping a major version number: +The following are simple examples of things considered public API, +and therefore should *not* be changed without bumping a major version +number: - * module names and contents, when not marked private by Python convention - (a single leading underscore) + * module names and contents, when not marked private by Python + convention (a single leading underscore) * function and object signature (parameter order and name) -The following are *not* considered public API and may change without notice: +The following are *not* considered public API and may change without +notice: * the exact wording and contents of error messages; typical reasons to do this seem to involve unit tests. API users are encouraged to use the extensive introspection provided in - `jsonschema.exceptions.ValidationError`\s instead to make - meaningful assertions about what failed. + `jsonschema.exceptions.ValidationError`\s instead to make meaningful + assertions about what failed. * the order in which validation errors are returned or raised - * the ``compat.py`` module, which is for internal compatibility use + * the contents of the ``jsonschema.tests`` package + + * the contents of the ``jsonschema.benchmarks`` package + + * the ``jsonschema.compat`` module, which is for internal + compatibility use * anything marked private -With the exception of the last two of those, flippant changes are avoided, but -changes can and will be made if there is improvement to be had. Feel free to -open an issue ticket if there is a specific issue or question worth raising. +With the exception of the last two of those, flippant changes are +avoided, but changes can and will be made if there is improvement to be +had. Feel free to open an issue ticket if there is a specific issue or +question worth raising. diff --git a/docs/index.rst b/docs/index.rst index 1fd4ba6327465dde51a547c0cd787f71093afe6f..e4d72527f57c321c3a09a3bf8df45200b2152fba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,43 +1,9 @@ -========== -jsonschema -========== - - .. module:: jsonschema +.. include:: ../README.rst -``jsonschema`` is an implementation of `JSON Schema `_ -for Python (supporting 2.7+ including Python 3). - -.. code-block:: python - - >>> from jsonschema import validate - - >>> # A sample schema, like what we'd get from json.load() - >>> schema = { - ... "type" : "object", - ... "properties" : { - ... "price" : {"type" : "number"}, - ... "name" : {"type" : "string"}, - ... }, - ... } - - >>> # If no exception is raised by validate(), the instance is valid. - >>> validate(instance={"name" : "Eggs", "price" : 34.99}, schema=schema) - - >>> validate( - ... instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema, - ... ) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - ValidationError: 'Invalid' is not of type 'number' - - -You can find further information (installation instructions, mailing list) -as well as the source code and issue tracker on our -`GitHub page `__. - -Contents: +Contents +-------- .. toctree:: :maxdepth: 2 diff --git a/docs/jsonschema_role.py b/docs/jsonschema_role.py index 6c7b1440aad1f236729b96ad3e8677c2c1a0e0a6..6f61cdb82ca3e8b157a35f182abd5a491bee59bb 100644 --- a/docs/jsonschema_role.py +++ b/docs/jsonschema_role.py @@ -12,7 +12,8 @@ import certifi from lxml import html -VALIDATION_SPEC = "https://json-schema.org/draft-04/json-schema-validation.html" +__version__ = "1.0.0" +VALIDATION_SPEC = "https://json-schema.org/draft-07/json-schema-validation.html" def setup(app): @@ -24,7 +25,6 @@ def setup(app): app (sphinx.application.Sphinx): the Sphinx application context - """ app.add_config_value("cache_path", "_cache", "") @@ -39,6 +39,8 @@ def setup(app): spec = fetch_or_load(path) app.add_role("validator", docutils_sucks(spec)) + return dict(version=__version__, parallel_read_safe=True) + def fetch_or_load(spec_path): """ @@ -49,7 +51,6 @@ def fetch_or_load(spec_path): cache_path: the path to a cached specification - """ headers = {} @@ -81,12 +82,11 @@ def docutils_sucks(spec): It doesn't allow using a class because it does stupid stuff like try to set attributes on the callable object rather than just keeping a dict. - """ base_url = VALIDATION_SPEC - ref_url = "https://json-schema.org/draft-04/json-schema-core.html#rfc.section.4.1" - schema_url = "https://json-schema.org/draft-04/json-schema-core.html#rfc.section.6" + ref_url = "https://json-schema.org/draft-07/json-schema-core.html#rfc.section.8.3" + schema_url = "https://json-schema.org/draft-07/json-schema-core.html#rfc.section.7" def validator(name, raw_text, text, lineno, inliner): """ @@ -120,7 +120,6 @@ def docutils_sucks(spec): a 2-tuple of nodes to insert into the document and an iterable of system messages, both possibly empty - """ if text == "$ref": diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..0c4f8bca512ebcf840543d4c1a2032b8056f78b1 --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,3 @@ +lxml +sphinx +sphinxcontrib-spelling diff --git a/docs/requirements.txt b/docs/requirements.txt index eb3007ae5fc9347c80baa697a0dac2e6e5c086f2..fd569829f85c783421a4b94eced222127a29b7f3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,36 @@ -certifi -lxml -sphinx==1.6.7 -sphinxcontrib-spelling +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile requirements.in +# +alabaster==0.7.12 # via sphinx +babel==2.7.0 # via sphinx +certifi==2019.9.11 # via requests +chardet==3.0.4 # via requests +docutils==0.15.2 # via sphinx +idna==2.8 # via requests +imagesize==1.1.0 # via sphinx +jinja2==2.10.3 # via sphinx +lxml==4.4.1 +markupsafe==1.1.1 # via jinja2 +packaging==19.2 # via sphinx +pyenchant==2.0.0 # via sphinxcontrib-spelling +pygments==2.4.2 # via sphinx +pyparsing==2.4.4 # via packaging +pytz==2019.3 # via babel +requests==2.22.0 # via sphinx +six==1.13.0 # via packaging, sphinxcontrib-spelling +snowballstemmer==2.0.0 # via sphinx +sphinx==2.2.1 +sphinxcontrib-applehelp==1.0.1 # via sphinx +sphinxcontrib-devhelp==1.0.1 # via sphinx +sphinxcontrib-htmlhelp==1.0.2 # via sphinx +sphinxcontrib-jsmath==1.0.1 # via sphinx +sphinxcontrib-qthelp==1.0.2 # via sphinx +sphinxcontrib-serializinghtml==1.1.3 # via sphinx +sphinxcontrib-spelling==4.3.0 +urllib3==1.25.6 # via requests + +# The following packages are considered to be unsafe in a requirements file: +# setuptools==41.6.0 # via sphinx diff --git a/docs/spelling-wordlist.txt b/docs/spelling-wordlist.txt index e43b09cefc6a34d23cc681ae091b9022d3cacae3..496c56e1492762a66b7373e650a78c8e89c0eff4 100644 --- a/docs/spelling-wordlist.txt +++ b/docs/spelling-wordlist.txt @@ -8,13 +8,18 @@ ValidationError th callables deque +dereferences hostname implementers indices # ipv4/6, sigh... ipv iterable +iteratively jsonschema +majorly +metaschema +online pre programmatically recurses @@ -28,3 +33,10 @@ validator validators versioned schemas + +Zac +HD + +Berman +Freenode +GPL diff --git a/docs/validate.rst b/docs/validate.rst index 5a4448ddabec8af278258e5ac0aee9370efb8124..8b15962684956e053ea209cd99561a9a9997f1b1 100644 --- a/docs/validate.rst +++ b/docs/validate.rst @@ -321,14 +321,26 @@ to validate. Their names can be viewed by inspecting the `FormatChecker.checkers` attribute. Certain checkers will only be available if an appropriate package is available for use. The easiest way to ensure you have what is needed is to install ``jsonschema`` using the -``format`` setuptools extra -- i.e. +``format`` or ``format_nongpl`` setuptools extra -- i.e. .. code-block:: sh $ pip install jsonschema[format] -which will install all of the below dependencies for all formats. The -more specific list of available checkers, along with their requirement +which will install all of the below dependencies for all formats. + +Or if you want to install MIT-license compatible dependencies only: + +.. code-block:: sh + + $ pip install jsonschema[format_nongpl] + +The non-GPL extra is intended to not install any direct dependencies +that are GPL (but that of course end-users should do their own verification). +At the moment, it supports all the available checkers except for ``iri`` and +``iri-reference``. + +The more specific list of available checkers, along with their requirement (if any,) are listed below. .. note:: @@ -342,7 +354,7 @@ Checker Notes ========================= ==================== ``color`` requires webcolors_ ``date`` -``date-time`` requires strict-rfc3339_ +``date-time`` requires strict-rfc3339_ or rfc3339-validator_ ``email`` ``hostname`` ``idn-hostname`` requires idna_ @@ -353,9 +365,9 @@ Checker Notes ``json-pointer`` requires jsonpointer_ ``regex`` ``relative-json-pointer`` requires jsonpointer_ -``time`` requires strict-rfc3339_ -``uri`` requires rfc3987_ -``uri-reference`` requires rfc3987_ +``time`` requires strict-rfc3339_ or rfc3339-validator_ +``uri`` requires rfc3987_ or rfc3986-validator_ +``uri-reference`` requires rfc3987_ or rfc3986-validator_ ========================= ==================== @@ -365,7 +377,8 @@ Checker Notes .. _rfc5322: https://tools.ietf.org/html/rfc5322#section-3.4.1 .. _strict-rfc3339: https://pypi.org/pypi/strict-rfc3339/ .. _webcolors: https://pypi.org/pypi/webcolors/ - +.. _rfc3339-validator: https://pypi.org/project/rfc3339-validator/ +.. _rfc3986-validator: https://pypi.org/project/rfc3986-validator/ .. note:: diff --git a/json/.travis.yml b/json/.travis.yml index 9c50823158bf6f609e6f321635ef9ef75ab61f38..f65e40bb852449e2652c152b01d66dc86ed0d875 100644 --- a/json/.travis.yml +++ b/json/.travis.yml @@ -1,9 +1,8 @@ language: python -python: "2.7" +python: "3.7" node_js: "9" install: - pip install tox - - npm install script: - tox - - npm test + - npm install && npm test || true diff --git a/json/README.md b/json/README.md index 635f87c24d2bad09a7121027de3b9257a743b94b..f65934c4f530a7e12528ba318f8806ac7c980570 100644 --- a/json/README.md +++ b/json/README.md @@ -67,8 +67,8 @@ This suite is being used by: ### Clojure ### +* [jinx](https://github.com/juxt/jinx) * [json-schema](https://github.com/tatut/json-schema) -* [JSON Schema for Clojure(Script) (JUXT)](https://github.com/juxt/json-schema) ### Coffeescript ### @@ -80,7 +80,7 @@ This suite is being used by: ### Dart ### -* [json_schema](https://github.com/patefacio/json_schema) +* [json_schema](https://github.com/patefacio/json_schema) ### Elixir ### @@ -92,7 +92,7 @@ This suite is being used by: ### Go ### -* [gojsonschema](https://github.com/sigu-399/gojsonschema) +* [gojsonschema](https://github.com/sigu-399/gojsonschema) * [validate-json](https://github.com/cesanta/validate-json) ### Haskell ### @@ -127,11 +127,12 @@ This suite is being used by: ### Node.js ### -The JSON Schema Test Suite is also available as an -[npm](https://www.npmjs.com/package/json-schema-test-suite) package. -Node-specific support is maintained on the [node branch](https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/node). -See [NODE-README.md](https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/node/NODE-README.md) -for more information. +For node.js developers, the suite is also available as an +[npm](https://www.npmjs.com/package/@json-schema-org/tests) package. + +Node-specific support is maintained in a [separate +repository](https://github.com/json-schema-org/json-schema-test-suite-npm) +which also welcomes your contributions! ### .NET ### diff --git a/json/bin/jsonschema_suite b/json/bin/jsonschema_suite index 7c181f9dc2f08ee63bcb9d73606459546255b997..6b1c486450b3e5b6c401d10327c48f25d02c35a3 100755 --- a/json/bin/jsonschema_suite +++ b/json/bin/jsonschema_suite @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 from __future__ import print_function from pprint import pformat import argparse @@ -99,11 +99,13 @@ class SanityTests(unittest.TestCase): def test_all_descriptions_have_reasonable_length(self): for case in cases(self.test_files): - descript = case["description"] + description = case["description"] self.assertLess( - len(descript), - 60, - "%r is too long! (keep it to less than 60 chars)" % (descript,) + len(description), + 70, + "%r is too long! (keep it to less than 70 chars)" % ( + description, + ), ) def test_all_descriptions_are_unique(self): @@ -149,7 +151,7 @@ class SanityTests(unittest.TestCase): expected = { os.path.join(REMOTES_DIR, path): contents - for path, contents in REMOTES.iteritems() + for path, contents in REMOTES.items() } missing = set(files).symmetric_difference(expected) @@ -217,8 +219,9 @@ def main(arguments): if e.errno != errno.EEXIST: raise - with open(filepath, "wb") as out_file: + with open(filepath, "w") as out_file: json.dump(schema, out_file, indent=4, sort_keys=True) + out_file.write("\n") elif arguments.command == "serve": try: from flask import Flask, jsonify diff --git a/json/remotes/folder/folderInteger.json b/json/remotes/folder/folderInteger.json index dbe5c758ee3c3092bd155a63c3efd4892125ca02..8b50ea30859bc5ac8c05180e2a595f3ca205e640 100644 --- a/json/remotes/folder/folderInteger.json +++ b/json/remotes/folder/folderInteger.json @@ -1,3 +1,3 @@ { "type": "integer" -} \ No newline at end of file +} diff --git a/json/remotes/integer.json b/json/remotes/integer.json index dbe5c758ee3c3092bd155a63c3efd4892125ca02..8b50ea30859bc5ac8c05180e2a595f3ca205e640 100644 --- a/json/remotes/integer.json +++ b/json/remotes/integer.json @@ -1,3 +1,3 @@ { "type": "integer" -} \ No newline at end of file +} diff --git a/json/remotes/name-defs.json b/json/remotes/name-defs.json index 1a4fe4ac2b08aaca194190dd256c885dd8b17aa5..1dab4a4343d304270cf9a4b8984d509b764123d0 100644 --- a/json/remotes/name-defs.json +++ b/json/remotes/name-defs.json @@ -2,8 +2,12 @@ "$defs": { "orNull": { "anyOf": [ - {"type": "null"}, - {"$ref": "#"} + { + "type": "null" + }, + { + "$ref": "#" + } ] } }, diff --git a/json/remotes/name.json b/json/remotes/name.json index 19ba093552cb8bcbf7790fde2c317875f3e8fe31..fceacb8097cb8670ab4c1821e4c74a642984baf6 100644 --- a/json/remotes/name.json +++ b/json/remotes/name.json @@ -2,8 +2,12 @@ "definitions": { "orNull": { "anyOf": [ - {"type": "null"}, - {"$ref": "#"} + { + "type": "null" + }, + { + "$ref": "#" + } ] } }, diff --git a/json/remotes/subSchemas.json b/json/remotes/subSchemas.json index 8b6d8f842fc0ddc8a114cb6ebc000f2b2080a9c3..9f8030bceb01046c38279b6691acc1731ea18830 100644 --- a/json/remotes/subSchemas.json +++ b/json/remotes/subSchemas.json @@ -1,8 +1,8 @@ { "integer": { "type": "integer" - }, + }, "refToInteger": { "$ref": "#/integer" } -} \ No newline at end of file +} diff --git a/json/tests/draft2019-06/optional/ecmascript-regex.json b/json/tests/draft2019-06/optional/ecmascript-regex.json deleted file mode 100644 index 08dc9360b870ec759477f8d987a9df115275c735..0000000000000000000000000000000000000000 --- a/json/tests/draft2019-06/optional/ecmascript-regex.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "description": "ECMA 262 regex non-compliance", - "schema": { "format": "regex" }, - "tests": [ - { - "description": "ECMA 262 has no support for \\Z anchor from .NET", - "data": "^\\S(|(.|\\n)*\\S)\\Z", - "valid": false - } - ] - } -] diff --git a/json/tests/draft2019-06/additionalItems.json b/json/tests/draft2019-09/additionalItems.json similarity index 100% rename from json/tests/draft2019-06/additionalItems.json rename to json/tests/draft2019-09/additionalItems.json diff --git a/json/tests/draft2019-06/additionalProperties.json b/json/tests/draft2019-09/additionalProperties.json similarity index 100% rename from json/tests/draft2019-06/additionalProperties.json rename to json/tests/draft2019-09/additionalProperties.json diff --git a/json/tests/draft2019-06/allOf.json b/json/tests/draft2019-09/allOf.json similarity index 100% rename from json/tests/draft2019-06/allOf.json rename to json/tests/draft2019-09/allOf.json diff --git a/json/tests/draft2019-09/anchor.json b/json/tests/draft2019-09/anchor.json new file mode 100644 index 0000000000000000000000000000000000000000..06b0ba4d25b653cb36c527280b0ce5e116f50686 --- /dev/null +++ b/json/tests/draft2019-09/anchor.json @@ -0,0 +1,87 @@ +[ + { + "description": "Location-independent identifier", + "schema": { + "allOf": [{ + "$ref": "#foo" + }], + "$defs": { + "A": { + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "allOf": [{ + "$ref": "http://localhost:1234/bar#foo" + }], + "$defs": { + "A": { + "$id": "http://localhost:1234/bar", + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "$id": "http://localhost:1234/root", + "allOf": [{ + "$ref": "http://localhost:1234/nested.json#foo" + }], + "$defs": { + "A": { + "$id": "nested.json", + "$defs": { + "B": { + "$anchor": "foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + } +] diff --git a/json/tests/draft2019-06/anyOf.json b/json/tests/draft2019-09/anyOf.json similarity index 100% rename from json/tests/draft2019-06/anyOf.json rename to json/tests/draft2019-09/anyOf.json diff --git a/json/tests/draft2019-06/boolean_schema.json b/json/tests/draft2019-09/boolean_schema.json similarity index 100% rename from json/tests/draft2019-06/boolean_schema.json rename to json/tests/draft2019-09/boolean_schema.json diff --git a/json/tests/draft2019-06/const.json b/json/tests/draft2019-09/const.json similarity index 100% rename from json/tests/draft2019-06/const.json rename to json/tests/draft2019-09/const.json diff --git a/json/tests/draft2019-06/contains.json b/json/tests/draft2019-09/contains.json similarity index 100% rename from json/tests/draft2019-06/contains.json rename to json/tests/draft2019-09/contains.json diff --git a/json/tests/draft2019-06/default.json b/json/tests/draft2019-09/default.json similarity index 100% rename from json/tests/draft2019-06/default.json rename to json/tests/draft2019-09/default.json diff --git a/json/tests/draft2019-06/defs.json b/json/tests/draft2019-09/defs.json similarity index 77% rename from json/tests/draft2019-06/defs.json rename to json/tests/draft2019-09/defs.json index e3c298d37aab10208c545e2c7eb0e2c9ec15965b..f2fbec42b26492030be796ac4653e8c825255d91 100644 --- a/json/tests/draft2019-06/defs.json +++ b/json/tests/draft2019-09/defs.json @@ -1,7 +1,7 @@ [ { "description": "valid definition", - "schema": {"$ref": "http://json-schema.org/draft/2019-06/schema#"}, + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, "tests": [ { "description": "valid definition schema", @@ -12,7 +12,7 @@ }, { "description": "invalid definition", - "schema": {"$ref": "http://json-schema.org/draft/2019-06/schema#"}, + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, "tests": [ { "description": "invalid definition schema", diff --git a/json/tests/draft2019-06/dependencies.json b/json/tests/draft2019-09/dependencies.json similarity index 100% rename from json/tests/draft2019-06/dependencies.json rename to json/tests/draft2019-09/dependencies.json diff --git a/json/tests/draft2019-06/enum.json b/json/tests/draft2019-09/enum.json similarity index 100% rename from json/tests/draft2019-06/enum.json rename to json/tests/draft2019-09/enum.json diff --git a/json/tests/draft2019-06/exclusiveMaximum.json b/json/tests/draft2019-09/exclusiveMaximum.json similarity index 100% rename from json/tests/draft2019-06/exclusiveMaximum.json rename to json/tests/draft2019-09/exclusiveMaximum.json diff --git a/json/tests/draft2019-06/exclusiveMinimum.json b/json/tests/draft2019-09/exclusiveMinimum.json similarity index 100% rename from json/tests/draft2019-06/exclusiveMinimum.json rename to json/tests/draft2019-09/exclusiveMinimum.json diff --git a/json/tests/draft2019-09/format.json b/json/tests/draft2019-09/format.json new file mode 100644 index 0000000000000000000000000000000000000000..93305f5cd1a99bf0cd19ffb501ab66f7fe0314b8 --- /dev/null +++ b/json/tests/draft2019-09/format.json @@ -0,0 +1,614 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/json/tests/draft2019-06/if-then-else.json b/json/tests/draft2019-09/if-then-else.json similarity index 98% rename from json/tests/draft2019-06/if-then-else.json rename to json/tests/draft2019-09/if-then-else.json index 37a229c83f5cc49b8d5406db5d4114e87fda8f49..be7328163d4b2ddf579efac25f7c3cf0238ab54a 100644 --- a/json/tests/draft2019-06/if-then-else.json +++ b/json/tests/draft2019-09/if-then-else.json @@ -174,7 +174,7 @@ }, "tests": [ { - "description": "valid, but woud have been invalid through then", + "description": "valid, but would have been invalid through then", "data": -100, "valid": true }, diff --git a/json/tests/draft2019-06/items.json b/json/tests/draft2019-09/items.json similarity index 100% rename from json/tests/draft2019-06/items.json rename to json/tests/draft2019-09/items.json diff --git a/json/tests/draft2019-06/maxItems.json b/json/tests/draft2019-09/maxItems.json similarity index 100% rename from json/tests/draft2019-06/maxItems.json rename to json/tests/draft2019-09/maxItems.json diff --git a/json/tests/draft2019-06/maxLength.json b/json/tests/draft2019-09/maxLength.json similarity index 100% rename from json/tests/draft2019-06/maxLength.json rename to json/tests/draft2019-09/maxLength.json diff --git a/json/tests/draft2019-06/maxProperties.json b/json/tests/draft2019-09/maxProperties.json similarity index 100% rename from json/tests/draft2019-06/maxProperties.json rename to json/tests/draft2019-09/maxProperties.json diff --git a/json/tests/draft2019-06/maximum.json b/json/tests/draft2019-09/maximum.json similarity index 100% rename from json/tests/draft2019-06/maximum.json rename to json/tests/draft2019-09/maximum.json diff --git a/json/tests/draft2019-06/minItems.json b/json/tests/draft2019-09/minItems.json similarity index 100% rename from json/tests/draft2019-06/minItems.json rename to json/tests/draft2019-09/minItems.json diff --git a/json/tests/draft2019-06/minLength.json b/json/tests/draft2019-09/minLength.json similarity index 100% rename from json/tests/draft2019-06/minLength.json rename to json/tests/draft2019-09/minLength.json diff --git a/json/tests/draft2019-06/minProperties.json b/json/tests/draft2019-09/minProperties.json similarity index 100% rename from json/tests/draft2019-06/minProperties.json rename to json/tests/draft2019-09/minProperties.json diff --git a/json/tests/draft2019-06/minimum.json b/json/tests/draft2019-09/minimum.json similarity index 100% rename from json/tests/draft2019-06/minimum.json rename to json/tests/draft2019-09/minimum.json diff --git a/json/tests/draft2019-06/multipleOf.json b/json/tests/draft2019-09/multipleOf.json similarity index 100% rename from json/tests/draft2019-06/multipleOf.json rename to json/tests/draft2019-09/multipleOf.json diff --git a/json/tests/draft2019-06/not.json b/json/tests/draft2019-09/not.json similarity index 100% rename from json/tests/draft2019-06/not.json rename to json/tests/draft2019-09/not.json diff --git a/json/tests/draft2019-06/oneOf.json b/json/tests/draft2019-09/oneOf.json similarity index 100% rename from json/tests/draft2019-06/oneOf.json rename to json/tests/draft2019-09/oneOf.json diff --git a/json/tests/draft2019-06/optional/bignum.json b/json/tests/draft2019-09/optional/bignum.json similarity index 100% rename from json/tests/draft2019-06/optional/bignum.json rename to json/tests/draft2019-09/optional/bignum.json diff --git a/json/tests/draft2019-06/optional/content.json b/json/tests/draft2019-09/optional/content.json similarity index 100% rename from json/tests/draft2019-06/optional/content.json rename to json/tests/draft2019-09/optional/content.json diff --git a/json/tests/draft2019-09/optional/ecmascript-regex.json b/json/tests/draft2019-09/optional/ecmascript-regex.json new file mode 100644 index 0000000000000000000000000000000000000000..d82e0feb035652c04fd92a33b8e4813ef8fb0fc3 --- /dev/null +++ b/json/tests/draft2019-09/optional/ecmascript-regex.json @@ -0,0 +1,213 @@ +[ + { + "description": "ECMA 262 regex non-compliance", + "schema": { "format": "regex" }, + "tests": [ + { + "description": "ECMA 262 has no support for \\Z anchor from .NET", + "data": "^\\S(|(.|\\n)*\\S)\\Z", + "valid": false + } + ] + }, + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\a to ascii BEL", + "schema": { + "type": "string", + "pattern": "^\\a$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\a", + "valid": false + }, + { + "description": "matches", + "data": "\u0007", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\w matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches ascii whitespace only", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "latin-1 non-breaking-space does not match (unlike e.g. Python)", + "data": "\u00a0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but ascii whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "latin-1 non-breaking-space matches (unlike e.g. Python)", + "data": "\u00a0", + "valid": true + } + ] + } +] diff --git a/json/tests/draft2019-06/optional/format/date-time.json b/json/tests/draft2019-09/optional/format/date-time.json similarity index 100% rename from json/tests/draft2019-06/optional/format/date-time.json rename to json/tests/draft2019-09/optional/format/date-time.json diff --git a/json/tests/draft2019-06/optional/format/date.json b/json/tests/draft2019-09/optional/format/date.json similarity index 100% rename from json/tests/draft2019-06/optional/format/date.json rename to json/tests/draft2019-09/optional/format/date.json diff --git a/json/tests/draft2019-06/optional/format/email.json b/json/tests/draft2019-09/optional/format/email.json similarity index 100% rename from json/tests/draft2019-06/optional/format/email.json rename to json/tests/draft2019-09/optional/format/email.json diff --git a/json/tests/draft2019-06/optional/format/hostname.json b/json/tests/draft2019-09/optional/format/hostname.json similarity index 100% rename from json/tests/draft2019-06/optional/format/hostname.json rename to json/tests/draft2019-09/optional/format/hostname.json diff --git a/json/tests/draft2019-06/optional/format/idn-email.json b/json/tests/draft2019-09/optional/format/idn-email.json similarity index 100% rename from json/tests/draft2019-06/optional/format/idn-email.json rename to json/tests/draft2019-09/optional/format/idn-email.json diff --git a/json/tests/draft2019-06/optional/format/idn-hostname.json b/json/tests/draft2019-09/optional/format/idn-hostname.json similarity index 100% rename from json/tests/draft2019-06/optional/format/idn-hostname.json rename to json/tests/draft2019-09/optional/format/idn-hostname.json diff --git a/json/tests/draft2019-06/optional/format/ipv4.json b/json/tests/draft2019-09/optional/format/ipv4.json similarity index 100% rename from json/tests/draft2019-06/optional/format/ipv4.json rename to json/tests/draft2019-09/optional/format/ipv4.json diff --git a/json/tests/draft2019-06/optional/format/ipv6.json b/json/tests/draft2019-09/optional/format/ipv6.json similarity index 100% rename from json/tests/draft2019-06/optional/format/ipv6.json rename to json/tests/draft2019-09/optional/format/ipv6.json diff --git a/json/tests/draft2019-06/optional/format/iri-reference.json b/json/tests/draft2019-09/optional/format/iri-reference.json similarity index 100% rename from json/tests/draft2019-06/optional/format/iri-reference.json rename to json/tests/draft2019-09/optional/format/iri-reference.json diff --git a/json/tests/draft2019-06/optional/format/iri.json b/json/tests/draft2019-09/optional/format/iri.json similarity index 100% rename from json/tests/draft2019-06/optional/format/iri.json rename to json/tests/draft2019-09/optional/format/iri.json diff --git a/json/tests/draft2019-06/optional/format/json-pointer.json b/json/tests/draft2019-09/optional/format/json-pointer.json similarity index 100% rename from json/tests/draft2019-06/optional/format/json-pointer.json rename to json/tests/draft2019-09/optional/format/json-pointer.json diff --git a/json/tests/draft2019-06/optional/format/regex.json b/json/tests/draft2019-09/optional/format/regex.json similarity index 100% rename from json/tests/draft2019-06/optional/format/regex.json rename to json/tests/draft2019-09/optional/format/regex.json diff --git a/json/tests/draft2019-06/optional/format/relative-json-pointer.json b/json/tests/draft2019-09/optional/format/relative-json-pointer.json similarity index 100% rename from json/tests/draft2019-06/optional/format/relative-json-pointer.json rename to json/tests/draft2019-09/optional/format/relative-json-pointer.json diff --git a/json/tests/draft2019-06/optional/format/time.json b/json/tests/draft2019-09/optional/format/time.json similarity index 100% rename from json/tests/draft2019-06/optional/format/time.json rename to json/tests/draft2019-09/optional/format/time.json diff --git a/json/tests/draft2019-06/optional/format/uri-reference.json b/json/tests/draft2019-09/optional/format/uri-reference.json similarity index 100% rename from json/tests/draft2019-06/optional/format/uri-reference.json rename to json/tests/draft2019-09/optional/format/uri-reference.json diff --git a/json/tests/draft2019-06/optional/format/uri-template.json b/json/tests/draft2019-09/optional/format/uri-template.json similarity index 92% rename from json/tests/draft2019-06/optional/format/uri-template.json rename to json/tests/draft2019-09/optional/format/uri-template.json index d8396a5a79b10f7d184f318abdae23ee6211e01d..33ab76ee739d0a4ca63d6735a1dbaae22734ca1e 100644 --- a/json/tests/draft2019-06/optional/format/uri-template.json +++ b/json/tests/draft2019-09/optional/format/uri-template.json @@ -1,9 +1,7 @@ [ { "description": "format: uri-template", - "schema": { - "format": "uri-template" - }, + "schema": {"format": "uri-template"}, "tests": [ { "description": "a valid uri-template", diff --git a/json/tests/draft2019-06/optional/format/uri.json b/json/tests/draft2019-09/optional/format/uri.json similarity index 100% rename from json/tests/draft2019-06/optional/format/uri.json rename to json/tests/draft2019-09/optional/format/uri.json diff --git a/json/tests/draft2019-06/optional/zeroTerminatedFloats.json b/json/tests/draft2019-09/optional/zeroTerminatedFloats.json similarity index 100% rename from json/tests/draft2019-06/optional/zeroTerminatedFloats.json rename to json/tests/draft2019-09/optional/zeroTerminatedFloats.json diff --git a/json/tests/draft2019-06/pattern.json b/json/tests/draft2019-09/pattern.json similarity index 100% rename from json/tests/draft2019-06/pattern.json rename to json/tests/draft2019-09/pattern.json diff --git a/json/tests/draft2019-06/patternProperties.json b/json/tests/draft2019-09/patternProperties.json similarity index 100% rename from json/tests/draft2019-06/patternProperties.json rename to json/tests/draft2019-09/patternProperties.json diff --git a/json/tests/draft2019-06/properties.json b/json/tests/draft2019-09/properties.json similarity index 100% rename from json/tests/draft2019-06/properties.json rename to json/tests/draft2019-09/properties.json diff --git a/json/tests/draft2019-06/propertyNames.json b/json/tests/draft2019-09/propertyNames.json similarity index 100% rename from json/tests/draft2019-06/propertyNames.json rename to json/tests/draft2019-09/propertyNames.json diff --git a/json/tests/draft2019-06/ref.json b/json/tests/draft2019-09/ref.json similarity index 81% rename from json/tests/draft2019-06/ref.json rename to json/tests/draft2019-09/ref.json index dbeb7d3bba4ce1016b82fb50cd342de5462aed71..285de55c06c3582c129f8eeb74af1ae8bc850bcc 100644 --- a/json/tests/draft2019-06/ref.json +++ b/json/tests/draft2019-09/ref.json @@ -175,7 +175,7 @@ }, { "description": "remote ref, containing refs itself", - "schema": {"$ref": "http://json-schema.org/draft/2019-06/schema#"}, + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, "tests": [ { "description": "remote ref valid", @@ -355,89 +355,5 @@ "valid": false } ] - }, - { - "description": "Location-independent identifier", - "schema": { - "allOf": [{ - "$ref": "#foo" - }], - "$defs": { - "A": { - "$id": "#foo", - "type": "integer" - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] - }, - { - "description": "Location-independent identifier with absolute URI", - "schema": { - "allOf": [{ - "$ref": "http://localhost:1234/bar#foo" - }], - "$defs": { - "A": { - "$id": "http://localhost:1234/bar#foo", - "type": "integer" - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] - }, - { - "description": "Location-independent identifier with base URI change in subschema", - "schema": { - "$id": "http://localhost:1234/root", - "allOf": [{ - "$ref": "http://localhost:1234/nested.json#foo" - }], - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#foo", - "type": "integer" - } - } - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] } ] diff --git a/json/tests/draft2019-06/refRemote.json b/json/tests/draft2019-09/refRemote.json similarity index 100% rename from json/tests/draft2019-06/refRemote.json rename to json/tests/draft2019-09/refRemote.json diff --git a/json/tests/draft2019-06/required.json b/json/tests/draft2019-09/required.json similarity index 100% rename from json/tests/draft2019-06/required.json rename to json/tests/draft2019-09/required.json diff --git a/json/tests/draft2019-06/type.json b/json/tests/draft2019-09/type.json similarity index 100% rename from json/tests/draft2019-06/type.json rename to json/tests/draft2019-09/type.json diff --git a/json/tests/draft2019-06/uniqueItems.json b/json/tests/draft2019-09/uniqueItems.json similarity index 50% rename from json/tests/draft2019-06/uniqueItems.json rename to json/tests/draft2019-09/uniqueItems.json index 8885ed006feaed1ec76ae1182329beac81bb8348..d312ad71ab302b85d716bb3ff83cf97771e6ad8d 100644 --- a/json/tests/draft2019-06/uniqueItems.json +++ b/json/tests/draft2019-09/uniqueItems.json @@ -85,5 +85,89 @@ "valid": false } ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] } ] diff --git a/json/tests/draft3/format.json b/json/tests/draft3/format.json new file mode 100644 index 0000000000000000000000000000000000000000..827933629406f177432fb323af996e7b2c425e42 --- /dev/null +++ b/json/tests/draft3/format.json @@ -0,0 +1,362 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ip-address"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "host-name"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of CSS colors", + "schema": {"format": "color"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/json/tests/draft3/optional/jsregex.json b/json/tests/draft3/optional/ecmascript-regex.json similarity index 100% rename from json/tests/draft3/optional/jsregex.json rename to json/tests/draft3/optional/ecmascript-regex.json diff --git a/json/tests/draft3/uniqueItems.json b/json/tests/draft3/uniqueItems.json index c1f4ab99c9a485d4d8a9d9f97b4945899cbcd4b5..59e3542ceff248e3005cebc419928358d6e8212e 100644 --- a/json/tests/draft3/uniqueItems.json +++ b/json/tests/draft3/uniqueItems.json @@ -75,5 +75,89 @@ "valid": false } ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] } ] diff --git a/json/tests/draft4/format.json b/json/tests/draft4/format.json new file mode 100644 index 0000000000000000000000000000000000000000..61e4b62a537ad521c231f91cb04491e6a3529d5c --- /dev/null +++ b/json/tests/draft4/format.json @@ -0,0 +1,218 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/json/tests/draft4/optional/ecmascript-regex.json b/json/tests/draft4/optional/ecmascript-regex.json index 08dc9360b870ec759477f8d987a9df115275c735..d82e0feb035652c04fd92a33b8e4813ef8fb0fc3 100644 --- a/json/tests/draft4/optional/ecmascript-regex.json +++ b/json/tests/draft4/optional/ecmascript-regex.json @@ -9,5 +9,205 @@ "valid": false } ] + }, + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\a to ascii BEL", + "schema": { + "type": "string", + "pattern": "^\\a$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\a", + "valid": false + }, + { + "description": "matches", + "data": "\u0007", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\w matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches ascii whitespace only", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "latin-1 non-breaking-space does not match (unlike e.g. Python)", + "data": "\u00a0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but ascii whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "latin-1 non-breaking-space matches (unlike e.g. Python)", + "data": "\u00a0", + "valid": true + } + ] } ] diff --git a/json/tests/draft4/uniqueItems.json b/json/tests/draft4/uniqueItems.json index 8885ed006feaed1ec76ae1182329beac81bb8348..d312ad71ab302b85d716bb3ff83cf97771e6ad8d 100644 --- a/json/tests/draft4/uniqueItems.json +++ b/json/tests/draft4/uniqueItems.json @@ -85,5 +85,89 @@ "valid": false } ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] } ] diff --git a/json/tests/draft6/contains.json b/json/tests/draft6/contains.json index b7ae5a25fea53c5781884139f7ae4efb0508a4d4..67ecbd991ae07797dab231d78f4702507b1159d0 100644 --- a/json/tests/draft6/contains.json +++ b/json/tests/draft6/contains.json @@ -89,6 +89,11 @@ "description": "empty array is invalid", "data": [], "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true } ] } diff --git a/json/tests/draft6/format.json b/json/tests/draft6/format.json new file mode 100644 index 0000000000000000000000000000000000000000..32e81524aa01c1d4c970dce21b81a6f56540c515 --- /dev/null +++ b/json/tests/draft6/format.json @@ -0,0 +1,326 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/json/tests/draft6/optional/ecmascript-regex.json b/json/tests/draft6/optional/ecmascript-regex.json index 08dc9360b870ec759477f8d987a9df115275c735..d82e0feb035652c04fd92a33b8e4813ef8fb0fc3 100644 --- a/json/tests/draft6/optional/ecmascript-regex.json +++ b/json/tests/draft6/optional/ecmascript-regex.json @@ -9,5 +9,205 @@ "valid": false } ] + }, + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\a to ascii BEL", + "schema": { + "type": "string", + "pattern": "^\\a$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\a", + "valid": false + }, + { + "description": "matches", + "data": "\u0007", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\w matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches ascii whitespace only", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "latin-1 non-breaking-space does not match (unlike e.g. Python)", + "data": "\u00a0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but ascii whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "latin-1 non-breaking-space matches (unlike e.g. Python)", + "data": "\u00a0", + "valid": true + } + ] } ] diff --git a/json/tests/draft6/optional/format.json b/json/tests/draft6/optional/format.json index 74743ff61667bd67267d0cdbd5581882e2c60ffd..3dd265fe4f5f48fe0d4bf4d60c2753a5c62392ad 100644 --- a/json/tests/draft6/optional/format.json +++ b/json/tests/draft6/optional/format.json @@ -199,9 +199,7 @@ }, { "description": "format: uri-template", - "schema": { - "format": "uri-template" - }, + "schema": {"format": "uri-template"}, "tests": [ { "description": "a valid uri-template", diff --git a/json/tests/draft6/uniqueItems.json b/json/tests/draft6/uniqueItems.json index 8885ed006feaed1ec76ae1182329beac81bb8348..d312ad71ab302b85d716bb3ff83cf97771e6ad8d 100644 --- a/json/tests/draft6/uniqueItems.json +++ b/json/tests/draft6/uniqueItems.json @@ -85,5 +85,89 @@ "valid": false } ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] } ] diff --git a/json/tests/draft7/contains.json b/json/tests/draft7/contains.json index b7ae5a25fea53c5781884139f7ae4efb0508a4d4..67ecbd991ae07797dab231d78f4702507b1159d0 100644 --- a/json/tests/draft7/contains.json +++ b/json/tests/draft7/contains.json @@ -89,6 +89,11 @@ "description": "empty array is invalid", "data": [], "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true } ] } diff --git a/json/tests/draft7/format.json b/json/tests/draft7/format.json new file mode 100644 index 0000000000000000000000000000000000000000..93305f5cd1a99bf0cd19ffb501ab66f7fe0314b8 --- /dev/null +++ b/json/tests/draft7/format.json @@ -0,0 +1,614 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/json/tests/draft7/if-then-else.json b/json/tests/draft7/if-then-else.json index 37a229c83f5cc49b8d5406db5d4114e87fda8f49..be7328163d4b2ddf579efac25f7c3cf0238ab54a 100644 --- a/json/tests/draft7/if-then-else.json +++ b/json/tests/draft7/if-then-else.json @@ -174,7 +174,7 @@ }, "tests": [ { - "description": "valid, but woud have been invalid through then", + "description": "valid, but would have been invalid through then", "data": -100, "valid": true }, diff --git a/json/tests/draft7/optional/ecmascript-regex.json b/json/tests/draft7/optional/ecmascript-regex.json index 08dc9360b870ec759477f8d987a9df115275c735..d82e0feb035652c04fd92a33b8e4813ef8fb0fc3 100644 --- a/json/tests/draft7/optional/ecmascript-regex.json +++ b/json/tests/draft7/optional/ecmascript-regex.json @@ -9,5 +9,205 @@ "valid": false } ] + }, + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\a to ascii BEL", + "schema": { + "type": "string", + "pattern": "^\\a$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\a", + "valid": false + }, + { + "description": "matches", + "data": "\u0007", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\w matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches ascii whitespace only", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "latin-1 non-breaking-space does not match (unlike e.g. Python)", + "data": "\u00a0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but ascii whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "latin-1 non-breaking-space matches (unlike e.g. Python)", + "data": "\u00a0", + "valid": true + } + ] } ] diff --git a/json/tests/draft7/optional/format/uri-template.json b/json/tests/draft7/optional/format/uri-template.json index d8396a5a79b10f7d184f318abdae23ee6211e01d..33ab76ee739d0a4ca63d6735a1dbaae22734ca1e 100644 --- a/json/tests/draft7/optional/format/uri-template.json +++ b/json/tests/draft7/optional/format/uri-template.json @@ -1,9 +1,7 @@ [ { "description": "format: uri-template", - "schema": { - "format": "uri-template" - }, + "schema": {"format": "uri-template"}, "tests": [ { "description": "a valid uri-template", diff --git a/json/tests/draft7/uniqueItems.json b/json/tests/draft7/uniqueItems.json index 8885ed006feaed1ec76ae1182329beac81bb8348..d0a94d8cf50e297c570b1eae792b0508540d9de8 100644 --- a/json/tests/draft7/uniqueItems.json +++ b/json/tests/draft7/uniqueItems.json @@ -85,5 +85,89 @@ "valid": false } ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] } ] diff --git a/json/tests/latest b/json/tests/latest new file mode 120000 index 0000000000000000000000000000000000000000..90f70dbabc86f9c7f7513b4261a9cd1c66f26d56 --- /dev/null +++ b/json/tests/latest @@ -0,0 +1 @@ +draft2019-09 \ No newline at end of file diff --git a/json/tox.ini b/json/tox.ini index 5301222a8400a9ee79c38396ae620c53b34d3ecb..9c4e94990df036adc93137839717dcd5eb00b090 100644 --- a/json/tox.ini +++ b/json/tox.ini @@ -1,8 +1,9 @@ [tox] minversion = 1.6 -envlist = py27 +envlist = sanity skipsdist = True -[testenv] +[testenv:sanity] +# used just for validating the structure of the test case files themselves deps = jsonschema commands = {envpython} bin/jsonschema_suite check diff --git a/jsonschema/__init__.py b/jsonschema/__init__.py index 0da56aac0245768e4f4ef4b85ac7c05228dd91f7..6b630cdfbbee08e6a5398ae199f625bc4b272095 100644 --- a/jsonschema/__init__.py +++ b/jsonschema/__init__.py @@ -6,7 +6,6 @@ supported JSON Schema versions. Most commonly, `validate` is the quickest way to simply validate a given instance under a schema, and will create a validator for you. - """ from jsonschema.exceptions import ( @@ -28,6 +27,8 @@ from jsonschema.validators import ( RefResolver, validate, ) - -from pkg_resources import get_distribution -__version__ = get_distribution(__name__).version +try: + from importlib import metadata +except ImportError: # for Python<3.8 + import importlib_metadata as metadata +__version__ = metadata.version("jsonschema") diff --git a/jsonschema/_format.py b/jsonschema/_format.py index d3f1345e3fb010348c7197f8cfdfec2f9d867e29..281a7cfcffe9c39758b2146ae13e26f8e52298c3 100644 --- a/jsonschema/_format.py +++ b/jsonschema/_format.py @@ -28,7 +28,6 @@ class FormatChecker(object): The known formats to validate. This argument can be used to limit which formats will be used during validation. - """ checkers = {} @@ -39,6 +38,9 @@ class FormatChecker(object): else: self.checkers = dict((k, self.checkers[k]) for k in formats) + def __repr__(self): + return "".format(sorted(self.checkers)) + def checks(self, format, raises=()): """ Register a decorated function as validating a new format. @@ -57,7 +59,6 @@ class FormatChecker(object): The exception object will be accessible as the `jsonschema.exceptions.ValidationError.cause` attribute of the resulting validation error. - """ def _checks(func): @@ -85,7 +86,6 @@ class FormatChecker(object): Raises: FormatError: if the instance does not conform to ``format`` - """ if format not in self.checkers: @@ -119,7 +119,6 @@ class FormatChecker(object): Returns: bool: whether it conformed - """ try: @@ -249,7 +248,27 @@ else: try: import rfc3987 except ImportError: - pass + try: + from rfc3986_validator import validate_rfc3986 + except ImportError: + pass + else: + @_checks_drafts(name="uri") + def is_uri(instance): + if not isinstance(instance, str_types): + return True + return validate_rfc3986(instance, rule="URI") + + @_checks_drafts( + draft6="uri-reference", + draft7="uri-reference", + raises=ValueError, + ) + def is_uri_reference(instance): + if not isinstance(instance, str_types): + return True + return validate_rfc3986(instance, rule="URI_reference") + else: @_checks_drafts(draft7="iri", raises=ValueError) def is_iri(instance): @@ -281,15 +300,19 @@ else: try: - import strict_rfc3339 + from strict_rfc3339 import validate_rfc3339 except ImportError: - pass -else: + try: + from rfc3339_validator import validate_rfc3339 + except ImportError: + validate_rfc3339 = None + +if validate_rfc3339: @_checks_drafts(name="date-time") def is_datetime(instance): if not isinstance(instance, str_types): return True - return strict_rfc3339.validate_rfc3339(instance) + return validate_rfc3339(instance) @_checks_drafts(draft7="time") def is_time(instance): diff --git a/jsonschema/_types.py b/jsonschema/_types.py index f556dedb608ae8d37fe750b1505aa942563cc85b..a71a4e34bdccdcc047bc1c6cf05e54857be4fe72 100644 --- a/jsonschema/_types.py +++ b/jsonschema/_types.py @@ -104,7 +104,7 @@ class TypeChecker(object): The name of the type to check. - fn (callable): + fn (collections.Callable): A function taking exactly two parameters - the type checker calling the function and the instance to check. diff --git a/jsonschema/_utils.py b/jsonschema/_utils.py index 79c2bc9a47989ba7a960820bc0cad30cf85780ac..ceb880198d140c88b96cead0956c4b8a752e8d4f 100644 --- a/jsonschema/_utils.py +++ b/jsonschema/_utils.py @@ -9,7 +9,6 @@ from jsonschema.compat import MutableMapping, str_types, urlsplit class URIDict(MutableMapping): """ Dictionary which uses normalized URIs as keys. - """ def normalize(self, uri): @@ -41,7 +40,6 @@ class URIDict(MutableMapping): class Unset(object): """ An as-of-yet unset attribute or unprovided default parameter. - """ def __repr__(self): @@ -51,7 +49,6 @@ class Unset(object): def load_schema(name): """ Load a schema from ./schemas/``name``.json and return it. - """ data = pkgutil.get_data("jsonschema", "schemas/{0}.json".format(name)) @@ -61,7 +58,6 @@ def load_schema(name): def indent(string, times=1): """ A dumb version of `textwrap.indent` from Python 3.3. - """ return "\n".join(" " * (4 * times) + line for line in string.splitlines()) @@ -78,7 +74,6 @@ def format_as_index(indices): indices (sequence): The indices to format. - """ if not indices: @@ -94,7 +89,6 @@ def find_additional_properties(instance, schema): / or ``patternProperties``. Assumes ``instance`` is dict-like already. - """ properties = schema.get("properties", {}) @@ -109,7 +103,6 @@ def find_additional_properties(instance, schema): def extras_msg(extras): """ Create an error message for extra items or properties. - """ if len(extras) == 1: @@ -127,7 +120,6 @@ def types_msg(instance, types): be considered to be a description of that object and used as its type. Otherwise the message is simply the reprs of the given ``types``. - """ reprs = [] @@ -147,7 +139,6 @@ def flatten(suitable_for_isinstance): * an arbitrary nested tree of tuples Return a flattened tuple of the given argument. - """ types = set() @@ -167,7 +158,6 @@ def ensure_list(thing): Wrap ``thing`` in a list if it's a single str. Otherwise, return it unchanged. - """ if isinstance(thing, str_types): diff --git a/jsonschema/benchmarks/__init__.py b/jsonschema/benchmarks/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e3dcc689930da95198c251ce806637d6413c8b1e 100644 --- a/jsonschema/benchmarks/__init__.py +++ b/jsonschema/benchmarks/__init__.py @@ -0,0 +1,5 @@ +""" +Benchmarks for validation. + +This package is *not* public API. +""" diff --git a/jsonschema/benchmarks/issue232.py b/jsonschema/benchmarks/issue232.py index 460bb06ccd8be022522ec4c7987eff10dfcec091..65e3aedf79432a36126428f4ecb0cb0dc0c7dd18 100644 --- a/jsonschema/benchmarks/issue232.py +++ b/jsonschema/benchmarks/issue232.py @@ -1,25 +1,26 @@ #!/usr/bin/env python """ -A performance benchmark using the example from issue #232: - -https://github.com/Julian/jsonschema/pull/232 +A performance benchmark using the example from issue #232. +See https://github.com/Julian/jsonschema/pull/232. """ from twisted.python.filepath import FilePath -from perf import Runner +from pyperf import Runner from pyrsistent import m -from jsonschema.tests._suite import Collection +from jsonschema.tests._suite import Version import jsonschema -collection = Collection( +issue232 = Version( path=FilePath(__file__).sibling("issue232"), remotes=m(), name="issue232", - validator=jsonschema.Draft7Validator, ) if __name__ == "__main__": - collection.benchmark(runner=Runner()) + issue232.benchmark( + runner=Runner(), + Validator=jsonschema.Draft4Validator, + ) diff --git a/jsonschema/benchmarks/json_schema_test_suite.py b/jsonschema/benchmarks/json_schema_test_suite.py index c4d3ccd69ed0babea894c2973a6effd5fbb408cb..5add5051df1fdf59e34ba2af597c79ac8a128d1f 100644 --- a/jsonschema/benchmarks/json_schema_test_suite.py +++ b/jsonschema/benchmarks/json_schema_test_suite.py @@ -5,7 +5,7 @@ A performance benchmark using the official test suite. This benchmarks jsonschema using every valid example in the JSON-Schema-Test-Suite. It will take some time to complete. """ -from perf import Runner +from pyperf import Runner from jsonschema.tests._suite import Suite diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 6a503ecd0227a610ac070225b6106b1df2322c62..ab3335b27c55d9ae746f3abb5f6551485858f2c9 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -1,3 +1,6 @@ +""" +The ``jsonschema`` command line. +""" from __future__ import absolute_import import argparse import json @@ -28,7 +31,7 @@ parser.add_argument( dest="instances", type=_json_file, help=( - "a path to a JSON instance (i.e. filename.json)" + "a path to a JSON instance (i.e. filename.json) " "to validate (may be specified multiple times)" ), ) @@ -52,12 +55,12 @@ parser.add_argument( ) parser.add_argument( "--version", - action='version', + action="version", version=__version__, ) parser.add_argument( "schema", - help="the JSON Schema to validate with (i.e. filename.schema)", + help="the JSON Schema to validate with (i.e. schema.json)", type=_json_file, ) diff --git a/jsonschema/compat.py b/jsonschema/compat.py index b8bc643fbe723f1c1e6eb760ec1034676a3af684..47e09804551688c92888f5588e7ff9089fcd0cbd 100644 --- a/jsonschema/compat.py +++ b/jsonschema/compat.py @@ -20,7 +20,7 @@ if PY3: from functools import lru_cache from io import StringIO as NativeIO from urllib.parse import ( - unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit + unquote, urljoin, urlunsplit, SplitResult, urlsplit ) from urllib.request import pathname2url, urlopen str_types = str, @@ -29,9 +29,7 @@ if PY3: else: from itertools import izip as zip # noqa from io import BytesIO as NativeIO - from urlparse import ( - urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa - ) + from urlparse import urljoin, urlunsplit, SplitResult, urlsplit from urllib import pathname2url, unquote # noqa import urllib2 # noqa def urlopen(*args, **kwargs): @@ -44,14 +42,6 @@ else: from functools32 import lru_cache -# On python < 3.3 fragments are not handled properly with unknown schemes -def urlsplit(url): - scheme, netloc, path, query, fragment = _urlsplit(url) - if "#" in path: - path, fragment = path.split("#", 1) - return SplitResult(scheme, netloc, path, query, fragment) - - def urldefrag(url): if "#" in url: s, n, p, q, frag = urlsplit(url) diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 90081821f11a8de395e52e574fc47d54bf118552..691dcffe6c786722db9658b27d8cea1aa1827927 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -1,3 +1,6 @@ +""" +Validation errors, and some surrounding helpers. +""" from collections import defaultdict, deque import itertools import pprint @@ -129,17 +132,28 @@ class _Error(Exception): class ValidationError(_Error): + """ + An instance was invalid under a provided schema. + """ + _word_for_schema_in_error_message = "schema" _word_for_instance_in_error_message = "instance" class SchemaError(_Error): + """ + A schema was invalid under its corresponding metaschema. + """ + _word_for_schema_in_error_message = "metaschema" _word_for_instance_in_error_message = "schema" @attr.s(hash=True) class RefResolutionError(Exception): + """ + A ref could not be resolved. + """ _cause = attr.ib() @@ -148,6 +162,10 @@ class RefResolutionError(Exception): class UndefinedTypeCheck(Exception): + """ + A type checker was asked to check a type it did not have registered. + """ + def __init__(self, type): self.type = type @@ -162,6 +180,10 @@ class UndefinedTypeCheck(Exception): class UnknownType(Exception): + """ + A validator was asked to validate an instance against an unknown type. + """ + def __init__(self, type, instance, schema): self.type = type self.instance = instance @@ -187,6 +209,10 @@ class UnknownType(Exception): class FormatError(Exception): + """ + Validating a format failed. + """ + def __init__(self, message, cause=None): super(FormatError, self).__init__(message, cause) self.message = message @@ -205,7 +231,6 @@ class FormatError(Exception): class ErrorTree(object): """ ErrorTrees make it easier to check which validations failed. - """ _instance = _unset @@ -225,7 +250,6 @@ class ErrorTree(object): def __contains__(self, index): """ Check whether ``instance[index]`` has any errors. - """ return index in self._contents @@ -238,7 +262,6 @@ class ErrorTree(object): is not known by this tree, whatever error would be raised by ``instance.__getitem__`` will be propagated (usually this is some subclass of `exceptions.LookupError`. - """ if self._instance is not _unset and index not in self: @@ -246,22 +269,22 @@ class ErrorTree(object): return self._contents[index] def __setitem__(self, index, value): + """ + Add an error to the tree at the given ``index``. + """ 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 `total_errors`. - + Return the `total_errors`. """ - return self.total_errors def __repr__(self): @@ -271,7 +294,6 @@ class ErrorTree(object): 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)) @@ -279,6 +301,21 @@ class ErrorTree(object): def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES): + """ + Create a key function that can be used to sort errors by relevance. + + Arguments: + weak (set): + a collection of validator names to consider to be "weak". + If there are two errors at the same level of the instance + and one is in the set of weak validator names, the other + error will take priority. By default, :validator:`anyOf` and + :validator:`oneOf` are considered weak validators and will + be superseded by other same-level validation errors. + + strong (set): + a collection of validator names to consider to be "strong" + """ def relevance(error): validator = error.validator return -len(error.path), validator not in weak, validator in strong @@ -289,6 +326,43 @@ relevance = by_relevance() def best_match(errors, key=relevance): + """ + Try to find an error that appears to be the best match among given errors. + + In general, errors that are higher up in the instance (i.e. for which + `ValidationError.path` is shorter) are considered better matches, + since they indicate "more" is wrong with the instance. + + If the resulting match is either :validator:`oneOf` or :validator:`anyOf`, + the *opposite* assumption is made -- i.e. the deepest error is picked, + since these validators only need to match once, and any other errors may + not be relevant. + + Arguments: + errors (collections.Iterable): + + the errors to select from. Do not provide a mixture of + errors from different validation attempts (i.e. from + different instances or schemas), since it won't produce + sensical output. + + key (collections.Callable): + + the key to use when sorting errors. See `relevance` and + transitively `by_relevance` for more details (the default is + to sort with the defaults of that function). Changing the + default is only useful if you want to change the function + that rates errors but still want the error context descent + done by this function. + + Returns: + the best matching error, or ``None`` if the iterable was empty + + .. note:: + + This function is a heuristic. Its return value may change for a given + set of inputs from version to version if better heuristics are added. + """ errors = iter(errors) best = next(errors, None) if best is None: diff --git a/jsonschema/tests/_helpers.py b/jsonschema/tests/_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..70f291fe2ab20f7dd7d2039095ef05c47dc0a5d8 --- /dev/null +++ b/jsonschema/tests/_helpers.py @@ -0,0 +1,5 @@ +def bug(issue=None): + message = "A known bug." + if issue is not None: + message += " See issue #{issue}.".format(issue=issue) + return message diff --git a/jsonschema/tests/_suite.py b/jsonschema/tests/_suite.py index 8db0519508e65f4cd286e591b550a554af077bb9..b68a7b668c94f4fce221ebeaec1f6661ef54e03d 100644 --- a/jsonschema/tests/_suite.py +++ b/jsonschema/tests/_suite.py @@ -1,8 +1,8 @@ """ Python representations of the JSON Schema Test Suite tests. - """ +from functools import partial import json import os import re @@ -71,12 +71,12 @@ class Version(object): name = attr.ib() - def benchmark(self, runner): # pragma: no cover + def benchmark(self, runner, **kwargs): # pragma: no cover for suite in self.tests(): for test in suite: runner.bench_func( - name=test.fully_qualified_name, - func=test.validate_ignoring_errors, + test.fully_qualified_name, + partial(test.validate_ignoring_errors, **kwargs), ) def tests(self): @@ -202,9 +202,11 @@ class _Test(object): reason = skip(self) return unittest.skipIf(reason is not None, reason)(fn) - def validate(self, Validator=None, **kwargs): + def validate(self, Validator, **kwargs): resolver = jsonschema.RefResolver.from_schema( - schema=self.schema, store=self._remotes, + schema=self.schema, + store=self._remotes, + id_of=Validator.ID_OF, ) jsonschema.validate( instance=self.data, @@ -214,9 +216,9 @@ class _Test(object): **kwargs ) - def validate_ignoring_errors(self, **kwargs): # pragma: no cover + def validate_ignoring_errors(self, Validator): # pragma: no cover try: - self.validate(**kwargs) + self.validate(Validator=Validator) except jsonschema.ValidationError: pass diff --git a/jsonschema/tests/test_exceptions.py b/jsonschema/tests/test_exceptions.py index e83801f0e238ab2d08eaf85a419065d281caddef..eae00d76d77dd10a69c248ee8450429574a91b18 100644 --- a/jsonschema/tests/test_exceptions.py +++ b/jsonschema/tests/test_exceptions.py @@ -35,7 +35,6 @@ class TestBestMatch(TestCase): """ A property you *must* match is probably better than one you have to match a part of. - """ validator = Draft4Validator( @@ -56,7 +55,6 @@ class TestBestMatch(TestCase): I.e. since only one of the schemas must match, we look for the most relevant one. - """ validator = Draft4Validator( @@ -82,7 +80,6 @@ class TestBestMatch(TestCase): I.e. since only one of the schemas must match, we look for the most relevant one. - """ validator = Draft4Validator( @@ -104,7 +101,6 @@ class TestBestMatch(TestCase): """ 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( @@ -281,7 +277,6 @@ class TestErrorTree(TestCase): 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( @@ -441,7 +436,6 @@ class TestErrorInitReprStr(TestCase): Check for https://github.com/Julian/jsonschema/issues/164 which rendered exceptions unusable when a `ValidationError` involved instances with an `__eq__` method that returned truthy values. - """ class DontEQMeBro(object): diff --git a/jsonschema/tests/test_format.py b/jsonschema/tests/test_format.py index e9e5f995568d16e8f1f1a9735b97e7f8a44b1727..254985f6156ad56a52b9a5aae10e6ce1ce0e27b2 100644 --- a/jsonschema/tests/test_format.py +++ b/jsonschema/tests/test_format.py @@ -1,6 +1,5 @@ """ Tests for the parts of jsonschema related to the :validator:`format` property. - """ from unittest import TestCase @@ -78,3 +77,13 @@ class TestFormatChecker(TestCase): checker = FormatChecker() with self.assertRaises(FormatError): checker.check(instance="not-an-ipv4", format="ipv4") + + def test_repr(self): + checker = FormatChecker(formats=()) + checker.checks("foo")(lambda thing: True) + checker.checks("bar")(lambda thing: True) + checker.checks("baz")(lambda thing: True) + self.assertEqual( + repr(checker), + "", + ) diff --git a/jsonschema/tests/test_jsonschema_test_suite.py b/jsonschema/tests/test_jsonschema_test_suite.py index 065e319c30a304358058aa7bfa86cb3ef5332f3b..ebccf29735a557e8aa0a8f953aaab38f6a907f4d 100644 --- a/jsonschema/tests/test_jsonschema_test_suite.py +++ b/jsonschema/tests/test_jsonschema_test_suite.py @@ -19,6 +19,7 @@ from jsonschema import ( draft6_format_checker, draft7_format_checker, ) +from jsonschema.tests._helpers import bug from jsonschema.tests._suite import Suite from jsonschema.validators import _DEPRECATED_DEFAULT_TYPES, create @@ -65,17 +66,10 @@ else: return -def bug(issue=None): - message = "A known bug." - if issue is not None: - message += " See issue #{issue}.".format(issue=issue) - return message - - TestDraft3 = DRAFT3.to_unittest_testcase( DRAFT3.tests(), - DRAFT3.optional_tests_of(name="format"), DRAFT3.optional_tests_of(name="bignum"), + DRAFT3.optional_tests_of(name="format"), DRAFT3.optional_tests_of(name="zeroTerminatedFloats"), Validator=Draft3Validator, format_checker=draft3_format_checker, @@ -93,8 +87,8 @@ TestDraft3 = DRAFT3.to_unittest_testcase( TestDraft4 = DRAFT4.to_unittest_testcase( DRAFT4.tests(), - DRAFT4.optional_tests_of(name="format"), DRAFT4.optional_tests_of(name="bignum"), + DRAFT4.optional_tests_of(name="format"), DRAFT4.optional_tests_of(name="zeroTerminatedFloats"), Validator=Draft4Validator, format_checker=draft4_format_checker, @@ -125,11 +119,6 @@ TestDraft4 = DRAFT4.to_unittest_testcase( "Location-independent identifier with base URI change in subschema" ), )(test) - or skip( - message=bug(), - subject="refRemote", - case_description="base URI change - change folder", - )(test) or skip( message=bug(), subject="refRemote", @@ -146,8 +135,8 @@ TestDraft4 = DRAFT4.to_unittest_testcase( TestDraft6 = DRAFT6.to_unittest_testcase( DRAFT6.tests(), - DRAFT6.optional_tests_of(name="format"), DRAFT6.optional_tests_of(name="bignum"), + DRAFT6.optional_tests_of(name="format"), DRAFT6.optional_tests_of(name="zeroTerminatedFloats"), Validator=Draft6Validator, format_checker=draft6_format_checker, @@ -178,11 +167,6 @@ TestDraft6 = DRAFT6.to_unittest_testcase( "Location-independent identifier with base URI change in subschema" ), )(test) - or skip( - message=bug(), - subject="refRemote", - case_description="base URI change - change folder", - )(test) or skip( message=bug(), subject="refRemote", @@ -201,6 +185,7 @@ TestDraft7 = DRAFT7.to_unittest_testcase( DRAFT7.tests(), DRAFT7.format_tests(), DRAFT7.optional_tests_of(name="bignum"), + DRAFT7.optional_tests_of(name="content"), DRAFT7.optional_tests_of(name="zeroTerminatedFloats"), Validator=Draft7Validator, format_checker=draft7_format_checker, @@ -231,11 +216,6 @@ TestDraft7 = DRAFT7.to_unittest_testcase( "Location-independent identifier with base URI change in subschema" ), )(test) - or skip( - message=bug(), - subject="refRemote", - case_description="base URI change - change folder", - )(test) or skip( message=bug(), subject="refRemote", @@ -246,6 +226,25 @@ TestDraft7 = DRAFT7.to_unittest_testcase( subject="date-time", description="case-insensitive T and Z", )(test) + or skip( + message=bug(593), + subject="content", + case_description=( + "validation of string-encoded content based on media type" + ), + )(test) + or skip( + message=bug(593), + subject="content", + case_description="validation of binary string-encoding", + )(test) + or skip( + message=bug(593), + subject="content", + case_description=( + "validation of binary-encoded media type documents" + ), + )(test) ), ) diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 0adbf3060e28540a4099da423035af1234d7abd5..07be4f08bc2e4ff09c4b60affbc7e6b78ecd508a 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -14,7 +14,7 @@ import attr from jsonschema import FormatChecker, TypeChecker, exceptions, validators from jsonschema.compat import PY3, pathname2url -import jsonschema +from jsonschema.tests._helpers import bug def startswith(validator, startswith, instance, schema): @@ -1154,7 +1154,6 @@ class ValidatorTestMixin(MetaSchemaTestsMixin, object): """ Legacy RefResolvers support only the context manager form of resolution. - """ class LegacyRefResolver(object): @@ -1183,6 +1182,12 @@ class ValidatorTestMixin(MetaSchemaTestsMixin, object): def test_string_a_bytestring_is_a_string(self): self.Validator({"type": "string"}).validate(b"foo") + def test_patterns_can_be_native_strings(self): + """ + See https://github.com/Julian/jsonschema/issues/611. + """ + self.Validator({"pattern": "foo"}).validate("foo") + def test_it_can_validate_with_decimals(self): schema = {"items": {"type": "number"}} Validator = validators.extend( @@ -1283,14 +1288,14 @@ class AntiDraft6LeakMixin(object): self.Validator.check_schema(False) self.assertIn("False is not of type", str(e.exception)) - @unittest.skip("This test fails, but it shouldn't.") + @unittest.skip(bug(523)) def test_True_is_not_a_schema_even_if_you_forget_to_check(self): resolver = validators.RefResolver("", {}) with self.assertRaises(Exception) as e: self.Validator(True, resolver=resolver).validate(12) self.assertNotIsInstance(e.exception, exceptions.ValidationError) - @unittest.skip("This test fails, but it shouldn't.") + @unittest.skip(bug(523)) def test_False_is_not_a_schema_even_if_you_forget_to_check(self): resolver = validators.RefResolver("", {}) with self.assertRaises(Exception) as e: @@ -1348,35 +1353,6 @@ class TestDraft7Validator(ValidatorTestMixin, TestCase): invalid = {"type": "integer"}, "foo" -class TestBuiltinFormats(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. - """ - - # These tests belong upstream. - # See https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/246 - - -for Validator, checker in ( - (validators.Draft3Validator, jsonschema.draft3_format_checker), - (validators.Draft4Validator, jsonschema.draft4_format_checker), - (validators.Draft6Validator, jsonschema.draft6_format_checker), - (validators.Draft7Validator, jsonschema.draft7_format_checker), -): - for format in checker.checkers: - def test(self, checker=checker, format=format): - validator = Validator({"format": format}, format_checker=checker) - validator.validate(123) - - name = "test_{}_{}_ignores_non_strings".format( - Validator.__name__, format, - ) - test.__name__ = name - setattr(TestBuiltinFormats, name, test) - - class TestValidatorFor(SynchronousTestCase): def test_draft_3(self): schema = {"$schema": "http://json-schema.org/draft-03/schema"} diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 7b7d76dfb209238601868d5558e9e6c178e9143e..1dc420c70d2558f9a2805f0ddb8c0f1cb9d5cf93 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -1,3 +1,6 @@ +""" +Creation and extension of validators, with implementations for existing drafts. +""" from __future__ import division from warnings import warn @@ -38,9 +41,9 @@ class _DontDoThat(Exception): """ Raised when a Validators with non-default type checker is misused. - Asking one for DEFAULT_TYPES doesn't make sense, since type checkers exist - for the unrepresentable cases where DEFAULT_TYPES can't represent the type - relationship. + Asking one for DEFAULT_TYPES doesn't make sense, since type checkers + exist for the unrepresentable cases where DEFAULT_TYPES can't + represent the type relationship. """ def __str__(self): @@ -114,7 +117,9 @@ def validates(version): Returns: - callable: a class decorator to decorate the validator with the version + collections.Callable: + + a class decorator to decorate the validator with the version """ def _validates(cls): @@ -184,17 +189,17 @@ def create( version (str): an identifier for the version that this validator class will - validate. If provided, the returned validator class will have its - ``__name__`` set to include the version, and also will have - `jsonschema.validators.validates` automatically called for the - given version. + validate. If provided, the returned validator class will + have its ``__name__`` set to include the version, and also + will have `jsonschema.validators.validates` automatically + called for the given version. type_checker (jsonschema.TypeChecker): a type checker, used when applying the :validator:`type` validator. - If unprovided, a `jsonschema.TypeChecker` will be created with - a set of default types typical of JSON Schema drafts. + If unprovided, a `jsonschema.TypeChecker` will be created + with a set of default types typical of JSON Schema drafts. default_types (collections.Mapping): @@ -202,11 +207,11 @@ def create( Please use the type_checker argument instead. - If set, it provides mappings of JSON types to Python types that - will be converted to functions and redefined in this object's - `jsonschema.TypeChecker`. + If set, it provides mappings of JSON types to Python types + that will be converted to functions and redefined in this + object's `jsonschema.TypeChecker`. - id_of (callable): + id_of (collections.Callable): A function that given a schema, returns its ID. @@ -381,14 +386,14 @@ def extend(validator, validators=(), version=None, type_checker=None): .. note:: - Any validator callables with the same name as an existing one - will (silently) replace the old validator callable entirely, - effectively overriding any validation done in the "parent" - validator class. + Any validator callables with the same name as an + existing one will (silently) replace the old validator + callable entirely, effectively overriding any validation + done in the "parent" validator class. If you wish to instead extend the behavior of a parent's - validator callable, delegate and call it directly in the new - validator function by retrieving it using + validator callable, delegate and call it directly in + the new validator function by retrieving it using ``OldValidator.VALIDATORS["validator_name"]``. version (str): @@ -611,12 +616,12 @@ class RefResolver(object): A mapping from URI schemes to functions that should be used to retrieve them - urljoin_cache (functools.lru_cache): + urljoin_cache (:func:`functools.lru_cache`): A cache that will be used for caching the results of joining the resolution scope to subscopes. - remote_cache (functools.lru_cache): + remote_cache (:func:`functools.lru_cache`): A cache that will be used for caching the results of resolved remote URLs. @@ -677,11 +682,26 @@ class RefResolver(object): return cls(base_uri=id_of(schema), referrer=schema, *args, **kwargs) def push_scope(self, scope): + """ + Enter a given sub-scope. + + Treats further dereferences as being performed underneath the + given scope. + """ self._scopes_stack.append( self._urljoin_cache(self.resolution_scope, scope), ) def pop_scope(self): + """ + Exit the most recent entered scope. + + Treats further dereferences as being performed underneath the + original scope. + + Don't call this method more times than `push_scope` has been + called. + """ try: self._scopes_stack.pop() except IndexError: @@ -693,15 +713,24 @@ class RefResolver(object): @property def resolution_scope(self): + """ + Retrieve the current resolution scope. + """ return self._scopes_stack[-1] @property def base_uri(self): + """ + Retrieve the current base URI, not including any fragment. + """ uri, _ = urldefrag(self.resolution_scope) return uri @contextlib.contextmanager def in_scope(self, scope): + """ + Temporarily enter the given scope for the duration of the context. + """ self.push_scope(scope) try: yield @@ -730,10 +759,16 @@ class RefResolver(object): self.pop_scope() def resolve(self, ref): + """ + Resolve the given reference. + """ url = self._urljoin_cache(self.resolution_scope, ref) return url, self._remote_cache(url) def resolve_from_url(self, url): + """ + Resolve the given remote URL. + """ url, fragment = urldefrag(url) try: document = self.store[url] @@ -842,9 +877,9 @@ def validate(instance, schema, cls=None, *args, **kwargs): ... 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. + :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, especially if you intend to validate multiple instances with the same schema, you @@ -866,16 +901,16 @@ def validate(instance, schema, cls=None, *args, **kwargs): The 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 - the latest released draft. + 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 the latest released draft. - Any other provided positional and keyword arguments will be passed on when - instantiating the ``cls``. + Any other provided positional and keyword arguments will be passed + on when instantiating the ``cls``. Raises: @@ -903,8 +938,8 @@ def validator_for(schema, default=_LATEST_VERSION): """ Retrieve the validator class appropriate for validating the given schema. - Uses the :validator:`$schema` property that should be present in the given - schema to look up the appropriate validator class. + Uses the :validator:`$schema` property that should be present in the + given schema to look up the appropriate validator class. Arguments: @@ -914,11 +949,11 @@ def validator_for(schema, default=_LATEST_VERSION): default: - the default to return if the appropriate validator class cannot be - determined. + the default to return if the appropriate validator class + cannot be determined. - If unprovided, the default is to return - the latest supported draft. + If unprovided, the default is to return the latest supported + draft. """ if schema is True or schema is False or u"$schema" not in schema: return default diff --git a/setup.cfg b/setup.cfg index 7d56e5fe14df8802aaa5e0cd9041fa1e2d0df6af..c8290bd859b59960db64a899a17828e2a6bd6dbc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ classifiers = Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy @@ -27,6 +28,7 @@ packages = find: setup_requires = setuptools_scm install_requires = attrs>=17.4.0 + importlib_metadata;python_version<'3.8' pyrsistent>=0.14.0 setuptools six>=1.11.0 @@ -39,6 +41,12 @@ format = rfc3987 strict-rfc3339 webcolors +format_nongpl = + idna + jsonpointer>1.13 + webcolors + rfc3986-validator>0.1.0 + rfc3339-validator [options.entry_points] console_scripts = @@ -47,10 +55,22 @@ console_scripts = [options.package_data] jsonschema = schemas/*.json +[bdist_wheel] +universal = 1 + [flake8] +builtins = unicode exclude = jsonschema/__init__.py jsonschema/_reflect.py -[wheel] -universal = 1 +[pydocstyle] +match = (?!(test_|_|compat|cli)).*\.py # see PyCQA/pydocstyle#323 +add-select = + D410, # Trailing whitespace plz +add-ignore = + D107, # Hah, no + D200, # 1-line docstrings don't need to be on one line + D202, # One line is fine. + D412, # Trailing whitespace plz + D413, # No trailing whitespace plz diff --git a/tox.ini b/tox.ini index 3e12e2a9d420b7bf8e9be4566474c54b111d0b1e..4ed385bd40077011a53bb5a201afa6282da30620 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,13 @@ [tox] -skipsdist = True envlist = - py{35,36,37,py,py3}-{build,tests}, - docs-{html,doctest,linkcheck,spelling,style}, - readme, - safety, - style, + py{35,36,37,38,py,py3}-{build,tests,tests_nongpl}, + demo + readme + safety + secrets + style + docs-{html,doctest,linkcheck,spelling,style} +skipsdist = True [testenv] changedir = @@ -14,16 +16,20 @@ setenv = JSON_SCHEMA_TEST_SUITE = {toxinidir}/json whitelist_externals = python2.7 + mkdir + rm sh virtualenv commands = perf,tests: {envbindir}/python -m pip install '{toxinidir}[format]' + tests_nongpl: {envbindir}/python -m pip install '{toxinidir}[format_nongpl]' - tests: {envbindir}/trial {posargs:jsonschema} - py{py,27,37}-tests: {envpython} -m doctest {toxinidir}/README.rst + tests,tests_nongpl: {envbindir}/trial {posargs:jsonschema} + tests: {envpython} -m doctest {toxinidir}/README.rst - perf: {envpython} {toxinidir}/jsonschema/benchmarks/json_schema_test_suite.py --inherit-environ JSON_SCHEMA_TEST_SUITE - perf: {envpython} {toxinidir}/jsonschema/benchmarks/issue232.py --inherit-environ JSON_SCHEMA_TEST_SUITE + perf: mkdir {envtmpdir}/benchmarks/ + perf: {envpython} {toxinidir}/jsonschema/benchmarks/issue232.py --inherit-environ JSON_SCHEMA_TEST_SUITE --output {envtmpdir}/benchmarks/issue232.json + perf: {envpython} {toxinidir}/jsonschema/benchmarks/json_schema_test_suite.py --inherit-environ JSON_SCHEMA_TEST_SUITE --output {envtmpdir}/benchmarks/json_schema_test_suite.json # Check to make sure that releases build and install properly build: virtualenv --quiet --python=python2.7 {envtmpdir}/venv @@ -35,36 +41,55 @@ commands = build: python2.7 {toxinidir}/setup.py --quiet sdist --dist-dir={envtmpdir}/sdist --format=gztar,zip build: sh -c '{envbindir}/pip install --quiet --upgrade --force-reinstall {envtmpdir}/sdist/jsonschema*.tar.gz' build: sh -c '{envbindir}/pip install --quiet --upgrade --force-reinstall {envtmpdir}/sdist/jsonschema*.zip' + + build: {envbindir}/python -m pep517.check {toxinidir} + + # FIXME: This has side effects! But it's not my fault... I can't + # figure out yet how to get setuptools to not create this directory + # here yet. But whatever, probably this will change to pep517.build + # soon anways. + build: rm -rf {toxinidir}/jsonschema.egg-info deps = - -r{toxinidir}/test-requirements.txt + build: pep517 - tests,coverage,codecov: twisted - tests: lxml - tests: sphinx + perf: pyperf + + tests,tests_nongpl,coverage,codecov: -r{toxinidir}/test-requirements.txt coverage,codecov: coverage codecov: codecov - perf: perf - safety: safety +[testenv:bandit] +deps = bandit +commands = {envbindir}/bandit --recursive {toxinidir}/jsonschema + +[testenv:demo] +deps = jupyter +commands = + {envbindir}/jupyter nbconvert --output-dir {envtmpdir} {toxinidir}/DEMO.ipynb [testenv:readme] changedir = {toxinidir} deps = readme_renderer -commands = +commands = {envbindir}/python setup.py check --restructuredtext --strict [testenv:safety] deps = safety -commands = +commands = {envbindir}/pip install '{toxinidir}[format]' {envbindir}/safety check +[testenv:secrets] +deps = detect-secrets +commands = {envbindir}/detect-secrets scan {toxinidir} + [testenv:style] +basepython = pypy3 deps = ebb-lint>=0.19.1.0 - git+https://github.com/pyga/awpa@py37 -commands = {envbindir}/flake8 {posargs} {toxinidir}/jsonschema {toxinidir}/docs {toxinidir}/setup.py +commands = + {envbindir}/flake8 {posargs} {toxinidir}/jsonschema {toxinidir}/docs {toxinidir}/setup.py [testenv:coverage] setenv = @@ -77,66 +102,52 @@ commands = {envbindir}/coverage report --rcfile={toxinidir}/.coveragerc --show-missing {envbindir}/coverage html --directory={envtmpdir}/htmlcov --rcfile={toxinidir}/.coveragerc {posargs} -[testenv:codecov] -passenv = CODECOV* CI TRAVIS TRAVIS_* -setenv = {[testenv:coverage]setenv} -commands = - {envbindir}/python -m pip install '{toxinidir}[format]' - {envbindir}/coverage run --rcfile={toxinidir}/.coveragerc {envbindir}/trial jsonschema - {envbindir}/coverage xml -o {envtmpdir}/coverage.xml - codecov --required --disable gcov --file {envtmpdir}/coverage.xml - [testenv:docs-html] -basepython = pypy -changedir = docs -whitelist_externals = make -commands = make -f {toxinidir}/docs/Makefile BUILDDIR={envtmpdir}/build SPHINXOPTS='-a -c {toxinidir}/docs/ -n -T -W {posargs}' html +basepython = pypy3 +commands = {envpython} -m sphinx -b html {toxinidir}/docs/ {envtmpdir}/build {posargs:-a -n -q -T -W} deps = -r{toxinidir}/docs/requirements.txt - -e{toxinidir} + {toxinidir} [testenv:docs-doctest] -basepython = pypy -changedir = docs -whitelist_externals = make -commands = make -f {toxinidir}/docs/Makefile BUILDDIR={envtmpdir}/build SPHINXOPTS='-a -c {toxinidir}/docs/ -n -T -W {posargs}' doctest +basepython = pypy3 +commands = {envpython} -m sphinx -b doctest {toxinidir}/docs/ {envtmpdir}/build {posargs:-a -n -q -T -W} deps = -r{toxinidir}/docs/requirements.txt - -e{toxinidir} + {toxinidir} [testenv:docs-linkcheck] -basepython = pypy -changedir = docs -whitelist_externals = make -commands = make -f {toxinidir}/docs/Makefile BUILDDIR={envtmpdir}/build SPHINXOPTS='-a -c {toxinidir}/docs/ -n -T -W {posargs}' linkcheck +basepython = pypy3 +commands = {envpython} -m sphinx -b linkcheck {toxinidir}/docs/ {envtmpdir}/build {posargs:-a -n -q -T -W} deps = -r{toxinidir}/docs/requirements.txt - -e{toxinidir} + {toxinidir} [testenv:docs-spelling] -basepython = pypy -changedir = docs -whitelist_externals = make -commands = make -f {toxinidir}/docs/Makefile BUILDDIR={envtmpdir}/build SPHINXOPTS='-a -c {toxinidir}/docs/ -n -T -W {posargs}' spelling +basepython = pypy3 +commands = {envpython} -m sphinx -b spelling {toxinidir}/docs/ {envtmpdir}/build {posargs:-a -n -q -T -W} deps = -r{toxinidir}/docs/requirements.txt - -e{toxinidir} + {toxinidir} [testenv:docs-style] -basepython = pypy -changedir = docs +basepython = pypy3 commands = doc8 {posargs} {toxinidir}/docs deps = doc8 pygments pygments-github-lexers -[doc8] -ignore-path = - version.txt, - .*/, - _*/ +[testenv:codecov] +passenv = CODECOV* CI TRAVIS TRAVIS_* +setenv = {[testenv:coverage]setenv} +commands = + {envbindir}/python -m pip install '{toxinidir}[format]' + {envbindir}/coverage run --rcfile={toxinidir}/.coveragerc {envbindir}/trial jsonschema + {envbindir}/coverage xml -o {envtmpdir}/coverage.xml + codecov --required --disable gcov --file {envtmpdir}/coverage.xml [travis] python = - pypy: pypy, docs, readme, safety, style + pypy: pypy, readme, safety, secrets + pypy3: pypy3, demo, docs, style