summaryrefslogtreecommitdiffstats
path: root/third_party/python/jsonschema
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/jsonschema')
-rw-r--r--third_party/python/jsonschema/jsonschema-4.17.3.dist-info/METADATA195
-rw-r--r--third_party/python/jsonschema/jsonschema-4.17.3.dist-info/RECORD52
-rw-r--r--third_party/python/jsonschema/jsonschema-4.17.3.dist-info/WHEEL4
-rw-r--r--third_party/python/jsonschema/jsonschema-4.17.3.dist-info/entry_points.txt2
-rw-r--r--third_party/python/jsonschema/jsonschema-4.17.3.dist-info/licenses/COPYING19
-rw-r--r--third_party/python/jsonschema/jsonschema/__init__.py71
-rw-r--r--third_party/python/jsonschema/jsonschema/__main__.py3
-rw-r--r--third_party/python/jsonschema/jsonschema/_format.py518
-rw-r--r--third_party/python/jsonschema/jsonschema/_legacy_validators.py319
-rw-r--r--third_party/python/jsonschema/jsonschema/_types.py203
-rw-r--r--third_party/python/jsonschema/jsonschema/_utils.py349
-rw-r--r--third_party/python/jsonschema/jsonschema/_validators.py476
-rw-r--r--third_party/python/jsonschema/jsonschema/benchmarks/__init__.py5
-rw-r--r--third_party/python/jsonschema/jsonschema/benchmarks/issue232.py25
-rw-r--r--third_party/python/jsonschema/jsonschema/benchmarks/issue232/issue.json2653
-rw-r--r--third_party/python/jsonschema/jsonschema/benchmarks/json_schema_test_suite.py12
-rw-r--r--third_party/python/jsonschema/jsonschema/cli.py299
-rw-r--r--third_party/python/jsonschema/jsonschema/exceptions.py396
-rw-r--r--third_party/python/jsonschema/jsonschema/protocols.py225
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft2019-09.json42
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft2020-12.json58
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft3.json172
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft4.json149
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft6.json153
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/draft7.json166
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/applicator56
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/content17
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/core57
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/meta-data37
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/validation98
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/applicator48
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/content17
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/core51
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format14
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-annotation14
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-assertion14
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/meta-data37
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/unevaluated15
-rw-r--r--third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/validation98
-rw-r--r--third_party/python/jsonschema/jsonschema/validators.py1161
40 files changed, 8300 insertions, 0 deletions
diff --git a/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/METADATA b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/METADATA
new file mode 100644
index 0000000000..07ec3f119b
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/METADATA
@@ -0,0 +1,195 @@
+Metadata-Version: 2.1
+Name: jsonschema
+Version: 4.17.3
+Summary: An implementation of JSON Schema validation for Python
+Project-URL: Homepage, https://github.com/python-jsonschema/jsonschema
+Project-URL: Documentation, https://python-jsonschema.readthedocs.io/
+Project-URL: Issues, https://github.com/python-jsonschema/jsonschema/issues/
+Project-URL: Funding, https://github.com/sponsors/Julian
+Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-jsonschema?utm_source=pypi-jsonschema&utm_medium=referral&utm_campaign=pypi-link
+Project-URL: Changelog, https://github.com/python-jsonschema/jsonschema/blob/main/CHANGELOG.rst
+Project-URL: Source, https://github.com/python-jsonschema/jsonschema
+Author: Julian Berman
+Author-email: Julian+jsonschema@GrayVines.com
+License: MIT
+License-File: COPYING
+Keywords: data validation,json,jsonschema,validation
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.7
+Requires-Dist: attrs>=17.4.0
+Requires-Dist: importlib-metadata; python_version < '3.8'
+Requires-Dist: importlib-resources>=1.4.0; python_version < '3.9'
+Requires-Dist: pkgutil-resolve-name>=1.3.10; python_version < '3.9'
+Requires-Dist: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0
+Requires-Dist: typing-extensions; python_version < '3.8'
+Provides-Extra: format
+Requires-Dist: fqdn; extra == 'format'
+Requires-Dist: idna; extra == 'format'
+Requires-Dist: isoduration; extra == 'format'
+Requires-Dist: jsonpointer>1.13; extra == 'format'
+Requires-Dist: rfc3339-validator; extra == 'format'
+Requires-Dist: rfc3987; extra == 'format'
+Requires-Dist: uri-template; extra == 'format'
+Requires-Dist: webcolors>=1.11; extra == 'format'
+Provides-Extra: format-nongpl
+Requires-Dist: fqdn; extra == 'format-nongpl'
+Requires-Dist: idna; extra == 'format-nongpl'
+Requires-Dist: isoduration; extra == 'format-nongpl'
+Requires-Dist: jsonpointer>1.13; extra == 'format-nongpl'
+Requires-Dist: rfc3339-validator; extra == 'format-nongpl'
+Requires-Dist: rfc3986-validator>0.1.0; extra == 'format-nongpl'
+Requires-Dist: uri-template; extra == 'format-nongpl'
+Requires-Dist: webcolors>=1.11; extra == 'format-nongpl'
+Description-Content-Type: text/x-rst
+
+==========
+jsonschema
+==========
+
+|PyPI| |Pythons| |CI| |ReadTheDocs| |Precommit| |Zenodo|
+
+.. |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/
+
+.. |CI| image:: https://github.com/python-jsonschema/jsonschema/workflows/CI/badge.svg
+ :alt: Build status
+ :target: https://github.com/python-jsonschema/jsonschema/actions?query=workflow%3ACI
+
+.. |ReadTheDocs| image:: https://readthedocs.org/projects/python-jsonschema/badge/?version=stable&style=flat
+ :alt: ReadTheDocs status
+ :target: https://python-jsonschema.readthedocs.io/en/stable/
+
+.. |Precommit| image:: https://results.pre-commit.ci/badge/github/python-jsonschema/jsonschema/main.svg
+ :alt: pre-commit.ci status
+ :target: https://results.pre-commit.ci/latest/github/python-jsonschema/jsonschema/main
+
+.. |Zenodo| image:: https://zenodo.org/badge/3072629.svg
+ :target: https://zenodo.org/badge/latestdoi/3072629
+
+
+``jsonschema`` is an implementation of the `JSON Schema
+<https://json-schema.org>`_ specification for Python.
+
+.. 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'
+
+It can also be used from console:
+
+.. code-block:: bash
+
+ $ jsonschema --instance sample.json sample.schema
+
+Features
+--------
+
+* Partial support for
+ `Draft 2020-12 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft202012Validator>`_ and
+ `Draft 2019-09 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft201909Validator>`_,
+ except for ``dynamicRef`` / ``recursiveRef`` and ``$vocabulary`` (in-progress).
+ Full support for
+ `Draft 7 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft7Validator>`_,
+ `Draft 6 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft6Validator>`_,
+ `Draft 4 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft4Validator>`_
+ and
+ `Draft 3 <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/validators/#jsonschema.validators.Draft3Validator>`_
+
+* `Lazy validation <https://python-jsonschema.readthedocs.io/en/latest/api/jsonschema/protocols/#jsonschema.protocols.Validator.iter_errors>`_
+ that can iteratively report *all* validation errors.
+
+* `Programmatic querying <https://python-jsonschema.readthedocs.io/en/latest/errors/>`_
+ of which properties or items failed validation.
+
+
+Installation
+------------
+
+``jsonschema`` is available on `PyPI <https://pypi.org/project/jsonschema/>`_. You can install using `pip <https://pip.pypa.io/en/stable/>`_:
+
+.. code-block:: bash
+
+ $ pip install jsonschema
+
+
+Extras
+======
+
+Two extras are available when installing the package, both currently related to ``format`` validation:
+
+ * ``format``
+ * ``format-nongpl``
+
+They can be used when installing in order to include additional dependencies, e.g.:
+
+.. code-block:: bash
+
+ $ pip install jsonschema'[format]'
+
+Be aware that the mere presence of these dependencies – or even the specification of ``format`` checks in a schema – do *not* activate format checks (as per the specification).
+Please read the `format validation documentation <https://python-jsonschema.readthedocs.io/en/latest/validate/#validating-formats>`_ for further details.
+
+About
+-----
+
+I'm Julian Berman.
+
+``jsonschema`` is on `GitHub <https://github.com/python-jsonschema/jsonschema>`_.
+
+Get in touch, via GitHub or otherwise, if you've got something to contribute,
+it'd be most welcome!
+
+You can also generally find me on Libera (nick: ``Julian``) in various
+channels, including ``#python``.
+
+If you feel overwhelmingly grateful, you can also `sponsor me
+<https://github.com/sponsors/Julian/>`_.
+
+And for companies who appreciate ``jsonschema`` and its continued support
+and growth, ``jsonschema`` is also now supportable via `TideLift
+<https://tidelift.com/subscription/pkg/pypi-jsonschema?utm_source=pypi-j
+sonschema&utm_medium=referral&utm_campaign=readme>`_.
+
+
+Release Information
+-------------------
+
+v4.17.3
+=======
+
+* Fix instantiating validators with cached refs to boolean schemas
+ rather than objects (#1018).
diff --git a/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/RECORD b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/RECORD
new file mode 100644
index 0000000000..22b5fecf4a
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/RECORD
@@ -0,0 +1,52 @@
+jsonschema/__init__.py,sha256=FRdJDXN8-AFk-Fj1qclckQsZNeGQB__r_QuMjtRoze4,2187
+jsonschema/__main__.py,sha256=Sfz1ZNeogymj_KZxq6JXY3F6O_1v28sLIiskusifQ5s,40
+jsonschema/_format.py,sha256=P7gjEZzWa1dU0wDu3NbyG1cNW6drM3i454a_7sJE8TY,14575
+jsonschema/_legacy_validators.py,sha256=0KDc3X0gTzuW52NRQJ123ZW_IBLcIDsBaVFVfMo_vNY,10549
+jsonschema/_types.py,sha256=YgKkjzf97pKRsiuc5RN76jJPGCpmdMHJlhcOVWjCW78,5425
+jsonschema/_utils.py,sha256=D3oRGTSk6llsZDFdDAVQWBEi62Uy_4SWVbjPiG9PJcs,10429
+jsonschema/_validators.py,sha256=4H0TI2BIXhmJrmF3iC1VzKgxwhCfiGreJfBWBtP20HY,15956
+jsonschema/cli.py,sha256=DJhWQs6X5nsmWfUqrnSjmAdzg5Sbd3igcRGguHJAEQU,8518
+jsonschema/exceptions.py,sha256=asZ8nwPwuQlMCXTb7Ztyi1OuxkqRiPnxK9TuwiDqGRo,11336
+jsonschema/protocols.py,sha256=zfj-Rmc2rsNIQjEIFnnMsHlYEoS-rprnBHTG5pgvCy0,7295
+jsonschema/validators.py,sha256=zwfdaiQFvH-Z9EoEH-G_Bh8hCQzOirJg6-_AFoT8_ow,38150
+jsonschema/benchmarks/__init__.py,sha256=A0sQrxDBVHSyQ-8ru3L11hMXf3q9gVuB9x_YgHb4R9M,70
+jsonschema/benchmarks/issue232.py,sha256=GKQBwm03sf-pPSxBxc4YDvBBnMYknOk6m-WtTntN5VE,506
+jsonschema/benchmarks/json_schema_test_suite.py,sha256=PvfabpUYcF4_7csYDTcTauED8rnFEGYbdY5RqTXD08s,320
+jsonschema/benchmarks/issue232/issue.json,sha256=eaPOZjMRu5u8RpKrsA9uk7ucPZS5tkKG4D_hkOTQ3Hk,117105
+jsonschema/schemas/draft2019-09.json,sha256=e3YbPhIfCgyh6ioLjizIVrz4AWBLgmjXG6yqICvAwTs,1785
+jsonschema/schemas/draft2020-12.json,sha256=Qdp29a-3zgYtJI92JGOpL3ykfk4PkFsiS6av7vkd7Q8,2452
+jsonschema/schemas/draft3.json,sha256=LPdfZENvtb43Si6qJ6uLfh_WUcm0ba6mxnsC_WTiRYs,2600
+jsonschema/schemas/draft4.json,sha256=4UidC0dV8CeTMCWR0_y48Htok6gqlPJIlfjk7fEbguI,4357
+jsonschema/schemas/draft6.json,sha256=wp386fVINcOgbAOzxdXsDtp3cGVo-cTffPvHVmpRAG0,4437
+jsonschema/schemas/draft7.json,sha256=PVOSCIJhYGxVm2A_OFMpyfGrRbXWZ-uZBodFOwVdQF4,4819
+jsonschema/schemas/vocabularies/draft2019-09/applicator,sha256=aJUQDplyb7sQcFhRK77D7P1LJOj9L6zuPlBe5ysNTDE,1860
+jsonschema/schemas/vocabularies/draft2019-09/content,sha256=m31PVaTi_bAsQwBo_f-rxzKt3OI42j8d8mkCScM1MnQ,517
+jsonschema/schemas/vocabularies/draft2019-09/core,sha256=taLElX9kldClCB8ECevooU5BOayyA_x0hHH47eKvWyw,1531
+jsonschema/schemas/vocabularies/draft2019-09/meta-data,sha256=1H4kRd1qgicaKY2DzGxsuNSuHhXg3Fa-zTehY-zwEoY,892
+jsonschema/schemas/vocabularies/draft2019-09/validation,sha256=HlJsHTNac0gF_ILPV5jBK5YK19olF8Zs2lobCTWcPBw,2834
+jsonschema/schemas/vocabularies/draft2020-12/applicator,sha256=xKbkFHuR_vf-ptwFjLG_k0AvdBS3ZXiosWqvHa1qrO8,1659
+jsonschema/schemas/vocabularies/draft2020-12/content,sha256=CDQ3R3ZOSlgUJieTz01lIFenkThjxZUNQyl-jh_axbY,519
+jsonschema/schemas/vocabularies/draft2020-12/core,sha256=wtEqjk3RHTNt_IOj9mOqTGnwtJs76wlP_rJbUxb0gD0,1564
+jsonschema/schemas/vocabularies/draft2020-12/format,sha256=UOu_55BhGoSbjMQAoJwdDg-2q1wNQ6DyIgH9NiUFa_Q,403
+jsonschema/schemas/vocabularies/draft2020-12/format-annotation,sha256=q8d1rf79idIjWBcNm_k_Tr0jSVY7u-3WDwK-98gSvMA,448
+jsonschema/schemas/vocabularies/draft2020-12/format-assertion,sha256=xSJCuaG7eGsmw-gset1CjDH5yW5XXc6Z5W6l_qptogw,445
+jsonschema/schemas/vocabularies/draft2020-12/meta-data,sha256=j3bW4U9Bubku-TO3CM3FFEyLUmhlGtEZGEhfsXVPHHY,892
+jsonschema/schemas/vocabularies/draft2020-12/unevaluated,sha256=Lb-8tzmUtnCwl2SSre4f_7RsIWgnhNL1pMpWH54tDLQ,506
+jsonschema/schemas/vocabularies/draft2020-12/validation,sha256=cBCjHlQfMtK-ch4t40jfdcmzaHaj7TBId_wKvaHTelg,2834
+jsonschema/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+jsonschema/tests/_helpers.py,sha256=yoWpWVYq4auuTPPd1_8FXV77RlczQtiyutSh6c191ZM,618
+jsonschema/tests/_suite.py,sha256=EyzqI3EGlJiMX_o3C-W51J2m4JVRU0uqrWFjdmCspgI,7052
+jsonschema/tests/fuzz_validate.py,sha256=fUA7yTJIihaCwJplkUehZeyB84HcXEcqtY5oPJXIO7I,1114
+jsonschema/tests/test_cli.py,sha256=e9kjwfVp1sSBCF9ffGH7FwgcRlTaBZUehcWiM-BmmLk,28857
+jsonschema/tests/test_deprecations.py,sha256=uf0ct-i9V3UBc3zm8DRcgxCitURKl2-Qt3wpAUoK0GU,9284
+jsonschema/tests/test_exceptions.py,sha256=vvh4zfHVvE9egGn4qc1jQo2s8fh09oVt1WcCRpaKivM,19639
+jsonschema/tests/test_format.py,sha256=h79gKpcKV58qOQOn8TsJNf2HFkrUYzNZ3Uai_cc2nQE,3810
+jsonschema/tests/test_jsonschema_test_suite.py,sha256=-sUumFw_1MTcfMGrG5-Zw3_yldVHCto6SxPh-aoh64g,18090
+jsonschema/tests/test_types.py,sha256=_m7chMGj9U5G57jB_CmUuP5mSit3PET584ehoIlKkf4,6983
+jsonschema/tests/test_utils.py,sha256=lJRVYyQeZQTUCTU_M3BhlkxPMgjsc8KQCd7U_Qkook8,3749
+jsonschema/tests/test_validators.py,sha256=JW4Y5mzVMuumYdBCEFhxPO7EIqN-k2MnbwzBXHq7LIE,78785
+jsonschema-4.17.3.dist-info/METADATA,sha256=YnDEs-j6S4At_Fq54u7PqfYG4gQT8E5nrp8vu2PXeXw,7879
+jsonschema-4.17.3.dist-info/WHEEL,sha256=NaLmgHHW_f9jTvv_wRh9vcK7c7EK9o5fwsIXMOzoGgM,87
+jsonschema-4.17.3.dist-info/entry_points.txt,sha256=vO7rX4Fs_xIVJy2pnAtKgTSxfpnozAVQ0DjCmpMxnWE,51
+jsonschema-4.17.3.dist-info/licenses/COPYING,sha256=T5KgFaE8TRoEC-8BiqE0MLTxvHO0Gxa7hGw0Z2bedDk,1057
+jsonschema-4.17.3.dist-info/RECORD,,
diff --git a/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/WHEEL b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/WHEEL
new file mode 100644
index 0000000000..6d803659b7
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.11.1
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/entry_points.txt b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/entry_points.txt
new file mode 100644
index 0000000000..eecef9d8fa
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[console_scripts]
+jsonschema = jsonschema.cli:main
diff --git a/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/licenses/COPYING b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/licenses/COPYING
new file mode 100644
index 0000000000..af9cfbdb13
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema-4.17.3.dist-info/licenses/COPYING
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Julian Berman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/third_party/python/jsonschema/jsonschema/__init__.py b/third_party/python/jsonschema/jsonschema/__init__.py
new file mode 100644
index 0000000000..6628fc7eb9
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/__init__.py
@@ -0,0 +1,71 @@
+"""
+An implementation of JSON Schema for Python
+
+The main functionality is provided by the validator classes for each of the
+supported JSON Schema versions.
+
+Most commonly, `jsonschema.validators.validate` is the quickest way to simply
+validate a given instance under a schema, and will create a validator
+for you.
+"""
+import warnings
+
+from jsonschema._format import FormatChecker
+from jsonschema._types import TypeChecker
+from jsonschema.exceptions import (
+ ErrorTree,
+ FormatError,
+ RefResolutionError,
+ SchemaError,
+ ValidationError,
+)
+from jsonschema.protocols import Validator
+from jsonschema.validators import (
+ Draft3Validator,
+ Draft4Validator,
+ Draft6Validator,
+ Draft7Validator,
+ Draft201909Validator,
+ Draft202012Validator,
+ RefResolver,
+ validate,
+)
+
+
+def __getattr__(name):
+ if name == "__version__":
+ warnings.warn(
+ "Accessing jsonschema.__version__ is deprecated and will be "
+ "removed in a future release. Use importlib.metadata directly "
+ "to query for jsonschema's version.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ try:
+ from importlib import metadata
+ except ImportError:
+ import importlib_metadata as metadata
+
+ return metadata.version("jsonschema")
+
+ format_checkers = {
+ "draft3_format_checker": Draft3Validator,
+ "draft4_format_checker": Draft4Validator,
+ "draft6_format_checker": Draft6Validator,
+ "draft7_format_checker": Draft7Validator,
+ "draft201909_format_checker": Draft201909Validator,
+ "draft202012_format_checker": Draft202012Validator,
+ }
+ ValidatorForFormat = format_checkers.get(name)
+ if ValidatorForFormat is not None:
+ warnings.warn(
+ f"Accessing jsonschema.{name} is deprecated and will be "
+ "removed in a future release. Instead, use the FORMAT_CHECKER "
+ "attribute on the corresponding Validator.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return ValidatorForFormat.FORMAT_CHECKER
+
+ raise AttributeError(f"module {__name__} has no attribute {name}")
diff --git a/third_party/python/jsonschema/jsonschema/__main__.py b/third_party/python/jsonschema/jsonschema/__main__.py
new file mode 100644
index 0000000000..fdc21e2306
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/__main__.py
@@ -0,0 +1,3 @@
+from jsonschema.cli import main
+
+main()
diff --git a/third_party/python/jsonschema/jsonschema/_format.py b/third_party/python/jsonschema/jsonschema/_format.py
new file mode 100644
index 0000000000..5ec97977ad
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/_format.py
@@ -0,0 +1,518 @@
+from __future__ import annotations
+
+from contextlib import suppress
+from uuid import UUID
+import datetime
+import ipaddress
+import re
+import typing
+import warnings
+
+from jsonschema.exceptions import FormatError
+
+_FormatCheckCallable = typing.Callable[[object], bool]
+_F = typing.TypeVar("_F", bound=_FormatCheckCallable)
+_RaisesType = typing.Union[
+ typing.Type[Exception], typing.Tuple[typing.Type[Exception], ...],
+]
+
+
+class FormatChecker:
+ """
+ A ``format`` property checker.
+
+ JSON Schema does not mandate that the ``format`` property actually do any
+ validation. If validation is desired however, instances of this class can
+ be hooked into validators to enable format validation.
+
+ `FormatChecker` objects always return ``True`` when asked about
+ formats that they do not know how to validate.
+
+ To add a check for a custom format use the `FormatChecker.checks`
+ decorator.
+
+ Arguments:
+
+ formats:
+
+ The known formats to validate. This argument can be used to
+ limit which formats will be used during validation.
+ """
+
+ checkers: dict[
+ str,
+ tuple[_FormatCheckCallable, _RaisesType],
+ ] = {}
+
+ def __init__(self, formats: typing.Iterable[str] | None = None):
+ if formats is None:
+ formats = self.checkers.keys()
+ self.checkers = {k: self.checkers[k] for k in formats}
+
+ def __repr__(self):
+ return "<FormatChecker checkers={}>".format(sorted(self.checkers))
+
+ def checks(
+ self, format: str, raises: _RaisesType = (),
+ ) -> typing.Callable[[_F], _F]:
+ """
+ Register a decorated function as validating a new format.
+
+ Arguments:
+
+ format:
+
+ The format that the decorated function will check.
+
+ raises:
+
+ The exception(s) raised by the decorated function when an
+ invalid instance is found.
+
+ The exception object will be accessible as the
+ `jsonschema.exceptions.ValidationError.cause` attribute of the
+ resulting validation error.
+ """
+
+ def _checks(func: _F) -> _F:
+ self.checkers[format] = (func, raises)
+ return func
+
+ return _checks
+
+ @classmethod
+ def cls_checks(
+ cls, format: str, raises: _RaisesType = (),
+ ) -> typing.Callable[[_F], _F]:
+ warnings.warn(
+ (
+ "FormatChecker.cls_checks is deprecated. Call "
+ "FormatChecker.checks on a specific FormatChecker instance "
+ "instead."
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return cls._cls_checks(format=format, raises=raises)
+
+ @classmethod
+ def _cls_checks(
+ cls, format: str, raises: _RaisesType = (),
+ ) -> typing.Callable[[_F], _F]:
+ def _checks(func: _F) -> _F:
+ cls.checkers[format] = (func, raises)
+ return func
+
+ return _checks
+
+ def check(self, instance: object, format: str) -> None:
+ """
+ Check whether the instance conforms to the given format.
+
+ Arguments:
+
+ instance (*any primitive type*, i.e. str, number, bool):
+
+ The instance to check
+
+ format:
+
+ The format that instance should conform to
+
+ Raises:
+
+ FormatError:
+
+ if the instance does not conform to ``format``
+ """
+
+ if format not in self.checkers:
+ return
+
+ func, raises = self.checkers[format]
+ result, cause = None, None
+ try:
+ result = func(instance)
+ except raises as e:
+ cause = e
+ if not result:
+ raise FormatError(f"{instance!r} is not a {format!r}", cause=cause)
+
+ def conforms(self, instance: object, format: str) -> bool:
+ """
+ Check whether the instance conforms to the given format.
+
+ Arguments:
+
+ instance (*any primitive type*, i.e. str, number, bool):
+
+ The instance to check
+
+ format:
+
+ The format that instance should conform to
+
+ Returns:
+
+ bool: whether it conformed
+ """
+
+ try:
+ self.check(instance, format)
+ except FormatError:
+ return False
+ else:
+ return True
+
+
+draft3_format_checker = FormatChecker()
+draft4_format_checker = FormatChecker()
+draft6_format_checker = FormatChecker()
+draft7_format_checker = FormatChecker()
+draft201909_format_checker = FormatChecker()
+draft202012_format_checker = FormatChecker()
+
+_draft_checkers: dict[str, FormatChecker] = dict(
+ draft3=draft3_format_checker,
+ draft4=draft4_format_checker,
+ draft6=draft6_format_checker,
+ draft7=draft7_format_checker,
+ draft201909=draft201909_format_checker,
+ draft202012=draft202012_format_checker,
+)
+
+
+def _checks_drafts(
+ name=None,
+ draft3=None,
+ draft4=None,
+ draft6=None,
+ draft7=None,
+ draft201909=None,
+ draft202012=None,
+ raises=(),
+) -> typing.Callable[[_F], _F]:
+ draft3 = draft3 or name
+ draft4 = draft4 or name
+ draft6 = draft6 or name
+ draft7 = draft7 or name
+ draft201909 = draft201909 or name
+ draft202012 = draft202012 or name
+
+ def wrap(func: _F) -> _F:
+ if draft3:
+ func = _draft_checkers["draft3"].checks(draft3, raises)(func)
+ if draft4:
+ func = _draft_checkers["draft4"].checks(draft4, raises)(func)
+ if draft6:
+ func = _draft_checkers["draft6"].checks(draft6, raises)(func)
+ if draft7:
+ func = _draft_checkers["draft7"].checks(draft7, raises)(func)
+ if draft201909:
+ func = _draft_checkers["draft201909"].checks(draft201909, raises)(
+ func,
+ )
+ if draft202012:
+ func = _draft_checkers["draft202012"].checks(draft202012, raises)(
+ func,
+ )
+
+ # Oy. This is bad global state, but relied upon for now, until
+ # deprecation. See #519 and test_format_checkers_come_with_defaults
+ FormatChecker._cls_checks(
+ draft202012 or draft201909 or draft7 or draft6 or draft4 or draft3,
+ raises,
+ )(func)
+ return func
+
+ return wrap
+
+
+@_checks_drafts(name="idn-email")
+@_checks_drafts(name="email")
+def is_email(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return "@" in instance
+
+
+@_checks_drafts(
+ draft3="ip-address",
+ draft4="ipv4",
+ draft6="ipv4",
+ draft7="ipv4",
+ draft201909="ipv4",
+ draft202012="ipv4",
+ raises=ipaddress.AddressValueError,
+)
+def is_ipv4(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return bool(ipaddress.IPv4Address(instance))
+
+
+@_checks_drafts(name="ipv6", raises=ipaddress.AddressValueError)
+def is_ipv6(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ address = ipaddress.IPv6Address(instance)
+ return not getattr(address, "scope_id", "")
+
+
+with suppress(ImportError):
+ from fqdn import FQDN
+
+ @_checks_drafts(
+ draft3="host-name",
+ draft4="hostname",
+ draft6="hostname",
+ draft7="hostname",
+ draft201909="hostname",
+ draft202012="hostname",
+ )
+ def is_host_name(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return FQDN(instance).is_valid
+
+
+with suppress(ImportError):
+ # The built-in `idna` codec only implements RFC 3890, so we go elsewhere.
+ import idna
+
+ @_checks_drafts(
+ draft7="idn-hostname",
+ draft201909="idn-hostname",
+ draft202012="idn-hostname",
+ raises=(idna.IDNAError, UnicodeError),
+ )
+ def is_idn_host_name(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ idna.encode(instance)
+ return True
+
+
+try:
+ import rfc3987
+except ImportError:
+ with suppress(ImportError):
+ from rfc3986_validator import validate_rfc3986
+
+ @_checks_drafts(name="uri")
+ def is_uri(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return validate_rfc3986(instance, rule="URI")
+
+ @_checks_drafts(
+ draft6="uri-reference",
+ draft7="uri-reference",
+ draft201909="uri-reference",
+ draft202012="uri-reference",
+ raises=ValueError,
+ )
+ def is_uri_reference(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return validate_rfc3986(instance, rule="URI_reference")
+
+else:
+
+ @_checks_drafts(
+ draft7="iri",
+ draft201909="iri",
+ draft202012="iri",
+ raises=ValueError,
+ )
+ def is_iri(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return rfc3987.parse(instance, rule="IRI")
+
+ @_checks_drafts(
+ draft7="iri-reference",
+ draft201909="iri-reference",
+ draft202012="iri-reference",
+ raises=ValueError,
+ )
+ def is_iri_reference(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return rfc3987.parse(instance, rule="IRI_reference")
+
+ @_checks_drafts(name="uri", raises=ValueError)
+ def is_uri(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return rfc3987.parse(instance, rule="URI")
+
+ @_checks_drafts(
+ draft6="uri-reference",
+ draft7="uri-reference",
+ draft201909="uri-reference",
+ draft202012="uri-reference",
+ raises=ValueError,
+ )
+ def is_uri_reference(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return rfc3987.parse(instance, rule="URI_reference")
+
+
+with suppress(ImportError):
+ from rfc3339_validator import validate_rfc3339
+
+ @_checks_drafts(name="date-time")
+ def is_datetime(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return validate_rfc3339(instance.upper())
+
+ @_checks_drafts(
+ draft7="time",
+ draft201909="time",
+ draft202012="time",
+ )
+ def is_time(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return is_datetime("1970-01-01T" + instance)
+
+
+@_checks_drafts(name="regex", raises=re.error)
+def is_regex(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return bool(re.compile(instance))
+
+
+@_checks_drafts(
+ draft3="date",
+ draft7="date",
+ draft201909="date",
+ draft202012="date",
+ raises=ValueError,
+)
+def is_date(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return bool(instance.isascii() and datetime.date.fromisoformat(instance))
+
+
+@_checks_drafts(draft3="time", raises=ValueError)
+def is_draft3_time(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return bool(datetime.datetime.strptime(instance, "%H:%M:%S"))
+
+
+with suppress(ImportError):
+ from webcolors import CSS21_NAMES_TO_HEX
+ import webcolors
+
+ def is_css_color_code(instance: object) -> bool:
+ return webcolors.normalize_hex(instance)
+
+ @_checks_drafts(draft3="color", raises=(ValueError, TypeError))
+ def is_css21_color(instance: object) -> bool:
+ if (
+ not isinstance(instance, str)
+ or instance.lower() in CSS21_NAMES_TO_HEX
+ ):
+ return True
+ return is_css_color_code(instance)
+
+
+with suppress(ImportError):
+ import jsonpointer
+
+ @_checks_drafts(
+ draft6="json-pointer",
+ draft7="json-pointer",
+ draft201909="json-pointer",
+ draft202012="json-pointer",
+ raises=jsonpointer.JsonPointerException,
+ )
+ def is_json_pointer(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return bool(jsonpointer.JsonPointer(instance))
+
+ # TODO: I don't want to maintain this, so it
+ # needs to go either into jsonpointer (pending
+ # https://github.com/stefankoegl/python-json-pointer/issues/34) or
+ # into a new external library.
+ @_checks_drafts(
+ draft7="relative-json-pointer",
+ draft201909="relative-json-pointer",
+ draft202012="relative-json-pointer",
+ raises=jsonpointer.JsonPointerException,
+ )
+ def is_relative_json_pointer(instance: object) -> bool:
+ # Definition taken from:
+ # https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
+ if not isinstance(instance, str):
+ return True
+ if not instance:
+ return False
+
+ non_negative_integer, rest = [], ""
+ for i, character in enumerate(instance):
+ if character.isdigit():
+ # digits with a leading "0" are not allowed
+ if i > 0 and int(instance[i - 1]) == 0:
+ return False
+
+ non_negative_integer.append(character)
+ continue
+
+ if not non_negative_integer:
+ return False
+
+ rest = instance[i:]
+ break
+ return (rest == "#") or bool(jsonpointer.JsonPointer(rest))
+
+
+with suppress(ImportError):
+ import uri_template
+
+ @_checks_drafts(
+ draft6="uri-template",
+ draft7="uri-template",
+ draft201909="uri-template",
+ draft202012="uri-template",
+ )
+ def is_uri_template(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ return uri_template.validate(instance)
+
+
+with suppress(ImportError):
+ import isoduration
+
+ @_checks_drafts(
+ draft201909="duration",
+ draft202012="duration",
+ raises=isoduration.DurationParsingException,
+ )
+ def is_duration(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ isoduration.parse_duration(instance)
+ # FIXME: See bolsote/isoduration#25 and bolsote/isoduration#21
+ return instance.endswith(tuple("DMYWHMS"))
+
+
+@_checks_drafts(
+ draft201909="uuid",
+ draft202012="uuid",
+ raises=ValueError,
+)
+def is_uuid(instance: object) -> bool:
+ if not isinstance(instance, str):
+ return True
+ UUID(instance)
+ return all(instance[position] == "-" for position in (8, 13, 18, 23))
diff --git a/third_party/python/jsonschema/jsonschema/_legacy_validators.py b/third_party/python/jsonschema/jsonschema/_legacy_validators.py
new file mode 100644
index 0000000000..cc5e3f44c1
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/_legacy_validators.py
@@ -0,0 +1,319 @@
+from jsonschema import _utils
+from jsonschema.exceptions import ValidationError
+
+
+def id_of_ignore_ref(property="$id"):
+ def id_of(schema):
+ """
+ Ignore an ``$id`` sibling of ``$ref`` if it is present.
+
+ Otherwise, return the ID of the given schema.
+ """
+ if schema is True or schema is False or "$ref" in schema:
+ return ""
+ return schema.get(property, "")
+ return id_of
+
+
+def ignore_ref_siblings(schema):
+ """
+ Ignore siblings of ``$ref`` if it is present.
+
+ Otherwise, return all keywords.
+
+ Suitable for use with `create`'s ``applicable_validators`` argument.
+ """
+ ref = schema.get("$ref")
+ if ref is not None:
+ return [("$ref", ref)]
+ else:
+ return schema.items()
+
+
+def dependencies_draft3(validator, dependencies, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, dependency in dependencies.items():
+ if property not in instance:
+ continue
+
+ if validator.is_type(dependency, "object"):
+ yield from validator.descend(
+ instance, dependency, schema_path=property,
+ )
+ elif validator.is_type(dependency, "string"):
+ if dependency not in instance:
+ message = f"{dependency!r} is a dependency of {property!r}"
+ yield ValidationError(message)
+ else:
+ for each in dependency:
+ if each not in instance:
+ message = f"{each!r} is a dependency of {property!r}"
+ yield ValidationError(message)
+
+
+def dependencies_draft4_draft6_draft7(
+ validator,
+ dependencies,
+ instance,
+ schema,
+):
+ """
+ Support for the ``dependencies`` keyword from pre-draft 2019-09.
+
+ In later drafts, the keyword was split into separate
+ ``dependentRequired`` and ``dependentSchemas`` validators.
+ """
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, dependency in dependencies.items():
+ if property not in instance:
+ continue
+
+ if validator.is_type(dependency, "array"):
+ for each in dependency:
+ if each not in instance:
+ message = f"{each!r} is a dependency of {property!r}"
+ yield ValidationError(message)
+ else:
+ yield from validator.descend(
+ instance, dependency, schema_path=property,
+ )
+
+
+def disallow_draft3(validator, disallow, instance, schema):
+ for disallowed in _utils.ensure_list(disallow):
+ if validator.evolve(schema={"type": [disallowed]}).is_valid(instance):
+ message = f"{disallowed!r} is disallowed for {instance!r}"
+ yield ValidationError(message)
+
+
+def extends_draft3(validator, extends, instance, schema):
+ if validator.is_type(extends, "object"):
+ yield from validator.descend(instance, extends)
+ return
+ for index, subschema in enumerate(extends):
+ yield from validator.descend(instance, subschema, schema_path=index)
+
+
+def items_draft3_draft4(validator, items, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ if validator.is_type(items, "object"):
+ for index, item in enumerate(instance):
+ yield from validator.descend(item, items, path=index)
+ else:
+ for (index, item), subschema in zip(enumerate(instance), items):
+ yield from validator.descend(
+ item, subschema, path=index, schema_path=index,
+ )
+
+
+def items_draft6_draft7_draft201909(validator, items, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ if validator.is_type(items, "array"):
+ for (index, item), subschema in zip(enumerate(instance), items):
+ yield from validator.descend(
+ item, subschema, path=index, schema_path=index,
+ )
+ else:
+ for index, item in enumerate(instance):
+ yield from validator.descend(item, items, path=index)
+
+
+def minimum_draft3_draft4(validator, minimum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if schema.get("exclusiveMinimum", False):
+ failed = instance <= minimum
+ cmp = "less than or equal to"
+ else:
+ failed = instance < minimum
+ cmp = "less than"
+
+ if failed:
+ message = f"{instance!r} is {cmp} the minimum of {minimum!r}"
+ yield ValidationError(message)
+
+
+def maximum_draft3_draft4(validator, maximum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if schema.get("exclusiveMaximum", False):
+ failed = instance >= maximum
+ cmp = "greater than or equal to"
+ else:
+ failed = instance > maximum
+ cmp = "greater than"
+
+ if failed:
+ message = f"{instance!r} is {cmp} the maximum of {maximum!r}"
+ yield ValidationError(message)
+
+
+def properties_draft3(validator, properties, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, subschema in properties.items():
+ if property in instance:
+ yield from validator.descend(
+ instance[property],
+ subschema,
+ path=property,
+ schema_path=property,
+ )
+ elif subschema.get("required", False):
+ error = ValidationError(f"{property!r} is a required property")
+ error._set(
+ validator="required",
+ validator_value=subschema["required"],
+ instance=instance,
+ schema=schema,
+ )
+ error.path.appendleft(property)
+ error.schema_path.extend([property, "required"])
+ yield error
+
+
+def type_draft3(validator, types, instance, schema):
+ types = _utils.ensure_list(types)
+
+ all_errors = []
+ for index, type in enumerate(types):
+ if validator.is_type(type, "object"):
+ errors = list(validator.descend(instance, type, schema_path=index))
+ if not errors:
+ return
+ all_errors.extend(errors)
+ else:
+ if validator.is_type(instance, type):
+ return
+ else:
+ reprs = []
+ for type in types:
+ try:
+ reprs.append(repr(type["name"]))
+ except Exception:
+ reprs.append(repr(type))
+ yield ValidationError(
+ f"{instance!r} is not of type {', '.join(reprs)}",
+ context=all_errors,
+ )
+
+
+def contains_draft6_draft7(validator, contains, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ if not any(
+ validator.evolve(schema=contains).is_valid(element)
+ for element in instance
+ ):
+ yield ValidationError(
+ f"None of {instance!r} are valid under the given schema",
+ )
+
+
+def recursiveRef(validator, recursiveRef, instance, schema):
+ lookup_url, target = validator.resolver.resolution_scope, validator.schema
+
+ for each in reversed(validator.resolver._scopes_stack[1:]):
+ lookup_url, next_target = validator.resolver.resolve(each)
+ if next_target.get("$recursiveAnchor"):
+ target = next_target
+ else:
+ break
+
+ fragment = recursiveRef.lstrip("#")
+ subschema = validator.resolver.resolve_fragment(target, fragment)
+ # FIXME: This is gutted (and not calling .descend) because it can trigger
+ # recursion errors, so there's a bug here. Re-enable the tests to
+ # see it.
+ subschema
+ return []
+
+
+def find_evaluated_item_indexes_by_schema(validator, instance, schema):
+ """
+ Get all indexes of items that get evaluated under the current schema
+
+ Covers all keywords related to unevaluatedItems: items, prefixItems, if,
+ then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
+ """
+ if validator.is_type(schema, "boolean"):
+ return []
+ evaluated_indexes = []
+
+ if "additionalItems" in schema:
+ return list(range(0, len(instance)))
+
+ if "$ref" in schema:
+ scope, resolved = validator.resolver.resolve(schema["$ref"])
+ validator.resolver.push_scope(scope)
+
+ try:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, resolved,
+ )
+ finally:
+ validator.resolver.pop_scope()
+
+ if "items" in schema:
+ if validator.is_type(schema["items"], "object"):
+ return list(range(0, len(instance)))
+ evaluated_indexes += list(range(0, len(schema["items"])))
+
+ if "if" in schema:
+ if validator.evolve(schema=schema["if"]).is_valid(instance):
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["if"],
+ )
+ if "then" in schema:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["then"],
+ )
+ else:
+ if "else" in schema:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["else"],
+ )
+
+ for keyword in ["contains", "unevaluatedItems"]:
+ if keyword in schema:
+ for k, v in enumerate(instance):
+ if validator.evolve(schema=schema[keyword]).is_valid(v):
+ evaluated_indexes.append(k)
+
+ for keyword in ["allOf", "oneOf", "anyOf"]:
+ if keyword in schema:
+ for subschema in schema[keyword]:
+ errs = list(validator.descend(instance, subschema))
+ if not errs:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, subschema,
+ )
+
+ return evaluated_indexes
+
+
+def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+ evaluated_item_indexes = find_evaluated_item_indexes_by_schema(
+ validator, instance, schema,
+ )
+ unevaluated_items = [
+ item for index, item in enumerate(instance)
+ if index not in evaluated_item_indexes
+ ]
+ if unevaluated_items:
+ error = "Unevaluated items are not allowed (%s %s unexpected)"
+ yield ValidationError(error % _utils.extras_msg(unevaluated_items))
diff --git a/third_party/python/jsonschema/jsonschema/_types.py b/third_party/python/jsonschema/jsonschema/_types.py
new file mode 100644
index 0000000000..5b543f71bb
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/_types.py
@@ -0,0 +1,203 @@
+from __future__ import annotations
+
+import numbers
+import typing
+
+from pyrsistent import pmap
+from pyrsistent.typing import PMap
+import attr
+
+from jsonschema.exceptions import UndefinedTypeCheck
+
+
+# unfortunately, the type of pmap is generic, and if used as the attr.ib
+# converter, the generic type is presented to mypy, which then fails to match
+# the concrete type of a type checker mapping
+# this "do nothing" wrapper presents the correct information to mypy
+def _typed_pmap_converter(
+ init_val: typing.Mapping[
+ str,
+ typing.Callable[["TypeChecker", typing.Any], bool],
+ ],
+) -> PMap[str, typing.Callable[["TypeChecker", typing.Any], bool]]:
+ return pmap(init_val)
+
+
+def is_array(checker, instance):
+ return isinstance(instance, list)
+
+
+def is_bool(checker, instance):
+ return isinstance(instance, bool)
+
+
+def is_integer(checker, instance):
+ # bool inherits from int, so ensure bools aren't reported as ints
+ if isinstance(instance, bool):
+ return False
+ return isinstance(instance, int)
+
+
+def is_null(checker, instance):
+ return instance is None
+
+
+def is_number(checker, instance):
+ # bool inherits from int, so ensure bools aren't reported as ints
+ if isinstance(instance, bool):
+ return False
+ return isinstance(instance, numbers.Number)
+
+
+def is_object(checker, instance):
+ return isinstance(instance, dict)
+
+
+def is_string(checker, instance):
+ return isinstance(instance, str)
+
+
+def is_any(checker, instance):
+ return True
+
+
+@attr.s(frozen=True, repr=False)
+class TypeChecker:
+ """
+ A :kw:`type` property checker.
+
+ A `TypeChecker` performs type checking for a `Validator`, converting
+ between the defined JSON Schema types and some associated Python types or
+ objects.
+
+ Modifying the behavior just mentioned by redefining which Python objects
+ are considered to be of which JSON Schema types can be done using
+ `TypeChecker.redefine` or `TypeChecker.redefine_many`, and types can be
+ removed via `TypeChecker.remove`. Each of these return a new `TypeChecker`.
+
+ Arguments:
+
+ type_checkers:
+
+ The initial mapping of types to their checking functions.
+ """
+
+ _type_checkers: PMap[
+ str, typing.Callable[["TypeChecker", typing.Any], bool],
+ ] = attr.ib(
+ default=pmap(),
+ converter=_typed_pmap_converter,
+ )
+
+ def __repr__(self):
+ types = ", ".join(repr(k) for k in sorted(self._type_checkers))
+ return f"<{self.__class__.__name__} types={{{types}}}>"
+
+ def is_type(self, instance, type: str) -> bool:
+ """
+ Check if the instance is of the appropriate type.
+
+ Arguments:
+
+ instance:
+
+ The instance to check
+
+ type:
+
+ The name of the type that is expected.
+
+ Raises:
+
+ `jsonschema.exceptions.UndefinedTypeCheck`:
+
+ if ``type`` is unknown to this object.
+ """
+ try:
+ fn = self._type_checkers[type]
+ except KeyError:
+ raise UndefinedTypeCheck(type) from None
+
+ return fn(self, instance)
+
+ def redefine(self, type: str, fn) -> "TypeChecker":
+ """
+ Produce a new checker with the given type redefined.
+
+ Arguments:
+
+ type:
+
+ The name of the type to check.
+
+ fn (collections.abc.Callable):
+
+ A callable taking exactly two parameters - the type
+ checker calling the function and the instance to check.
+ The function should return true if instance is of this
+ type and false otherwise.
+ """
+ return self.redefine_many({type: fn})
+
+ def redefine_many(self, definitions=()) -> "TypeChecker":
+ """
+ Produce a new checker with the given types redefined.
+
+ Arguments:
+
+ definitions (dict):
+
+ A dictionary mapping types to their checking functions.
+ """
+ type_checkers = self._type_checkers.update(definitions)
+ return attr.evolve(self, type_checkers=type_checkers)
+
+ def remove(self, *types) -> "TypeChecker":
+ """
+ Produce a new checker with the given types forgotten.
+
+ Arguments:
+
+ types:
+
+ the names of the types to remove.
+
+ Raises:
+
+ `jsonschema.exceptions.UndefinedTypeCheck`:
+
+ if any given type is unknown to this object
+ """
+
+ type_checkers = self._type_checkers
+ for each in types:
+ try:
+ type_checkers = type_checkers.remove(each)
+ except KeyError:
+ raise UndefinedTypeCheck(each)
+ return attr.evolve(self, type_checkers=type_checkers)
+
+
+draft3_type_checker = TypeChecker(
+ {
+ "any": is_any,
+ "array": is_array,
+ "boolean": is_bool,
+ "integer": is_integer,
+ "object": is_object,
+ "null": is_null,
+ "number": is_number,
+ "string": is_string,
+ },
+)
+draft4_type_checker = draft3_type_checker.remove("any")
+draft6_type_checker = draft4_type_checker.redefine(
+ "integer",
+ lambda checker, instance: (
+ is_integer(checker, instance)
+ or isinstance(instance, float) and instance.is_integer()
+ ),
+)
+draft7_type_checker = draft6_type_checker
+draft201909_type_checker = draft7_type_checker
+draft202012_type_checker = draft201909_type_checker
diff --git a/third_party/python/jsonschema/jsonschema/_utils.py b/third_party/python/jsonschema/jsonschema/_utils.py
new file mode 100644
index 0000000000..418348ce1c
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/_utils.py
@@ -0,0 +1,349 @@
+from collections.abc import Mapping, MutableMapping, Sequence
+from urllib.parse import urlsplit
+import itertools
+import json
+import re
+import sys
+
+# The files() API was added in Python 3.9.
+if sys.version_info >= (3, 9): # pragma: no cover
+ from importlib import resources
+else: # pragma: no cover
+ import importlib_resources as resources # type: ignore
+
+
+class URIDict(MutableMapping):
+ """
+ Dictionary which uses normalized URIs as keys.
+ """
+
+ def normalize(self, uri):
+ return urlsplit(uri).geturl()
+
+ def __init__(self, *args, **kwargs):
+ self.store = dict()
+ self.store.update(*args, **kwargs)
+
+ def __getitem__(self, uri):
+ return self.store[self.normalize(uri)]
+
+ def __setitem__(self, uri, value):
+ self.store[self.normalize(uri)] = value
+
+ def __delitem__(self, uri):
+ del self.store[self.normalize(uri)]
+
+ def __iter__(self):
+ return iter(self.store)
+
+ def __len__(self):
+ return len(self.store)
+
+ def __repr__(self):
+ return repr(self.store)
+
+
+class Unset:
+ """
+ An as-of-yet unset attribute or unprovided default parameter.
+ """
+
+ def __repr__(self):
+ return "<unset>"
+
+
+def load_schema(name):
+ """
+ Load a schema from ./schemas/``name``.json and return it.
+ """
+
+ path = resources.files(__package__).joinpath(f"schemas/{name}.json")
+ data = path.read_text(encoding="utf-8")
+ return json.loads(data)
+
+
+def format_as_index(container, indices):
+ """
+ Construct a single string containing indexing operations for the indices.
+
+ For example for a container ``bar``, [1, 2, "foo"] -> bar[1][2]["foo"]
+
+ Arguments:
+
+ container (str):
+
+ A word to use for the thing being indexed
+
+ indices (sequence):
+
+ The indices to format.
+ """
+
+ if not indices:
+ return container
+ return f"{container}[{']['.join(repr(index) for index in indices)}]"
+
+
+def find_additional_properties(instance, schema):
+ """
+ Return the set of additional properties for the given ``instance``.
+
+ Weeds out properties that should have been validated by ``properties`` and
+ / or ``patternProperties``.
+
+ Assumes ``instance`` is dict-like already.
+ """
+
+ properties = schema.get("properties", {})
+ patterns = "|".join(schema.get("patternProperties", {}))
+ for property in instance:
+ if property not in properties:
+ if patterns and re.search(patterns, property):
+ continue
+ yield property
+
+
+def extras_msg(extras):
+ """
+ Create an error message for extra items or properties.
+ """
+
+ if len(extras) == 1:
+ verb = "was"
+ else:
+ verb = "were"
+ return ", ".join(repr(extra) for extra in sorted(extras)), verb
+
+
+def ensure_list(thing):
+ """
+ Wrap ``thing`` in a list if it's a single str.
+
+ Otherwise, return it unchanged.
+ """
+
+ if isinstance(thing, str):
+ return [thing]
+ return thing
+
+
+def _mapping_equal(one, two):
+ """
+ Check if two mappings are equal using the semantics of `equal`.
+ """
+ if len(one) != len(two):
+ return False
+ return all(
+ key in two and equal(value, two[key])
+ for key, value in one.items()
+ )
+
+
+def _sequence_equal(one, two):
+ """
+ Check if two sequences are equal using the semantics of `equal`.
+ """
+ if len(one) != len(two):
+ return False
+ return all(equal(i, j) for i, j in zip(one, two))
+
+
+def equal(one, two):
+ """
+ Check if two things are equal evading some Python type hierarchy semantics.
+
+ Specifically in JSON Schema, evade `bool` inheriting from `int`,
+ recursing into sequences to do the same.
+ """
+ if isinstance(one, str) or isinstance(two, str):
+ return one == two
+ if isinstance(one, Sequence) and isinstance(two, Sequence):
+ return _sequence_equal(one, two)
+ if isinstance(one, Mapping) and isinstance(two, Mapping):
+ return _mapping_equal(one, two)
+ return unbool(one) == unbool(two)
+
+
+def unbool(element, true=object(), false=object()):
+ """
+ A hack to make True and 1 and False and 0 unique for ``uniq``.
+ """
+
+ if element is True:
+ return true
+ elif element is False:
+ return false
+ return element
+
+
+def uniq(container):
+ """
+ Check if all of a container's elements are unique.
+
+ Tries to rely on the container being recursively sortable, or otherwise
+ falls back on (slow) brute force.
+ """
+ try:
+ sort = sorted(unbool(i) for i in container)
+ sliced = itertools.islice(sort, 1, None)
+
+ for i, j in zip(sort, sliced):
+ if equal(i, j):
+ return False
+
+ except (NotImplementedError, TypeError):
+ seen = []
+ for e in container:
+ e = unbool(e)
+
+ for i in seen:
+ if equal(i, e):
+ return False
+
+ seen.append(e)
+ return True
+
+
+def find_evaluated_item_indexes_by_schema(validator, instance, schema):
+ """
+ Get all indexes of items that get evaluated under the current schema
+
+ Covers all keywords related to unevaluatedItems: items, prefixItems, if,
+ then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
+ """
+ if validator.is_type(schema, "boolean"):
+ return []
+ evaluated_indexes = []
+
+ if "items" in schema:
+ return list(range(0, len(instance)))
+
+ if "$ref" in schema:
+ scope, resolved = validator.resolver.resolve(schema["$ref"])
+ validator.resolver.push_scope(scope)
+
+ try:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, resolved,
+ )
+ finally:
+ validator.resolver.pop_scope()
+
+ if "prefixItems" in schema:
+ evaluated_indexes += list(range(0, len(schema["prefixItems"])))
+
+ if "if" in schema:
+ if validator.evolve(schema=schema["if"]).is_valid(instance):
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["if"],
+ )
+ if "then" in schema:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["then"],
+ )
+ else:
+ if "else" in schema:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, schema["else"],
+ )
+
+ for keyword in ["contains", "unevaluatedItems"]:
+ if keyword in schema:
+ for k, v in enumerate(instance):
+ if validator.evolve(schema=schema[keyword]).is_valid(v):
+ evaluated_indexes.append(k)
+
+ for keyword in ["allOf", "oneOf", "anyOf"]:
+ if keyword in schema:
+ for subschema in schema[keyword]:
+ errs = list(validator.descend(instance, subschema))
+ if not errs:
+ evaluated_indexes += find_evaluated_item_indexes_by_schema(
+ validator, instance, subschema,
+ )
+
+ return evaluated_indexes
+
+
+def find_evaluated_property_keys_by_schema(validator, instance, schema):
+ """
+ Get all keys of items that get evaluated under the current schema
+
+ Covers all keywords related to unevaluatedProperties: properties,
+ additionalProperties, unevaluatedProperties, patternProperties,
+ dependentSchemas, allOf, oneOf, anyOf, if, then, else
+ """
+ if validator.is_type(schema, "boolean"):
+ return []
+ evaluated_keys = []
+
+ if "$ref" in schema:
+ scope, resolved = validator.resolver.resolve(schema["$ref"])
+ validator.resolver.push_scope(scope)
+
+ try:
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, resolved,
+ )
+ finally:
+ validator.resolver.pop_scope()
+
+ for keyword in [
+ "properties", "additionalProperties", "unevaluatedProperties",
+ ]:
+ if keyword in schema:
+ if validator.is_type(schema[keyword], "boolean"):
+ for property, value in instance.items():
+ if validator.evolve(schema=schema[keyword]).is_valid(
+ {property: value},
+ ):
+ evaluated_keys.append(property)
+
+ if validator.is_type(schema[keyword], "object"):
+ for property, subschema in schema[keyword].items():
+ if property in instance and validator.evolve(
+ schema=subschema,
+ ).is_valid(instance[property]):
+ evaluated_keys.append(property)
+
+ if "patternProperties" in schema:
+ for property, value in instance.items():
+ for pattern, _ in schema["patternProperties"].items():
+ if re.search(pattern, property) and validator.evolve(
+ schema=schema["patternProperties"],
+ ).is_valid({property: value}):
+ evaluated_keys.append(property)
+
+ if "dependentSchemas" in schema:
+ for property, subschema in schema["dependentSchemas"].items():
+ if property not in instance:
+ continue
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, subschema,
+ )
+
+ for keyword in ["allOf", "oneOf", "anyOf"]:
+ if keyword in schema:
+ for subschema in schema[keyword]:
+ errs = list(validator.descend(instance, subschema))
+ if not errs:
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, subschema,
+ )
+
+ if "if" in schema:
+ if validator.evolve(schema=schema["if"]).is_valid(instance):
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, schema["if"],
+ )
+ if "then" in schema:
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, schema["then"],
+ )
+ else:
+ if "else" in schema:
+ evaluated_keys += find_evaluated_property_keys_by_schema(
+ validator, instance, schema["else"],
+ )
+
+ return evaluated_keys
diff --git a/third_party/python/jsonschema/jsonschema/_validators.py b/third_party/python/jsonschema/jsonschema/_validators.py
new file mode 100644
index 0000000000..8542a879c8
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/_validators.py
@@ -0,0 +1,476 @@
+from fractions import Fraction
+from urllib.parse import urldefrag, urljoin
+import re
+
+from jsonschema._utils import (
+ ensure_list,
+ equal,
+ extras_msg,
+ find_additional_properties,
+ find_evaluated_item_indexes_by_schema,
+ find_evaluated_property_keys_by_schema,
+ unbool,
+ uniq,
+)
+from jsonschema.exceptions import FormatError, ValidationError
+
+
+def patternProperties(validator, patternProperties, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for pattern, subschema in patternProperties.items():
+ for k, v in instance.items():
+ if re.search(pattern, k):
+ yield from validator.descend(
+ v, subschema, path=k, schema_path=pattern,
+ )
+
+
+def propertyNames(validator, propertyNames, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property in instance:
+ yield from validator.descend(instance=property, schema=propertyNames)
+
+
+def additionalProperties(validator, aP, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ extras = set(find_additional_properties(instance, schema))
+
+ if validator.is_type(aP, "object"):
+ for extra in extras:
+ yield from validator.descend(instance[extra], aP, path=extra)
+ elif not aP and extras:
+ if "patternProperties" in schema:
+ if len(extras) == 1:
+ verb = "does"
+ else:
+ verb = "do"
+
+ joined = ", ".join(repr(each) for each in sorted(extras))
+ patterns = ", ".join(
+ repr(each) for each in sorted(schema["patternProperties"])
+ )
+ error = f"{joined} {verb} not match any of the regexes: {patterns}"
+ yield ValidationError(error)
+ else:
+ error = "Additional properties are not allowed (%s %s unexpected)"
+ yield ValidationError(error % extras_msg(extras))
+
+
+def items(validator, items, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ prefix = len(schema.get("prefixItems", []))
+ total = len(instance)
+ if items is False and total > prefix:
+ message = f"Expected at most {prefix} items, but found {total}"
+ yield ValidationError(message)
+ else:
+ for index in range(prefix, total):
+ yield from validator.descend(
+ instance=instance[index],
+ schema=items,
+ path=index,
+ )
+
+
+def additionalItems(validator, aI, instance, schema):
+ if (
+ not validator.is_type(instance, "array")
+ or validator.is_type(schema.get("items", {}), "object")
+ ):
+ return
+
+ len_items = len(schema.get("items", []))
+ if validator.is_type(aI, "object"):
+ for index, item in enumerate(instance[len_items:], start=len_items):
+ yield from validator.descend(item, aI, path=index)
+ elif not aI and len(instance) > len(schema.get("items", [])):
+ error = "Additional items are not allowed (%s %s unexpected)"
+ yield ValidationError(
+ error % extras_msg(instance[len(schema.get("items", [])):]),
+ )
+
+
+def const(validator, const, instance, schema):
+ if not equal(instance, const):
+ yield ValidationError(f"{const!r} was expected")
+
+
+def contains(validator, contains, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ matches = 0
+ min_contains = schema.get("minContains", 1)
+ max_contains = schema.get("maxContains", len(instance))
+
+ for each in instance:
+ if validator.evolve(schema=contains).is_valid(each):
+ matches += 1
+ if matches > max_contains:
+ yield ValidationError(
+ "Too many items match the given schema "
+ f"(expected at most {max_contains})",
+ validator="maxContains",
+ validator_value=max_contains,
+ )
+ return
+
+ if matches < min_contains:
+ if not matches:
+ yield ValidationError(
+ f"{instance!r} does not contain items "
+ "matching the given schema",
+ )
+ else:
+ yield ValidationError(
+ "Too few items match the given schema (expected at least "
+ f"{min_contains} but only {matches} matched)",
+ validator="minContains",
+ validator_value=min_contains,
+ )
+
+
+def exclusiveMinimum(validator, minimum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if instance <= minimum:
+ yield ValidationError(
+ f"{instance!r} is less than or equal to "
+ f"the minimum of {minimum!r}",
+ )
+
+
+def exclusiveMaximum(validator, maximum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if instance >= maximum:
+ yield ValidationError(
+ f"{instance!r} is greater than or equal "
+ f"to the maximum of {maximum!r}",
+ )
+
+
+def minimum(validator, minimum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if instance < minimum:
+ message = f"{instance!r} is less than the minimum of {minimum!r}"
+ yield ValidationError(message)
+
+
+def maximum(validator, maximum, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if instance > maximum:
+ message = f"{instance!r} is greater than the maximum of {maximum!r}"
+ yield ValidationError(message)
+
+
+def multipleOf(validator, dB, instance, schema):
+ if not validator.is_type(instance, "number"):
+ return
+
+ if isinstance(dB, float):
+ quotient = instance / dB
+ try:
+ failed = int(quotient) != quotient
+ except OverflowError:
+ # When `instance` is large and `dB` is less than one,
+ # quotient can overflow to infinity; and then casting to int
+ # raises an error.
+ #
+ # In this case we fall back to Fraction logic, which is
+ # exact and cannot overflow. The performance is also
+ # acceptable: we try the fast all-float option first, and
+ # we know that fraction(dB) can have at most a few hundred
+ # digits in each part. The worst-case slowdown is therefore
+ # for already-slow enormous integers or Decimals.
+ failed = (Fraction(instance) / Fraction(dB)).denominator != 1
+ else:
+ failed = instance % dB
+
+ if failed:
+ yield ValidationError(f"{instance!r} is not a multiple of {dB}")
+
+
+def minItems(validator, mI, instance, schema):
+ if validator.is_type(instance, "array") and len(instance) < mI:
+ yield ValidationError(f"{instance!r} is too short")
+
+
+def maxItems(validator, mI, instance, schema):
+ if validator.is_type(instance, "array") and len(instance) > mI:
+ yield ValidationError(f"{instance!r} is too long")
+
+
+def uniqueItems(validator, uI, instance, schema):
+ if (
+ uI
+ and validator.is_type(instance, "array")
+ and not uniq(instance)
+ ):
+ yield ValidationError(f"{instance!r} has non-unique elements")
+
+
+def pattern(validator, patrn, instance, schema):
+ if (
+ validator.is_type(instance, "string")
+ and not re.search(patrn, instance)
+ ):
+ yield ValidationError(f"{instance!r} does not match {patrn!r}")
+
+
+def format(validator, format, instance, schema):
+ if validator.format_checker is not None:
+ try:
+ validator.format_checker.check(instance, format)
+ except FormatError as error:
+ yield ValidationError(error.message, cause=error.cause)
+
+
+def minLength(validator, mL, instance, schema):
+ if validator.is_type(instance, "string") and len(instance) < mL:
+ yield ValidationError(f"{instance!r} is too short")
+
+
+def maxLength(validator, mL, instance, schema):
+ if validator.is_type(instance, "string") and len(instance) > mL:
+ yield ValidationError(f"{instance!r} is too long")
+
+
+def dependentRequired(validator, dependentRequired, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, dependency in dependentRequired.items():
+ if property not in instance:
+ continue
+
+ for each in dependency:
+ if each not in instance:
+ message = f"{each!r} is a dependency of {property!r}"
+ yield ValidationError(message)
+
+
+def dependentSchemas(validator, dependentSchemas, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, dependency in dependentSchemas.items():
+ if property not in instance:
+ continue
+ yield from validator.descend(
+ instance, dependency, schema_path=property,
+ )
+
+
+def enum(validator, enums, instance, schema):
+ if instance == 0 or instance == 1:
+ unbooled = unbool(instance)
+ if all(unbooled != unbool(each) for each in enums):
+ yield ValidationError(f"{instance!r} is not one of {enums!r}")
+ elif instance not in enums:
+ yield ValidationError(f"{instance!r} is not one of {enums!r}")
+
+
+def ref(validator, ref, instance, schema):
+ resolve = getattr(validator.resolver, "resolve", None)
+ if resolve is None:
+ with validator.resolver.resolving(ref) as resolved:
+ yield from validator.descend(instance, resolved)
+ else:
+ scope, resolved = validator.resolver.resolve(ref)
+ validator.resolver.push_scope(scope)
+
+ try:
+ yield from validator.descend(instance, resolved)
+ finally:
+ validator.resolver.pop_scope()
+
+
+def dynamicRef(validator, dynamicRef, instance, schema):
+ _, fragment = urldefrag(dynamicRef)
+
+ for url in validator.resolver._scopes_stack:
+ lookup_url = urljoin(url, dynamicRef)
+ with validator.resolver.resolving(lookup_url) as subschema:
+ if ("$dynamicAnchor" in subschema
+ and fragment == subschema["$dynamicAnchor"]):
+ yield from validator.descend(instance, subschema)
+ break
+ else:
+ with validator.resolver.resolving(dynamicRef) as subschema:
+ yield from validator.descend(instance, subschema)
+
+
+def type(validator, types, instance, schema):
+ types = ensure_list(types)
+
+ if not any(validator.is_type(instance, type) for type in types):
+ reprs = ", ".join(repr(type) for type in types)
+ yield ValidationError(f"{instance!r} is not of type {reprs}")
+
+
+def properties(validator, properties, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+
+ for property, subschema in properties.items():
+ if property in instance:
+ yield from validator.descend(
+ instance[property],
+ subschema,
+ path=property,
+ schema_path=property,
+ )
+
+
+def required(validator, required, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+ for property in required:
+ if property not in instance:
+ yield ValidationError(f"{property!r} is a required property")
+
+
+def minProperties(validator, mP, instance, schema):
+ if validator.is_type(instance, "object") and len(instance) < mP:
+ yield ValidationError(f"{instance!r} does not have enough properties")
+
+
+def maxProperties(validator, mP, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+ if validator.is_type(instance, "object") and len(instance) > mP:
+ yield ValidationError(f"{instance!r} has too many properties")
+
+
+def allOf(validator, allOf, instance, schema):
+ for index, subschema in enumerate(allOf):
+ yield from validator.descend(instance, subschema, schema_path=index)
+
+
+def anyOf(validator, anyOf, instance, schema):
+ all_errors = []
+ for index, subschema in enumerate(anyOf):
+ errs = list(validator.descend(instance, subschema, schema_path=index))
+ if not errs:
+ break
+ all_errors.extend(errs)
+ else:
+ yield ValidationError(
+ f"{instance!r} is not valid under any of the given schemas",
+ context=all_errors,
+ )
+
+
+def oneOf(validator, oneOf, instance, schema):
+ subschemas = enumerate(oneOf)
+ all_errors = []
+ for index, subschema in subschemas:
+ errs = list(validator.descend(instance, subschema, schema_path=index))
+ if not errs:
+ first_valid = subschema
+ break
+ all_errors.extend(errs)
+ else:
+ yield ValidationError(
+ f"{instance!r} is not valid under any of the given schemas",
+ context=all_errors,
+ )
+
+ more_valid = [
+ each for _, each in subschemas
+ if validator.evolve(schema=each).is_valid(instance)
+ ]
+ if more_valid:
+ more_valid.append(first_valid)
+ reprs = ", ".join(repr(schema) for schema in more_valid)
+ yield ValidationError(f"{instance!r} is valid under each of {reprs}")
+
+
+def not_(validator, not_schema, instance, schema):
+ if validator.evolve(schema=not_schema).is_valid(instance):
+ message = f"{instance!r} should not be valid under {not_schema!r}"
+ yield ValidationError(message)
+
+
+def if_(validator, if_schema, instance, schema):
+ if validator.evolve(schema=if_schema).is_valid(instance):
+ if "then" in schema:
+ then = schema["then"]
+ yield from validator.descend(instance, then, schema_path="then")
+ elif "else" in schema:
+ else_ = schema["else"]
+ yield from validator.descend(instance, else_, schema_path="else")
+
+
+def unevaluatedItems(validator, unevaluatedItems, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+ evaluated_item_indexes = find_evaluated_item_indexes_by_schema(
+ validator, instance, schema,
+ )
+ unevaluated_items = [
+ item for index, item in enumerate(instance)
+ if index not in evaluated_item_indexes
+ ]
+ if unevaluated_items:
+ error = "Unevaluated items are not allowed (%s %s unexpected)"
+ yield ValidationError(error % extras_msg(unevaluated_items))
+
+
+def unevaluatedProperties(validator, unevaluatedProperties, instance, schema):
+ if not validator.is_type(instance, "object"):
+ return
+ evaluated_keys = find_evaluated_property_keys_by_schema(
+ validator, instance, schema,
+ )
+ unevaluated_keys = []
+ for property in instance:
+ if property not in evaluated_keys:
+ for _ in validator.descend(
+ instance[property],
+ unevaluatedProperties,
+ path=property,
+ schema_path=property,
+ ):
+ # FIXME: Include context for each unevaluated property
+ # indicating why it's invalid under the subschema.
+ unevaluated_keys.append(property)
+
+ if unevaluated_keys:
+ if unevaluatedProperties is False:
+ error = "Unevaluated properties are not allowed (%s %s unexpected)"
+ yield ValidationError(error % extras_msg(unevaluated_keys))
+ else:
+ error = (
+ "Unevaluated properties are not valid under "
+ "the given schema (%s %s unevaluated and invalid)"
+ )
+ yield ValidationError(error % extras_msg(unevaluated_keys))
+
+
+def prefixItems(validator, prefixItems, instance, schema):
+ if not validator.is_type(instance, "array"):
+ return
+
+ for (index, item), subschema in zip(enumerate(instance), prefixItems):
+ yield from validator.descend(
+ instance=item,
+ schema=subschema,
+ schema_path=index,
+ path=index,
+ )
diff --git a/third_party/python/jsonschema/jsonschema/benchmarks/__init__.py b/third_party/python/jsonschema/jsonschema/benchmarks/__init__.py
new file mode 100644
index 0000000000..e3dcc68993
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/benchmarks/__init__.py
@@ -0,0 +1,5 @@
+"""
+Benchmarks for validation.
+
+This package is *not* public API.
+"""
diff --git a/third_party/python/jsonschema/jsonschema/benchmarks/issue232.py b/third_party/python/jsonschema/jsonschema/benchmarks/issue232.py
new file mode 100644
index 0000000000..bf357e9116
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/benchmarks/issue232.py
@@ -0,0 +1,25 @@
+"""
+A performance benchmark using the example from issue #232.
+
+See https://github.com/python-jsonschema/jsonschema/pull/232.
+"""
+from pathlib import Path
+
+from pyperf import Runner
+from pyrsistent import m
+
+from jsonschema.tests._suite import Version
+import jsonschema
+
+issue232 = Version(
+ path=Path(__file__).parent / "issue232",
+ remotes=m(),
+ name="issue232",
+)
+
+
+if __name__ == "__main__":
+ issue232.benchmark(
+ runner=Runner(),
+ Validator=jsonschema.Draft4Validator,
+ )
diff --git a/third_party/python/jsonschema/jsonschema/benchmarks/issue232/issue.json b/third_party/python/jsonschema/jsonschema/benchmarks/issue232/issue.json
new file mode 100644
index 0000000000..804c340845
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/benchmarks/issue232/issue.json
@@ -0,0 +1,2653 @@
+[
+ {
+ "description": "Petstore",
+ "schema": {
+ "title": "A JSON Schema for Swagger 2.0 API.",
+ "id": "http://swagger.io/v2/schema.json#",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "required": [
+ "swagger",
+ "info",
+ "paths"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "swagger": {
+ "type": "string",
+ "enum": [
+ "2.0"
+ ],
+ "description": "The Swagger version of this document."
+ },
+ "info": {
+ "$ref": "#/definitions/info"
+ },
+ "host": {
+ "type": "string",
+ "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$",
+ "description": "The host (name or ip) of the API. Example: 'swagger.io'"
+ },
+ "basePath": {
+ "type": "string",
+ "pattern": "^/",
+ "description": "The base path to the API. Example: '/api'."
+ },
+ "schemes": {
+ "$ref": "#/definitions/schemesList"
+ },
+ "consumes": {
+ "description": "A list of MIME types accepted by the API.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/mediaTypeList"
+ }
+ ]
+ },
+ "produces": {
+ "description": "A list of MIME types the API can produce.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/mediaTypeList"
+ }
+ ]
+ },
+ "paths": {
+ "$ref": "#/definitions/paths"
+ },
+ "definitions": {
+ "$ref": "#/definitions/definitions"
+ },
+ "parameters": {
+ "$ref": "#/definitions/parameterDefinitions"
+ },
+ "responses": {
+ "$ref": "#/definitions/responseDefinitions"
+ },
+ "security": {
+ "$ref": "#/definitions/security"
+ },
+ "securityDefinitions": {
+ "$ref": "#/definitions/securityDefinitions"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/tag"
+ },
+ "uniqueItems": true
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/externalDocs"
+ }
+ },
+ "definitions": {
+ "info": {
+ "type": "object",
+ "description": "General information about the API.",
+ "required": [
+ "version",
+ "title"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "title": {
+ "type": "string",
+ "description": "A unique and precise title of the API."
+ },
+ "version": {
+ "type": "string",
+ "description": "A semantic version number of the API."
+ },
+ "description": {
+ "type": "string",
+ "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed."
+ },
+ "termsOfService": {
+ "type": "string",
+ "description": "The terms of service for the API."
+ },
+ "contact": {
+ "$ref": "#/definitions/contact"
+ },
+ "license": {
+ "$ref": "#/definitions/license"
+ }
+ }
+ },
+ "contact": {
+ "type": "object",
+ "description": "Contact information for the owners of the API.",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The identifying name of the contact person/organization."
+ },
+ "url": {
+ "type": "string",
+ "description": "The URL pointing to the contact information.",
+ "format": "uri"
+ },
+ "email": {
+ "type": "string",
+ "description": "The email address of the contact person/organization.",
+ "format": "email"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "license": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the license type. It's encouraged to use an OSI compatible license."
+ },
+ "url": {
+ "type": "string",
+ "description": "The URL pointing to the license.",
+ "format": "uri"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "paths": {
+ "type": "object",
+ "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.",
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ },
+ "^/": {
+ "$ref": "#/definitions/pathItem"
+ }
+ },
+ "additionalProperties": false
+ },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/schema"
+ },
+ "description": "One or more JSON objects describing the schemas being consumed and produced by the API."
+ },
+ "parameterDefinitions": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/parameter"
+ },
+ "description": "One or more JSON representations for parameters"
+ },
+ "responseDefinitions": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/response"
+ },
+ "description": "One or more JSON representations for parameters"
+ },
+ "externalDocs": {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "information about external documentation",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "examples": {
+ "type": "object",
+ "additionalProperties": true
+ },
+ "mimeType": {
+ "type": "string",
+ "description": "The MIME type of the HTTP message."
+ },
+ "operation": {
+ "type": "object",
+ "required": [
+ "responses"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ },
+ "summary": {
+ "type": "string",
+ "description": "A brief summary of the operation."
+ },
+ "description": {
+ "type": "string",
+ "description": "A longer description of the operation, GitHub Flavored Markdown is allowed."
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/externalDocs"
+ },
+ "operationId": {
+ "type": "string",
+ "description": "A unique identifier of the operation."
+ },
+ "produces": {
+ "description": "A list of MIME types the API can produce.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/mediaTypeList"
+ }
+ ]
+ },
+ "consumes": {
+ "description": "A list of MIME types the API can consume.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/mediaTypeList"
+ }
+ ]
+ },
+ "parameters": {
+ "$ref": "#/definitions/parametersList"
+ },
+ "responses": {
+ "$ref": "#/definitions/responses"
+ },
+ "schemes": {
+ "$ref": "#/definitions/schemesList"
+ },
+ "deprecated": {
+ "type": "boolean",
+ "default": false
+ },
+ "security": {
+ "$ref": "#/definitions/security"
+ }
+ }
+ },
+ "pathItem": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "$ref": {
+ "type": "string"
+ },
+ "get": {
+ "$ref": "#/definitions/operation"
+ },
+ "put": {
+ "$ref": "#/definitions/operation"
+ },
+ "post": {
+ "$ref": "#/definitions/operation"
+ },
+ "delete": {
+ "$ref": "#/definitions/operation"
+ },
+ "options": {
+ "$ref": "#/definitions/operation"
+ },
+ "head": {
+ "$ref": "#/definitions/operation"
+ },
+ "patch": {
+ "$ref": "#/definitions/operation"
+ },
+ "parameters": {
+ "$ref": "#/definitions/parametersList"
+ }
+ }
+ },
+ "responses": {
+ "type": "object",
+ "description": "Response objects names can either be any valid HTTP status code or 'default'.",
+ "minProperties": 1,
+ "additionalProperties": false,
+ "patternProperties": {
+ "^([0-9]{3})$|^(default)$": {
+ "$ref": "#/definitions/responseValue"
+ },
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "not": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ }
+ },
+ "responseValue": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/response"
+ },
+ {
+ "$ref": "#/definitions/jsonReference"
+ }
+ ]
+ },
+ "response": {
+ "type": "object",
+ "required": [
+ "description"
+ ],
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/schema"
+ },
+ {
+ "$ref": "#/definitions/fileSchema"
+ }
+ ]
+ },
+ "headers": {
+ "$ref": "#/definitions/headers"
+ },
+ "examples": {
+ "$ref": "#/definitions/examples"
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/header"
+ }
+ },
+ "header": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "integer",
+ "boolean",
+ "array"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormat"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "vendorExtension": {
+ "description": "Any property starting with x- is valid.",
+ "additionalProperties": true,
+ "additionalItems": true
+ },
+ "bodyParameter": {
+ "type": "object",
+ "required": [
+ "name",
+ "in",
+ "schema"
+ ],
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "description": {
+ "type": "string",
+ "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the parameter."
+ },
+ "in": {
+ "type": "string",
+ "description": "Determines the location of the parameter.",
+ "enum": [
+ "body"
+ ]
+ },
+ "required": {
+ "type": "boolean",
+ "description": "Determines whether or not this parameter is required or optional.",
+ "default": false
+ },
+ "schema": {
+ "$ref": "#/definitions/schema"
+ }
+ },
+ "additionalProperties": false
+ },
+ "headerParameterSubSchema": {
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "required": {
+ "type": "boolean",
+ "description": "Determines whether or not this parameter is required or optional.",
+ "default": false
+ },
+ "in": {
+ "type": "string",
+ "description": "Determines the location of the parameter.",
+ "enum": [
+ "header"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the parameter."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "boolean",
+ "integer",
+ "array"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormat"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ }
+ }
+ },
+ "queryParameterSubSchema": {
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "required": {
+ "type": "boolean",
+ "description": "Determines whether or not this parameter is required or optional.",
+ "default": false
+ },
+ "in": {
+ "type": "string",
+ "description": "Determines the location of the parameter.",
+ "enum": [
+ "query"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the parameter."
+ },
+ "allowEmptyValue": {
+ "type": "boolean",
+ "default": false,
+ "description": "allows sending a parameter by name only or with an empty value."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "boolean",
+ "integer",
+ "array"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormatWithMulti"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ }
+ }
+ },
+ "formDataParameterSubSchema": {
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "required": {
+ "type": "boolean",
+ "description": "Determines whether or not this parameter is required or optional.",
+ "default": false
+ },
+ "in": {
+ "type": "string",
+ "description": "Determines the location of the parameter.",
+ "enum": [
+ "formData"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the parameter."
+ },
+ "allowEmptyValue": {
+ "type": "boolean",
+ "default": false,
+ "description": "allows sending a parameter by name only or with an empty value."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "boolean",
+ "integer",
+ "array",
+ "file"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormatWithMulti"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ }
+ }
+ },
+ "pathParameterSubSchema": {
+ "additionalProperties": false,
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "required": [
+ "required"
+ ],
+ "properties": {
+ "required": {
+ "type": "boolean",
+ "enum": [
+ true
+ ],
+ "description": "Determines whether or not this parameter is required or optional."
+ },
+ "in": {
+ "type": "string",
+ "description": "Determines the location of the parameter.",
+ "enum": [
+ "path"
+ ]
+ },
+ "description": {
+ "type": "string",
+ "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the parameter."
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "boolean",
+ "integer",
+ "array"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormat"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ }
+ }
+ },
+ "nonBodyParameter": {
+ "type": "object",
+ "required": [
+ "name",
+ "in",
+ "type"
+ ],
+ "oneOf": [
+ {
+ "$ref": "#/definitions/headerParameterSubSchema"
+ },
+ {
+ "$ref": "#/definitions/formDataParameterSubSchema"
+ },
+ {
+ "$ref": "#/definitions/queryParameterSubSchema"
+ },
+ {
+ "$ref": "#/definitions/pathParameterSubSchema"
+ }
+ ]
+ },
+ "parameter": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/bodyParameter"
+ },
+ {
+ "$ref": "#/definitions/nonBodyParameter"
+ }
+ ]
+ },
+ "schema": {
+ "type": "object",
+ "description": "A deterministic version of a JSON Schema object.",
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "properties": {
+ "$ref": {
+ "type": "string"
+ },
+ "format": {
+ "type": "string"
+ },
+ "title": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/title"
+ },
+ "description": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/description"
+ },
+ "default": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/default"
+ },
+ "multipleOf": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf"
+ },
+ "maximum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
+ },
+ "minLength": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
+ },
+ "pattern": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern"
+ },
+ "maxItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
+ },
+ "minItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
+ },
+ "uniqueItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems"
+ },
+ "maxProperties": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
+ },
+ "minProperties": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
+ },
+ "required": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray"
+ },
+ "enum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/enum"
+ },
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/schema"
+ },
+ {
+ "type": "boolean"
+ }
+ ],
+ "default": {}
+ },
+ "type": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/type"
+ },
+ "items": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/schema"
+ },
+ {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/definitions/schema"
+ }
+ }
+ ],
+ "default": {}
+ },
+ "allOf": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "$ref": "#/definitions/schema"
+ }
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/schema"
+ },
+ "default": {}
+ },
+ "discriminator": {
+ "type": "string"
+ },
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "xml": {
+ "$ref": "#/definitions/xml"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/externalDocs"
+ },
+ "example": {}
+ },
+ "additionalProperties": false
+ },
+ "fileSchema": {
+ "type": "object",
+ "description": "A deterministic version of a JSON Schema object.",
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "properties": {
+ "format": {
+ "type": "string"
+ },
+ "title": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/title"
+ },
+ "description": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/description"
+ },
+ "default": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/default"
+ },
+ "required": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "file"
+ ]
+ },
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/externalDocs"
+ },
+ "example": {}
+ },
+ "additionalProperties": false
+ },
+ "primitivesItems": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "string",
+ "number",
+ "integer",
+ "boolean",
+ "array"
+ ]
+ },
+ "format": {
+ "type": "string"
+ },
+ "items": {
+ "$ref": "#/definitions/primitivesItems"
+ },
+ "collectionFormat": {
+ "$ref": "#/definitions/collectionFormat"
+ },
+ "default": {
+ "$ref": "#/definitions/default"
+ },
+ "maximum": {
+ "$ref": "#/definitions/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "#/definitions/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "#/definitions/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "#/definitions/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "#/definitions/maxLength"
+ },
+ "minLength": {
+ "$ref": "#/definitions/minLength"
+ },
+ "pattern": {
+ "$ref": "#/definitions/pattern"
+ },
+ "maxItems": {
+ "$ref": "#/definitions/maxItems"
+ },
+ "minItems": {
+ "$ref": "#/definitions/minItems"
+ },
+ "uniqueItems": {
+ "$ref": "#/definitions/uniqueItems"
+ },
+ "enum": {
+ "$ref": "#/definitions/enum"
+ },
+ "multipleOf": {
+ "$ref": "#/definitions/multipleOf"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "security": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/securityRequirement"
+ },
+ "uniqueItems": true
+ },
+ "securityRequirement": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": true
+ }
+ },
+ "xml": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "prefix": {
+ "type": "string"
+ },
+ "attribute": {
+ "type": "boolean",
+ "default": false
+ },
+ "wrapped": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "tag": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "externalDocs": {
+ "$ref": "#/definitions/externalDocs"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "securityDefinitions": {
+ "type": "object",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/basicAuthenticationSecurity"
+ },
+ {
+ "$ref": "#/definitions/apiKeySecurity"
+ },
+ {
+ "$ref": "#/definitions/oauth2ImplicitSecurity"
+ },
+ {
+ "$ref": "#/definitions/oauth2PasswordSecurity"
+ },
+ {
+ "$ref": "#/definitions/oauth2ApplicationSecurity"
+ },
+ {
+ "$ref": "#/definitions/oauth2AccessCodeSecurity"
+ }
+ ]
+ }
+ },
+ "basicAuthenticationSecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "basic"
+ ]
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "apiKeySecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "name",
+ "in"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "apiKey"
+ ]
+ },
+ "name": {
+ "type": "string"
+ },
+ "in": {
+ "type": "string",
+ "enum": [
+ "header",
+ "query"
+ ]
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "oauth2ImplicitSecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "flow",
+ "authorizationUrl"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "oauth2"
+ ]
+ },
+ "flow": {
+ "type": "string",
+ "enum": [
+ "implicit"
+ ]
+ },
+ "scopes": {
+ "$ref": "#/definitions/oauth2Scopes"
+ },
+ "authorizationUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "oauth2PasswordSecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "flow",
+ "tokenUrl"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "oauth2"
+ ]
+ },
+ "flow": {
+ "type": "string",
+ "enum": [
+ "password"
+ ]
+ },
+ "scopes": {
+ "$ref": "#/definitions/oauth2Scopes"
+ },
+ "tokenUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "oauth2ApplicationSecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "flow",
+ "tokenUrl"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "oauth2"
+ ]
+ },
+ "flow": {
+ "type": "string",
+ "enum": [
+ "application"
+ ]
+ },
+ "scopes": {
+ "$ref": "#/definitions/oauth2Scopes"
+ },
+ "tokenUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "oauth2AccessCodeSecurity": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "flow",
+ "authorizationUrl",
+ "tokenUrl"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "oauth2"
+ ]
+ },
+ "flow": {
+ "type": "string",
+ "enum": [
+ "accessCode"
+ ]
+ },
+ "scopes": {
+ "$ref": "#/definitions/oauth2Scopes"
+ },
+ "authorizationUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "tokenUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "patternProperties": {
+ "^x-": {
+ "$ref": "#/definitions/vendorExtension"
+ }
+ }
+ },
+ "oauth2Scopes": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "mediaTypeList": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/mimeType"
+ },
+ "uniqueItems": true
+ },
+ "parametersList": {
+ "type": "array",
+ "description": "The parameters needed to send a valid API call.",
+ "additionalItems": false,
+ "items": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/parameter"
+ },
+ {
+ "$ref": "#/definitions/jsonReference"
+ }
+ ]
+ },
+ "uniqueItems": true
+ },
+ "schemesList": {
+ "type": "array",
+ "description": "The transfer protocol of the API.",
+ "items": {
+ "type": "string",
+ "enum": [
+ "http",
+ "https",
+ "ws",
+ "wss"
+ ]
+ },
+ "uniqueItems": true
+ },
+ "collectionFormat": {
+ "type": "string",
+ "enum": [
+ "csv",
+ "ssv",
+ "tsv",
+ "pipes"
+ ],
+ "default": "csv"
+ },
+ "collectionFormatWithMulti": {
+ "type": "string",
+ "enum": [
+ "csv",
+ "ssv",
+ "tsv",
+ "pipes",
+ "multi"
+ ],
+ "default": "csv"
+ },
+ "title": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/title"
+ },
+ "description": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/description"
+ },
+ "default": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/default"
+ },
+ "multipleOf": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf"
+ },
+ "maximum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum"
+ },
+ "exclusiveMaximum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum"
+ },
+ "minimum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum"
+ },
+ "exclusiveMinimum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum"
+ },
+ "maxLength": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
+ },
+ "minLength": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
+ },
+ "pattern": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern"
+ },
+ "maxItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
+ },
+ "minItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
+ },
+ "uniqueItems": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems"
+ },
+ "enum": {
+ "$ref": "http://json-schema.org/draft-04/schema#/properties/enum"
+ },
+ "jsonReference": {
+ "type": "object",
+ "required": [
+ "$ref"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "$ref": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "Example petsore",
+ "data": {
+ "swagger": "2.0",
+ "info": {
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "petstore.swagger.io",
+ "basePath": "/v2",
+ "tags": [
+ {
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ },
+ {
+ "name": "store",
+ "description": "Access to Petstore orders"
+ },
+ {
+ "name": "user",
+ "description": "Operations about user",
+ "externalDocs": {
+ "description": "Find out more about our store",
+ "url": "http://swagger.io"
+ }
+ }
+ ],
+ "schemes": [
+ "http"
+ ],
+ "paths": {
+ "/pet": {
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Add a new pet to the store",
+ "description": "",
+ "operationId": "addPet",
+ "consumes": [
+ "application/json",
+ "application/xml"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Update an existing pet",
+ "description": "",
+ "operationId": "updatePet",
+ "consumes": [
+ "application/json",
+ "application/xml"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ },
+ "405": {
+ "description": "Validation exception"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/findByStatus": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ],
+ "default": "available"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/findByTags": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Finds Pets by tags",
+ "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+ "operationId": "findPetsByTags",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "tags",
+ "in": "query",
+ "description": "Tags to filter by",
+ "required": true,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid tag value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "deprecated": true
+ }
+ },
+ "/pet/{petId}": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Find pet by ID",
+ "description": "Returns a single pet",
+ "operationId": "getPetById",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to return",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ]
+ },
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Updates a pet in the store with form data",
+ "description": "",
+ "operationId": "updatePetWithForm",
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet that needs to be updated",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "name": "name",
+ "in": "formData",
+ "description": "Updated name of the pet",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "status",
+ "in": "formData",
+ "description": "Updated status of the pet",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Deletes a pet",
+ "description": "",
+ "operationId": "deletePet",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "api_key",
+ "in": "header",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "Pet id to delete",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/{petId}/uploadImage": {
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "uploads an image",
+ "description": "",
+ "operationId": "uploadFile",
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to update",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "name": "additionalMetadata",
+ "in": "formData",
+ "description": "Additional data to pass to server",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "file",
+ "in": "formData",
+ "description": "file to upload",
+ "required": false,
+ "type": "file"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/ApiResponse"
+ }
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/store/inventory": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Returns pet inventories by status",
+ "description": "Returns a map of status codes to quantities",
+ "operationId": "getInventory",
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ]
+ }
+ },
+ "/store/order": {
+ "post": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Place an order for a pet",
+ "description": "",
+ "operationId": "placeOrder",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "order placed for purchasing the pet",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ },
+ "400": {
+ "description": "Invalid Order"
+ }
+ }
+ }
+ },
+ "/store/order/{orderId}": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Find purchase order by ID",
+ "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions",
+ "operationId": "getOrderById",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of pet that needs to be fetched",
+ "required": true,
+ "type": "integer",
+ "maximum": 10.0,
+ "minimum": 1.0,
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Delete purchase order by ID",
+ "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors",
+ "operationId": "deleteOrder",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of the order that needs to be deleted",
+ "required": true,
+ "type": "integer",
+ "minimum": 1.0,
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ }
+ },
+ "/user": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Create user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "createUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Created user object",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/createWithArray": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Creates list of users with given input array",
+ "description": "",
+ "operationId": "createUsersWithArrayInput",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "List of user object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/createWithList": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Creates list of users with given input array",
+ "description": "",
+ "operationId": "createUsersWithListInput",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "List of user object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/login": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Logs user into the system",
+ "description": "",
+ "operationId": "loginUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for login",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for login in clear text",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "string"
+ },
+ "headers": {
+ "X-Rate-Limit": {
+ "type": "integer",
+ "format": "int32",
+ "description": "calls per hour allowed by the user"
+ },
+ "X-Expires-After": {
+ "type": "string",
+ "format": "date-time",
+ "description": "date in UTC when token expires"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid username/password supplied"
+ }
+ }
+ }
+ },
+ "/user/logout": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Logs out current logged in user session",
+ "description": "",
+ "operationId": "logoutUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/{username}": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Get user by user name",
+ "description": "",
+ "operationId": "getUserByName",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be fetched. Use user1 for testing. ",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ },
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ },
+ "put": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Updated user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "updateUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "name that need to be updated",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Updated user object",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid user supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Delete user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "deleteUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be deleted",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ }
+ }
+ },
+ "securityDefinitions": {
+ "petstore_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+ "flow": "implicit",
+ "scopes": {
+ "write:pets": "modify pets in your account",
+ "read:pets": "read your pets"
+ }
+ },
+ "api_key": {
+ "type": "apiKey",
+ "name": "api_key",
+ "in": "header"
+ }
+ },
+ "definitions": {
+ "Order": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "petId": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "quantity": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "shipDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "status": {
+ "type": "string",
+ "description": "Order Status",
+ "enum": [
+ "placed",
+ "approved",
+ "delivered"
+ ]
+ },
+ "complete": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "xml": {
+ "name": "Order"
+ }
+ },
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Category"
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "username": {
+ "type": "string"
+ },
+ "firstName": {
+ "type": "string"
+ },
+ "lastName": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "phone": {
+ "type": "string"
+ },
+ "userStatus": {
+ "type": "integer",
+ "format": "int32",
+ "description": "User Status"
+ }
+ },
+ "xml": {
+ "name": "User"
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Tag"
+ }
+ },
+ "Pet": {
+ "type": "object",
+ "required": [
+ "name",
+ "photoUrls"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "category": {
+ "$ref": "#/definitions/Category"
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "photoUrls": {
+ "type": "array",
+ "xml": {
+ "name": "photoUrl",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string"
+ }
+ },
+ "tags": {
+ "type": "array",
+ "xml": {
+ "name": "tag",
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/definitions/Tag"
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "pet status in the store",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ]
+ }
+ },
+ "xml": {
+ "name": "Pet"
+ }
+ },
+ "ApiResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "type": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ }
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/third_party/python/jsonschema/jsonschema/benchmarks/json_schema_test_suite.py b/third_party/python/jsonschema/jsonschema/benchmarks/json_schema_test_suite.py
new file mode 100644
index 0000000000..905fb6a3b8
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/benchmarks/json_schema_test_suite.py
@@ -0,0 +1,12 @@
+"""
+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 pyperf import Runner
+
+from jsonschema.tests._suite import Suite
+
+if __name__ == "__main__":
+ Suite().benchmark(runner=Runner())
diff --git a/third_party/python/jsonschema/jsonschema/cli.py b/third_party/python/jsonschema/jsonschema/cli.py
new file mode 100644
index 0000000000..f93b5c5a08
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/cli.py
@@ -0,0 +1,299 @@
+"""
+The ``jsonschema`` command line.
+"""
+
+from json import JSONDecodeError
+from textwrap import dedent
+import argparse
+import json
+import sys
+import traceback
+import warnings
+
+try:
+ from importlib import metadata
+except ImportError:
+ import importlib_metadata as metadata # type: ignore
+
+try:
+ from pkgutil import resolve_name
+except ImportError:
+ from pkgutil_resolve_name import resolve_name # type: ignore
+
+import attr
+
+from jsonschema.exceptions import SchemaError
+from jsonschema.validators import RefResolver, validator_for
+
+warnings.warn(
+ (
+ "The jsonschema CLI is deprecated and will be removed in a future "
+ "version. Please use check-jsonschema instead, which can be installed "
+ "from https://pypi.org/project/check-jsonschema/"
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+)
+
+
+class _CannotLoadFile(Exception):
+ pass
+
+
+@attr.s
+class _Outputter:
+
+ _formatter = attr.ib()
+ _stdout = attr.ib()
+ _stderr = attr.ib()
+
+ @classmethod
+ def from_arguments(cls, arguments, stdout, stderr):
+ if arguments["output"] == "plain":
+ formatter = _PlainFormatter(arguments["error_format"])
+ elif arguments["output"] == "pretty":
+ formatter = _PrettyFormatter()
+ return cls(formatter=formatter, stdout=stdout, stderr=stderr)
+
+ def load(self, path):
+ try:
+ file = open(path)
+ except FileNotFoundError:
+ self.filenotfound_error(path=path, exc_info=sys.exc_info())
+ raise _CannotLoadFile()
+
+ with file:
+ try:
+ return json.load(file)
+ except JSONDecodeError:
+ self.parsing_error(path=path, exc_info=sys.exc_info())
+ raise _CannotLoadFile()
+
+ def filenotfound_error(self, **kwargs):
+ self._stderr.write(self._formatter.filenotfound_error(**kwargs))
+
+ def parsing_error(self, **kwargs):
+ self._stderr.write(self._formatter.parsing_error(**kwargs))
+
+ def validation_error(self, **kwargs):
+ self._stderr.write(self._formatter.validation_error(**kwargs))
+
+ def validation_success(self, **kwargs):
+ self._stdout.write(self._formatter.validation_success(**kwargs))
+
+
+@attr.s
+class _PrettyFormatter:
+
+ _ERROR_MSG = dedent(
+ """\
+ ===[{type}]===({path})===
+
+ {body}
+ -----------------------------
+ """,
+ )
+ _SUCCESS_MSG = "===[SUCCESS]===({path})===\n"
+
+ def filenotfound_error(self, path, exc_info):
+ return self._ERROR_MSG.format(
+ path=path,
+ type="FileNotFoundError",
+ body="{!r} does not exist.".format(path),
+ )
+
+ def parsing_error(self, path, exc_info):
+ exc_type, exc_value, exc_traceback = exc_info
+ exc_lines = "".join(
+ traceback.format_exception(exc_type, exc_value, exc_traceback),
+ )
+ return self._ERROR_MSG.format(
+ path=path,
+ type=exc_type.__name__,
+ body=exc_lines,
+ )
+
+ def validation_error(self, instance_path, error):
+ return self._ERROR_MSG.format(
+ path=instance_path,
+ type=error.__class__.__name__,
+ body=error,
+ )
+
+ def validation_success(self, instance_path):
+ return self._SUCCESS_MSG.format(path=instance_path)
+
+
+@attr.s
+class _PlainFormatter:
+
+ _error_format = attr.ib()
+
+ def filenotfound_error(self, path, exc_info):
+ return "{!r} does not exist.\n".format(path)
+
+ def parsing_error(self, path, exc_info):
+ return "Failed to parse {}: {}\n".format(
+ "<stdin>" if path == "<stdin>" else repr(path),
+ exc_info[1],
+ )
+
+ def validation_error(self, instance_path, error):
+ return self._error_format.format(file_name=instance_path, error=error)
+
+ def validation_success(self, instance_path):
+ return ""
+
+
+def _resolve_name_with_default(name):
+ if "." not in name:
+ name = "jsonschema." + name
+ return resolve_name(name)
+
+
+parser = argparse.ArgumentParser(
+ description="JSON Schema Validation CLI",
+)
+parser.add_argument(
+ "-i", "--instance",
+ action="append",
+ dest="instances",
+ help="""
+ a path to a JSON instance (i.e. filename.json) to validate (may
+ be specified multiple times). If no instances are provided via this
+ option, one will be expected on standard input.
+ """,
+)
+parser.add_argument(
+ "-F", "--error-format",
+ help="""
+ the format to use for each validation error message, specified
+ in a form suitable for str.format. This string will be passed
+ one formatted object named 'error' for each ValidationError.
+ Only provide this option when using --output=plain, which is the
+ default. If this argument is unprovided and --output=plain is
+ used, a simple default representation will be used.
+ """,
+)
+parser.add_argument(
+ "-o", "--output",
+ choices=["plain", "pretty"],
+ default="plain",
+ help="""
+ an output format to use. 'plain' (default) will produce minimal
+ text with one line for each error, while 'pretty' will produce
+ more detailed human-readable output on multiple lines.
+ """,
+)
+parser.add_argument(
+ "-V", "--validator",
+ type=_resolve_name_with_default,
+ help="""
+ the fully qualified object name of a validator to use, or, for
+ validators that are registered with jsonschema, simply the name
+ of the class.
+ """,
+)
+parser.add_argument(
+ "--base-uri",
+ help="""
+ a base URI to assign to the provided schema, even if it does not
+ declare one (via e.g. $id). This option can be used if you wish to
+ resolve relative references to a particular URI (or local path)
+ """,
+)
+parser.add_argument(
+ "--version",
+ action="version",
+ version=metadata.version("jsonschema"),
+)
+parser.add_argument(
+ "schema",
+ help="the path to a JSON Schema to validate with (i.e. schema.json)",
+)
+
+
+def parse_args(args):
+ arguments = vars(parser.parse_args(args=args or ["--help"]))
+ if arguments["output"] != "plain" and arguments["error_format"]:
+ raise parser.error(
+ "--error-format can only be used with --output plain",
+ )
+ if arguments["output"] == "plain" and arguments["error_format"] is None:
+ arguments["error_format"] = "{error.instance}: {error.message}\n"
+ return arguments
+
+
+def _validate_instance(instance_path, instance, validator, outputter):
+ invalid = False
+ for error in validator.iter_errors(instance):
+ invalid = True
+ outputter.validation_error(instance_path=instance_path, error=error)
+
+ if not invalid:
+ outputter.validation_success(instance_path=instance_path)
+ return invalid
+
+
+def main(args=sys.argv[1:]):
+ sys.exit(run(arguments=parse_args(args=args)))
+
+
+def run(arguments, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin):
+ outputter = _Outputter.from_arguments(
+ arguments=arguments,
+ stdout=stdout,
+ stderr=stderr,
+ )
+
+ try:
+ schema = outputter.load(arguments["schema"])
+ except _CannotLoadFile:
+ return 1
+
+ if arguments["validator"] is None:
+ arguments["validator"] = validator_for(schema)
+
+ try:
+ arguments["validator"].check_schema(schema)
+ except SchemaError as error:
+ outputter.validation_error(
+ instance_path=arguments["schema"],
+ error=error,
+ )
+ return 1
+
+ if arguments["instances"]:
+ load, instances = outputter.load, arguments["instances"]
+ else:
+ def load(_):
+ try:
+ return json.load(stdin)
+ except JSONDecodeError:
+ outputter.parsing_error(
+ path="<stdin>", exc_info=sys.exc_info(),
+ )
+ raise _CannotLoadFile()
+ instances = ["<stdin>"]
+
+ resolver = RefResolver(
+ base_uri=arguments["base_uri"],
+ referrer=schema,
+ ) if arguments["base_uri"] is not None else None
+
+ validator = arguments["validator"](schema, resolver=resolver)
+ exit_code = 0
+ for each in instances:
+ try:
+ instance = load(each)
+ except _CannotLoadFile:
+ exit_code = 1
+ else:
+ exit_code |= _validate_instance(
+ instance_path=each,
+ instance=instance,
+ validator=validator,
+ outputter=outputter,
+ )
+
+ return exit_code
diff --git a/third_party/python/jsonschema/jsonschema/exceptions.py b/third_party/python/jsonschema/jsonschema/exceptions.py
new file mode 100644
index 0000000000..87db3df3a6
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/exceptions.py
@@ -0,0 +1,396 @@
+"""
+Validation errors, and some surrounding helpers.
+"""
+from __future__ import annotations
+
+from collections import defaultdict, deque
+from pprint import pformat
+from textwrap import dedent, indent
+import heapq
+import itertools
+
+import attr
+
+from jsonschema import _utils
+
+WEAK_MATCHES: frozenset[str] = frozenset(["anyOf", "oneOf"])
+STRONG_MATCHES: frozenset[str] = frozenset()
+
+_unset = _utils.Unset()
+
+
+class _Error(Exception):
+ def __init__(
+ self,
+ message,
+ validator=_unset,
+ path=(),
+ cause=None,
+ context=(),
+ validator_value=_unset,
+ instance=_unset,
+ schema=_unset,
+ schema_path=(),
+ parent=None,
+ type_checker=_unset,
+ ):
+ super(_Error, self).__init__(
+ message,
+ validator,
+ path,
+ cause,
+ context,
+ validator_value,
+ instance,
+ schema,
+ schema_path,
+ parent,
+ )
+ self.message = message
+ self.path = self.relative_path = deque(path)
+ self.schema_path = self.relative_schema_path = deque(schema_path)
+ self.context = list(context)
+ self.cause = self.__cause__ = cause
+ self.validator = validator
+ self.validator_value = validator_value
+ self.instance = instance
+ self.schema = schema
+ self.parent = parent
+ self._type_checker = type_checker
+
+ for error in context:
+ error.parent = self
+
+ def __repr__(self):
+ return f"<{self.__class__.__name__}: {self.message!r}>"
+
+ def __str__(self):
+ essential_for_verbose = (
+ self.validator, self.validator_value, self.instance, self.schema,
+ )
+ if any(m is _unset for m in essential_for_verbose):
+ return self.message
+
+ schema_path = _utils.format_as_index(
+ container=self._word_for_schema_in_error_message,
+ indices=list(self.relative_schema_path)[:-1],
+ )
+ instance_path = _utils.format_as_index(
+ container=self._word_for_instance_in_error_message,
+ indices=self.relative_path,
+ )
+ prefix = 16 * " "
+
+ return dedent(
+ f"""\
+ {self.message}
+
+ Failed validating {self.validator!r} in {schema_path}:
+ {indent(pformat(self.schema, width=72), prefix).lstrip()}
+
+ On {instance_path}:
+ {indent(pformat(self.instance, width=72), prefix).lstrip()}
+ """.rstrip(),
+ )
+
+ @classmethod
+ def create_from(cls, other):
+ return cls(**other._contents())
+
+ @property
+ def absolute_path(self):
+ parent = self.parent
+ if parent is None:
+ return self.relative_path
+
+ path = deque(self.relative_path)
+ path.extendleft(reversed(parent.absolute_path))
+ return path
+
+ @property
+ def absolute_schema_path(self):
+ parent = self.parent
+ if parent is None:
+ return self.relative_schema_path
+
+ path = deque(self.relative_schema_path)
+ path.extendleft(reversed(parent.absolute_schema_path))
+ return path
+
+ @property
+ def json_path(self):
+ path = "$"
+ for elem in self.absolute_path:
+ if isinstance(elem, int):
+ path += "[" + str(elem) + "]"
+ else:
+ path += "." + elem
+ return path
+
+ def _set(self, type_checker=None, **kwargs):
+ if type_checker is not None and self._type_checker is _unset:
+ self._type_checker = type_checker
+
+ for k, v in kwargs.items():
+ if getattr(self, k) is _unset:
+ setattr(self, k, v)
+
+ def _contents(self):
+ attrs = (
+ "message", "cause", "context", "validator", "validator_value",
+ "path", "schema_path", "instance", "schema", "parent",
+ )
+ return dict((attr, getattr(self, attr)) for attr in attrs)
+
+ def _matches_type(self):
+ try:
+ expected = self.schema["type"]
+ except (KeyError, TypeError):
+ return False
+
+ if isinstance(expected, str):
+ return self._type_checker.is_type(self.instance, expected)
+
+ return any(
+ self._type_checker.is_type(self.instance, expected_type)
+ for expected_type in expected
+ )
+
+
+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()
+
+ def __str__(self):
+ return str(self._cause)
+
+
+class UndefinedTypeCheck(Exception):
+ """
+ A type checker was asked to check a type it did not have registered.
+ """
+
+ def __init__(self, type):
+ self.type = type
+
+ def __str__(self):
+ return f"Type {self.type!r} is unknown to this type checker"
+
+
+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
+ self.schema = schema
+
+ def __str__(self):
+ prefix = 16 * " "
+
+ return dedent(
+ f"""\
+ Unknown type {self.type!r} for validator with schema:
+ {indent(pformat(self.schema, width=72), prefix).lstrip()}
+
+ While checking instance:
+ {indent(pformat(self.instance, width=72), prefix).lstrip()}
+ """.rstrip(),
+ )
+
+
+class FormatError(Exception):
+ """
+ Validating a format failed.
+ """
+
+ def __init__(self, message, cause=None):
+ super(FormatError, self).__init__(message, cause)
+ self.message = message
+ self.cause = self.__cause__ = cause
+
+ def __str__(self):
+ return self.message
+
+
+class ErrorTree:
+ """
+ ErrorTrees make it easier to check which validations failed.
+ """
+
+ _instance = _unset
+
+ def __init__(self, errors=()):
+ self.errors = {}
+ self._contents = defaultdict(self.__class__)
+
+ for error in errors:
+ container = self
+ for element in error.path:
+ container = container[element]
+ container.errors[error.validator] = error
+
+ container._instance = error.instance
+
+ def __contains__(self, index):
+ """
+ Check whether ``instance[index]`` has any errors.
+ """
+
+ return index in self._contents
+
+ def __getitem__(self, index):
+ """
+ Retrieve the child tree one level down at the given ``index``.
+
+ If the index is not in the instance that this tree corresponds
+ to and is not known by this tree, whatever error would be raised
+ by ``instance.__getitem__`` will be propagated (usually this is
+ some subclass of `LookupError`.
+ """
+
+ if self._instance is not _unset and index not in self:
+ self._instance[index]
+ 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):
+ """
+ Return the `total_errors`.
+ """
+ return self.total_errors
+
+ def __repr__(self):
+ total = len(self)
+ errors = "error" if total == 1 else "errors"
+ return f"<{self.__class__.__name__} ({total} total {errors})>"
+
+ @property
+ def total_errors(self):
+ """
+ The total number of errors in the entire tree, including children.
+ """
+
+ child_errors = sum(len(tree) for _, tree in self._contents.items())
+ return len(self.errors) + child_errors
+
+
+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 validation keywords 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 validation keywords,
+ the other error will take priority. By default, :kw:`anyOf`
+ and :kw:`oneOf` are considered weak keywords and will be
+ superseded by other same-level validation errors.
+
+ strong (set):
+ a collection of validation keywords to consider to be
+ "strong"
+ """
+ def relevance(error):
+ validator = error.validator
+ return (
+ -len(error.path),
+ validator not in weak,
+ validator in strong,
+ not error._matches_type(),
+ )
+ return relevance
+
+
+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 :kw:`oneOf` or :kw:`anyOf`, the
+ *opposite* assumption is made -- i.e. the deepest error is picked,
+ since these keywords only need to match once, and any other errors
+ may not be relevant.
+
+ Arguments:
+ errors (collections.abc.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.abc.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:
+ return
+ best = max(itertools.chain([best], errors), key=key)
+
+ while best.context:
+ # Calculate the minimum via nsmallest, because we don't recurse if
+ # all nested errors have the same relevance (i.e. if min == max == all)
+ smallest = heapq.nsmallest(2, best.context, key=key)
+ if len(smallest) == 2 and key(smallest[0]) == key(smallest[1]):
+ return best
+ best = smallest[0]
+ return best
diff --git a/third_party/python/jsonschema/jsonschema/protocols.py b/third_party/python/jsonschema/jsonschema/protocols.py
new file mode 100644
index 0000000000..5f52166faf
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/protocols.py
@@ -0,0 +1,225 @@
+"""
+typing.Protocol classes for jsonschema interfaces.
+"""
+
+# for reference material on Protocols, see
+# https://www.python.org/dev/peps/pep-0544/
+
+from __future__ import annotations
+
+from collections.abc import Callable, Mapping
+from typing import TYPE_CHECKING, Any, ClassVar, Iterable
+import sys
+
+# doing these imports with `try ... except ImportError` doesn't pass mypy
+# checking because mypy sees `typing._SpecialForm` and
+# `typing_extensions._SpecialForm` as incompatible
+#
+# see:
+# https://mypy.readthedocs.io/en/stable/runtime_troubles.html#using-new-additions-to-the-typing-module
+# https://github.com/python/mypy/issues/4427
+if sys.version_info >= (3, 8):
+ from typing import Protocol, runtime_checkable
+else:
+ from typing_extensions import Protocol, runtime_checkable
+
+# in order for Sphinx to resolve references accurately from type annotations,
+# it needs to see names like `jsonschema.TypeChecker`
+# therefore, only import at type-checking time (to avoid circular references),
+# but use `jsonschema` for any types which will otherwise not be resolvable
+if TYPE_CHECKING:
+ import jsonschema
+ import jsonschema.validators
+
+from jsonschema.exceptions import ValidationError
+
+# For code authors working on the validator protocol, these are the three
+# use-cases which should be kept in mind:
+#
+# 1. As a protocol class, it can be used in type annotations to describe the
+# available methods and attributes of a validator
+# 2. It is the source of autodoc for the validator documentation
+# 3. It is runtime_checkable, meaning that it can be used in isinstance()
+# checks.
+#
+# Since protocols are not base classes, isinstance() checking is limited in
+# its capabilities. See docs on runtime_checkable for detail
+
+
+@runtime_checkable
+class Validator(Protocol):
+ """
+ The protocol to which all validator classes adhere.
+
+ Arguments:
+
+ schema:
+
+ The schema that the validator object will validate with.
+ It is assumed to be valid, and providing
+ an invalid schema can lead to undefined behavior. See
+ `Validator.check_schema` to validate a schema first.
+
+ resolver:
+
+ a resolver that will be used to resolve :kw:`$ref`
+ properties (JSON references). If unprovided, one will be created.
+
+ format_checker:
+
+ if provided, a checker which will be used to assert about
+ :kw:`format` properties present in the schema. If unprovided,
+ *no* format validation is done, and the presence of format
+ within schemas is strictly informational. Certain formats
+ require additional packages to be installed in order to assert
+ against instances. Ensure you've installed `jsonschema` with
+ its `extra (optional) dependencies <index:extras>` when
+ invoking ``pip``.
+
+ .. deprecated:: v4.12.0
+
+ Subclassing validator classes now explicitly warns this is not part of
+ their public API.
+ """
+
+ #: An object representing the validator's meta schema (the schema that
+ #: describes valid schemas in the given version).
+ META_SCHEMA: ClassVar[Mapping]
+
+ #: A mapping of validation keywords (`str`\s) to functions that
+ #: validate the keyword with that name. For more information see
+ #: `creating-validators`.
+ VALIDATORS: ClassVar[Mapping]
+
+ #: A `jsonschema.TypeChecker` that will be used when validating
+ #: :kw:`type` keywords in JSON schemas.
+ TYPE_CHECKER: ClassVar[jsonschema.TypeChecker]
+
+ #: A `jsonschema.FormatChecker` that will be used when validating
+ #: :kw:`format` keywords in JSON schemas.
+ FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker]
+
+ #: A function which given a schema returns its ID.
+ ID_OF: Callable[[Any], str | None]
+
+ #: The schema that will be used to validate instances
+ schema: Mapping | bool
+
+ def __init__(
+ self,
+ schema: Mapping | bool,
+ resolver: jsonschema.validators.RefResolver | None = None,
+ format_checker: jsonschema.FormatChecker | None = None,
+ ) -> None:
+ ...
+
+ @classmethod
+ def check_schema(cls, schema: Mapping | bool) -> None:
+ """
+ Validate the given schema against the validator's `META_SCHEMA`.
+
+ Raises:
+
+ `jsonschema.exceptions.SchemaError`:
+
+ if the schema is invalid
+ """
+
+ def is_type(self, instance: Any, type: str) -> bool:
+ """
+ Check if the instance is of the given (JSON Schema) type.
+
+ Arguments:
+
+ instance:
+
+ the value to check
+
+ type:
+
+ the name of a known (JSON Schema) type
+
+ Returns:
+
+ whether the instance is of the given type
+
+ Raises:
+
+ `jsonschema.exceptions.UnknownType`:
+
+ if ``type`` is not a known type
+ """
+
+ def is_valid(self, instance: Any) -> bool:
+ """
+ Check if the instance is valid under the current `schema`.
+
+ Returns:
+
+ whether the instance is valid or not
+
+ >>> schema = {"maxItems" : 2}
+ >>> Draft202012Validator(schema).is_valid([2, 3, 4])
+ False
+ """
+
+ def iter_errors(self, instance: Any) -> Iterable[ValidationError]:
+ r"""
+ Lazily yield each of the validation errors in the given instance.
+
+ >>> schema = {
+ ... "type" : "array",
+ ... "items" : {"enum" : [1, 2, 3]},
+ ... "maxItems" : 2,
+ ... }
+ >>> v = Draft202012Validator(schema)
+ >>> for error in sorted(v.iter_errors([2, 3, 4]), key=str):
+ ... print(error.message)
+ 4 is not one of [1, 2, 3]
+ [2, 3, 4] is too long
+
+ .. deprecated:: v4.0.0
+
+ Calling this function with a second schema argument is deprecated.
+ Use `Validator.evolve` instead.
+ """
+
+ def validate(self, instance: Any) -> None:
+ """
+ Check if the instance is valid under the current `schema`.
+
+ Raises:
+
+ `jsonschema.exceptions.ValidationError`:
+
+ if the instance is invalid
+
+ >>> schema = {"maxItems" : 2}
+ >>> Draft202012Validator(schema).validate([2, 3, 4])
+ Traceback (most recent call last):
+ ...
+ ValidationError: [2, 3, 4] is too long
+ """
+
+ def evolve(self, **kwargs) -> "Validator":
+ """
+ Create a new validator like this one, but with given changes.
+
+ Preserves all other attributes, so can be used to e.g. create a
+ validator with a different schema but with the same :kw:`$ref`
+ resolution behavior.
+
+ >>> validator = Draft202012Validator({})
+ >>> validator.evolve(schema={"type": "number"})
+ Draft202012Validator(schema={'type': 'number'}, format_checker=None)
+
+ The returned object satisfies the validator protocol, but may not
+ be of the same concrete class! In particular this occurs
+ when a :kw:`$ref` occurs to a schema with a different
+ :kw:`$schema` than this one (i.e. for a different draft).
+
+ >>> validator.evolve(
+ ... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]}
+ ... )
+ Draft7Validator(schema=..., format_checker=None)
+ """
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft2019-09.json b/third_party/python/jsonschema/jsonschema/schemas/draft2019-09.json
new file mode 100644
index 0000000000..2248a0c80b
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft2019-09.json
@@ -0,0 +1,42 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/core": true,
+ "https://json-schema.org/draft/2019-09/vocab/applicator": true,
+ "https://json-schema.org/draft/2019-09/vocab/validation": true,
+ "https://json-schema.org/draft/2019-09/vocab/meta-data": true,
+ "https://json-schema.org/draft/2019-09/vocab/format": false,
+ "https://json-schema.org/draft/2019-09/vocab/content": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Core and Validation specifications meta-schema",
+ "allOf": [
+ {"$ref": "meta/core"},
+ {"$ref": "meta/applicator"},
+ {"$ref": "meta/validation"},
+ {"$ref": "meta/meta-data"},
+ {"$ref": "meta/format"},
+ {"$ref": "meta/content"}
+ ],
+ "type": ["object", "boolean"],
+ "properties": {
+ "definitions": {
+ "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.",
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" },
+ "default": {}
+ },
+ "dependencies": {
+ "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"",
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$recursiveRef": "#" },
+ { "$ref": "meta/validation#/$defs/stringArray" }
+ ]
+ }
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft2020-12.json b/third_party/python/jsonschema/jsonschema/schemas/draft2020-12.json
new file mode 100644
index 0000000000..d5e2d31c3c
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft2020-12.json
@@ -0,0 +1,58 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/core": true,
+ "https://json-schema.org/draft/2020-12/vocab/applicator": true,
+ "https://json-schema.org/draft/2020-12/vocab/unevaluated": true,
+ "https://json-schema.org/draft/2020-12/vocab/validation": true,
+ "https://json-schema.org/draft/2020-12/vocab/meta-data": true,
+ "https://json-schema.org/draft/2020-12/vocab/format-annotation": true,
+ "https://json-schema.org/draft/2020-12/vocab/content": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Core and Validation specifications meta-schema",
+ "allOf": [
+ {"$ref": "meta/core"},
+ {"$ref": "meta/applicator"},
+ {"$ref": "meta/unevaluated"},
+ {"$ref": "meta/validation"},
+ {"$ref": "meta/meta-data"},
+ {"$ref": "meta/format-annotation"},
+ {"$ref": "meta/content"}
+ ],
+ "type": ["object", "boolean"],
+ "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.",
+ "properties": {
+ "definitions": {
+ "$comment": "\"definitions\" has been replaced by \"$defs\".",
+ "type": "object",
+ "additionalProperties": { "$dynamicRef": "#meta" },
+ "deprecated": true,
+ "default": {}
+ },
+ "dependencies": {
+ "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.",
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$dynamicRef": "#meta" },
+ { "$ref": "meta/validation#/$defs/stringArray" }
+ ]
+ },
+ "deprecated": true,
+ "default": {}
+ },
+ "$recursiveAnchor": {
+ "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".",
+ "$ref": "meta/core#/$defs/anchorString",
+ "deprecated": true
+ },
+ "$recursiveRef": {
+ "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".",
+ "$ref": "meta/core#/$defs/uriReferenceString",
+ "deprecated": true
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft3.json b/third_party/python/jsonschema/jsonschema/schemas/draft3.json
new file mode 100644
index 0000000000..8b26b1f89f
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft3.json
@@ -0,0 +1,172 @@
+{
+ "$schema" : "http://json-schema.org/draft-03/schema#",
+ "id" : "http://json-schema.org/draft-03/schema#",
+ "type" : "object",
+
+ "properties" : {
+ "type" : {
+ "type" : ["string", "array"],
+ "items" : {
+ "type" : ["string", {"$ref" : "#"}]
+ },
+ "uniqueItems" : true,
+ "default" : "any"
+ },
+
+ "properties" : {
+ "type" : "object",
+ "additionalProperties" : {"$ref" : "#"},
+ "default" : {}
+ },
+
+ "patternProperties" : {
+ "type" : "object",
+ "additionalProperties" : {"$ref" : "#"},
+ "default" : {}
+ },
+
+ "additionalProperties" : {
+ "type" : [{"$ref" : "#"}, "boolean"],
+ "default" : {}
+ },
+
+ "items" : {
+ "type" : [{"$ref" : "#"}, "array"],
+ "items" : {"$ref" : "#"},
+ "default" : {}
+ },
+
+ "additionalItems" : {
+ "type" : [{"$ref" : "#"}, "boolean"],
+ "default" : {}
+ },
+
+ "required" : {
+ "type" : "boolean",
+ "default" : false
+ },
+
+ "dependencies" : {
+ "type" : "object",
+ "additionalProperties" : {
+ "type" : ["string", "array", {"$ref" : "#"}],
+ "items" : {
+ "type" : "string"
+ }
+ },
+ "default" : {}
+ },
+
+ "minimum" : {
+ "type" : "number"
+ },
+
+ "maximum" : {
+ "type" : "number"
+ },
+
+ "exclusiveMinimum" : {
+ "type" : "boolean",
+ "default" : false
+ },
+
+ "exclusiveMaximum" : {
+ "type" : "boolean",
+ "default" : false
+ },
+
+ "minItems" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "default" : 0
+ },
+
+ "maxItems" : {
+ "type" : "integer",
+ "minimum" : 0
+ },
+
+ "uniqueItems" : {
+ "type" : "boolean",
+ "default" : false
+ },
+
+ "pattern" : {
+ "type" : "string",
+ "format" : "regex"
+ },
+
+ "minLength" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "default" : 0
+ },
+
+ "maxLength" : {
+ "type" : "integer"
+ },
+
+ "enum" : {
+ "type" : "array",
+ "minItems" : 1,
+ "uniqueItems" : true
+ },
+
+ "default" : {
+ "type" : "any"
+ },
+
+ "title" : {
+ "type" : "string"
+ },
+
+ "description" : {
+ "type" : "string"
+ },
+
+ "format" : {
+ "type" : "string"
+ },
+
+ "divisibleBy" : {
+ "type" : "number",
+ "minimum" : 0,
+ "exclusiveMinimum" : true,
+ "default" : 1
+ },
+
+ "disallow" : {
+ "type" : ["string", "array"],
+ "items" : {
+ "type" : ["string", {"$ref" : "#"}]
+ },
+ "uniqueItems" : true
+ },
+
+ "extends" : {
+ "type" : [{"$ref" : "#"}, "array"],
+ "items" : {"$ref" : "#"},
+ "default" : {}
+ },
+
+ "id" : {
+ "type" : "string"
+ },
+
+ "$ref" : {
+ "type" : "string"
+ },
+
+ "$schema" : {
+ "type" : "string",
+ "format" : "uri"
+ }
+ },
+
+ "dependencies" : {
+ "exclusiveMinimum" : "minimum",
+ "exclusiveMaximum" : "maximum"
+ },
+
+ "default" : {}
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft4.json b/third_party/python/jsonschema/jsonschema/schemas/draft4.json
new file mode 100644
index 0000000000..bcbb84743e
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft4.json
@@ -0,0 +1,149 @@
+{
+ "id": "http://json-schema.org/draft-04/schema#",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "positiveInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "positiveIntegerDefault0": {
+ "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
+ },
+ "simpleTypes": {
+ "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ },
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "$schema": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": {},
+ "multipleOf": {
+ "type": "number",
+ "minimum": 0,
+ "exclusiveMinimum": true
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "boolean",
+ "default": false
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxLength": { "$ref": "#/definitions/positiveInteger" },
+ "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": {
+ "anyOf": [
+ { "type": "boolean" },
+ { "$ref": "#" }
+ ],
+ "default": {}
+ },
+ "items": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/schemaArray" }
+ ],
+ "default": {}
+ },
+ "maxItems": { "$ref": "#/definitions/positiveInteger" },
+ "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxProperties": { "$ref": "#/definitions/positiveInteger" },
+ "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": {
+ "anyOf": [
+ { "type": "boolean" },
+ { "$ref": "#" }
+ ],
+ "default": {}
+ },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/stringArray" }
+ ]
+ }
+ },
+ "enum": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "format": { "type": "string" },
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "dependencies": {
+ "exclusiveMaximum": [ "maximum" ],
+ "exclusiveMinimum": [ "minimum" ]
+ },
+ "default": {}
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft6.json b/third_party/python/jsonschema/jsonschema/schemas/draft6.json
new file mode 100644
index 0000000000..a0d2bf7896
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft6.json
@@ -0,0 +1,153 @@
+{
+ "$schema": "http://json-schema.org/draft-06/schema#",
+ "$id": "http://json-schema.org/draft-06/schema#",
+ "title": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "allOf": [
+ { "$ref": "#/definitions/nonNegativeInteger" },
+ { "default": 0 }
+ ]
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ },
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$ref": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": {},
+ "examples": {
+ "type": "array",
+ "items": {}
+ },
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": { "$ref": "#" },
+ "items": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/schemaArray" }
+ ],
+ "default": {}
+ },
+ "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "contains": { "$ref": "#" },
+ "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": { "$ref": "#" },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/stringArray" }
+ ]
+ }
+ },
+ "propertyNames": { "$ref": "#" },
+ "const": {},
+ "enum": {
+ "type": "array"
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "format": { "type": "string" },
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "default": {}
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/draft7.json b/third_party/python/jsonschema/jsonschema/schemas/draft7.json
new file mode 100644
index 0000000000..746cde9690
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/draft7.json
@@ -0,0 +1,166 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "http://json-schema.org/draft-07/schema#",
+ "title": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "allOf": [
+ { "$ref": "#/definitions/nonNegativeInteger" },
+ { "default": 0 }
+ ]
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ },
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$ref": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$comment": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": true,
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "examples": {
+ "type": "array",
+ "items": true
+ },
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": { "$ref": "#" },
+ "items": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/schemaArray" }
+ ],
+ "default": true
+ },
+ "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "contains": { "$ref": "#" },
+ "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": { "$ref": "#" },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/stringArray" }
+ ]
+ }
+ },
+ "propertyNames": { "$ref": "#" },
+ "const": true,
+ "enum": {
+ "type": "array",
+ "items": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "format": { "type": "string" },
+ "contentMediaType": { "type": "string" },
+ "contentEncoding": { "type": "string" },
+ "if": {"$ref": "#"},
+ "then": {"$ref": "#"},
+ "else": {"$ref": "#"},
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "default": true
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/applicator b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/applicator
new file mode 100644
index 0000000000..24a1cc4f4f
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/applicator
@@ -0,0 +1,56 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/applicator",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/applicator": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Applicator vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "additionalItems": { "$recursiveRef": "#" },
+ "unevaluatedItems": { "$recursiveRef": "#" },
+ "items": {
+ "anyOf": [
+ { "$recursiveRef": "#" },
+ { "$ref": "#/$defs/schemaArray" }
+ ]
+ },
+ "contains": { "$recursiveRef": "#" },
+ "additionalProperties": { "$recursiveRef": "#" },
+ "unevaluatedProperties": { "$recursiveRef": "#" },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependentSchemas": {
+ "type": "object",
+ "additionalProperties": {
+ "$recursiveRef": "#"
+ }
+ },
+ "propertyNames": { "$recursiveRef": "#" },
+ "if": { "$recursiveRef": "#" },
+ "then": { "$recursiveRef": "#" },
+ "else": { "$recursiveRef": "#" },
+ "allOf": { "$ref": "#/$defs/schemaArray" },
+ "anyOf": { "$ref": "#/$defs/schemaArray" },
+ "oneOf": { "$ref": "#/$defs/schemaArray" },
+ "not": { "$recursiveRef": "#" }
+ },
+ "$defs": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$recursiveRef": "#" }
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/content b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/content
new file mode 100644
index 0000000000..f6752a8efb
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/content
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/content",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/content": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Content vocabulary meta-schema",
+
+ "type": ["object", "boolean"],
+ "properties": {
+ "contentMediaType": { "type": "string" },
+ "contentEncoding": { "type": "string" },
+ "contentSchema": { "$recursiveRef": "#" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/core b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/core
new file mode 100644
index 0000000000..eb708a5604
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/core
@@ -0,0 +1,57 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/core",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/core": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Core vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "type": "string",
+ "format": "uri-reference",
+ "$comment": "Non-empty fragments not allowed.",
+ "pattern": "^[^#]*#?$"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$anchor": {
+ "type": "string",
+ "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$"
+ },
+ "$ref": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$recursiveRef": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$recursiveAnchor": {
+ "type": "boolean",
+ "default": false
+ },
+ "$vocabulary": {
+ "type": "object",
+ "propertyNames": {
+ "type": "string",
+ "format": "uri"
+ },
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ },
+ "$comment": {
+ "type": "string"
+ },
+ "$defs": {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" },
+ "default": {}
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/meta-data b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/meta-data
new file mode 100644
index 0000000000..da04cff6d3
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/meta-data
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/meta-data",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/meta-data": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Meta-data vocabulary meta-schema",
+
+ "type": ["object", "boolean"],
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": true,
+ "deprecated": {
+ "type": "boolean",
+ "default": false
+ },
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "writeOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "examples": {
+ "type": "array",
+ "items": true
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/validation b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/validation
new file mode 100644
index 0000000000..9f59677b30
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2019-09/validation
@@ -0,0 +1,98 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/validation",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/validation": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Validation vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "maxItems": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxContains": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minContains": {
+ "$ref": "#/$defs/nonNegativeInteger",
+ "default": 1
+ },
+ "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/$defs/stringArray" },
+ "dependentRequired": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/$defs/stringArray"
+ }
+ },
+ "const": true,
+ "enum": {
+ "type": "array",
+ "items": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/$defs/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/$defs/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ }
+ },
+ "$defs": {
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "$ref": "#/$defs/nonNegativeInteger",
+ "default": 0
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/applicator b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/applicator
new file mode 100644
index 0000000000..ca69923096
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/applicator
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/applicator",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/applicator": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Applicator vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "prefixItems": { "$ref": "#/$defs/schemaArray" },
+ "items": { "$dynamicRef": "#meta" },
+ "contains": { "$dynamicRef": "#meta" },
+ "additionalProperties": { "$dynamicRef": "#meta" },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$dynamicRef": "#meta" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$dynamicRef": "#meta" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependentSchemas": {
+ "type": "object",
+ "additionalProperties": { "$dynamicRef": "#meta" },
+ "default": {}
+ },
+ "propertyNames": { "$dynamicRef": "#meta" },
+ "if": { "$dynamicRef": "#meta" },
+ "then": { "$dynamicRef": "#meta" },
+ "else": { "$dynamicRef": "#meta" },
+ "allOf": { "$ref": "#/$defs/schemaArray" },
+ "anyOf": { "$ref": "#/$defs/schemaArray" },
+ "oneOf": { "$ref": "#/$defs/schemaArray" },
+ "not": { "$dynamicRef": "#meta" }
+ },
+ "$defs": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$dynamicRef": "#meta" }
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/content b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/content
new file mode 100644
index 0000000000..2f6e056a9a
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/content
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/content",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/content": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Content vocabulary meta-schema",
+
+ "type": ["object", "boolean"],
+ "properties": {
+ "contentEncoding": { "type": "string" },
+ "contentMediaType": { "type": "string" },
+ "contentSchema": { "$dynamicRef": "#meta" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/core b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/core
new file mode 100644
index 0000000000..dfc092d964
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/core
@@ -0,0 +1,51 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/core",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/core": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Core vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "$ref": "#/$defs/uriReferenceString",
+ "$comment": "Non-empty fragments not allowed.",
+ "pattern": "^[^#]*#?$"
+ },
+ "$schema": { "$ref": "#/$defs/uriString" },
+ "$ref": { "$ref": "#/$defs/uriReferenceString" },
+ "$anchor": { "$ref": "#/$defs/anchorString" },
+ "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" },
+ "$dynamicAnchor": { "$ref": "#/$defs/anchorString" },
+ "$vocabulary": {
+ "type": "object",
+ "propertyNames": { "$ref": "#/$defs/uriString" },
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ },
+ "$comment": {
+ "type": "string"
+ },
+ "$defs": {
+ "type": "object",
+ "additionalProperties": { "$dynamicRef": "#meta" }
+ }
+ },
+ "$defs": {
+ "anchorString": {
+ "type": "string",
+ "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$"
+ },
+ "uriString": {
+ "type": "string",
+ "format": "uri"
+ },
+ "uriReferenceString": {
+ "type": "string",
+ "format": "uri-reference"
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format
new file mode 100644
index 0000000000..09bbfdda97
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "https://json-schema.org/draft/2019-09/meta/format",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/format": true
+ },
+ "$recursiveAnchor": true,
+
+ "title": "Format vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "format": { "type": "string" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-annotation b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-annotation
new file mode 100644
index 0000000000..51ef7ea118
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-annotation
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/format-annotation": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Format vocabulary meta-schema for annotation results",
+ "type": ["object", "boolean"],
+ "properties": {
+ "format": { "type": "string" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-assertion b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-assertion
new file mode 100644
index 0000000000..5e73fd7571
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/format-assertion
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/format-assertion",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/format-assertion": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Format vocabulary meta-schema for assertion results",
+ "type": ["object", "boolean"],
+ "properties": {
+ "format": { "type": "string" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/meta-data b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/meta-data
new file mode 100644
index 0000000000..05cbc22afd
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/meta-data
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/meta-data",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/meta-data": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Meta-data vocabulary meta-schema",
+
+ "type": ["object", "boolean"],
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": true,
+ "deprecated": {
+ "type": "boolean",
+ "default": false
+ },
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "writeOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "examples": {
+ "type": "array",
+ "items": true
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/unevaluated b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/unevaluated
new file mode 100644
index 0000000000..5f62a3ffa2
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/unevaluated
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/unevaluated": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Unevaluated applicator vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "unevaluatedItems": { "$dynamicRef": "#meta" },
+ "unevaluatedProperties": { "$dynamicRef": "#meta" }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/validation b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/validation
new file mode 100644
index 0000000000..606b87ba2e
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/schemas/vocabularies/draft2020-12/validation
@@ -0,0 +1,98 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://json-schema.org/draft/2020-12/meta/validation",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/validation": true
+ },
+ "$dynamicAnchor": "meta",
+
+ "title": "Validation vocabulary meta-schema",
+ "type": ["object", "boolean"],
+ "properties": {
+ "type": {
+ "anyOf": [
+ { "$ref": "#/$defs/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/$defs/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "const": true,
+ "enum": {
+ "type": "array",
+ "items": true
+ },
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "maxItems": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxContains": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minContains": {
+ "$ref": "#/$defs/nonNegativeInteger",
+ "default": 1
+ },
+ "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/$defs/stringArray" },
+ "dependentRequired": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/$defs/stringArray"
+ }
+ }
+ },
+ "$defs": {
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "$ref": "#/$defs/nonNegativeInteger",
+ "default": 0
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ }
+}
diff --git a/third_party/python/jsonschema/jsonschema/validators.py b/third_party/python/jsonschema/jsonschema/validators.py
new file mode 100644
index 0000000000..66e803ea2d
--- /dev/null
+++ b/third_party/python/jsonschema/jsonschema/validators.py
@@ -0,0 +1,1161 @@
+"""
+Creation and extension of validators, with implementations for existing drafts.
+"""
+from __future__ import annotations
+
+from collections import deque
+from collections.abc import Mapping, Sequence
+from functools import lru_cache
+from operator import methodcaller
+from urllib.parse import unquote, urldefrag, urljoin, urlsplit
+from urllib.request import urlopen
+from warnings import warn
+import contextlib
+import json
+import reprlib
+import typing
+import warnings
+
+from pyrsistent import m
+import attr
+
+from jsonschema import (
+ _format,
+ _legacy_validators,
+ _types,
+ _utils,
+ _validators,
+ exceptions,
+)
+
+_UNSET = _utils.Unset()
+
+_VALIDATORS: dict[str, typing.Any] = {}
+_META_SCHEMAS = _utils.URIDict()
+_VOCABULARIES: list[tuple[str, typing.Any]] = []
+
+
+def __getattr__(name):
+ if name == "ErrorTree":
+ warnings.warn(
+ "Importing ErrorTree from jsonschema.validators is deprecated. "
+ "Instead import it from jsonschema.exceptions.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ from jsonschema.exceptions import ErrorTree
+ return ErrorTree
+ elif name == "validators":
+ warnings.warn(
+ "Accessing jsonschema.validators.validators is deprecated. "
+ "Use jsonschema.validators.validator_for with a given schema.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return _VALIDATORS
+ elif name == "meta_schemas":
+ warnings.warn(
+ "Accessing jsonschema.validators.meta_schemas is deprecated. "
+ "Use jsonschema.validators.validator_for with a given schema.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return _META_SCHEMAS
+ raise AttributeError(f"module {__name__} has no attribute {name}")
+
+
+def validates(version):
+ """
+ Register the decorated validator for a ``version`` of the specification.
+
+ Registered validators and their meta schemas will be considered when
+ parsing :kw:`$schema` keywords' URIs.
+
+ Arguments:
+
+ version (str):
+
+ An identifier to use as the version's name
+
+ Returns:
+
+ collections.abc.Callable:
+
+ a class decorator to decorate the validator with the version
+ """
+
+ def _validates(cls):
+ _VALIDATORS[version] = cls
+ meta_schema_id = cls.ID_OF(cls.META_SCHEMA)
+ _META_SCHEMAS[meta_schema_id] = cls
+ return cls
+ return _validates
+
+
+def _id_of(schema):
+ """
+ Return the ID of a schema for recent JSON Schema drafts.
+ """
+ if schema is True or schema is False:
+ return ""
+ return schema.get("$id", "")
+
+
+def _store_schema_list():
+ if not _VOCABULARIES:
+ package = _utils.resources.files(__package__)
+ for version in package.joinpath("schemas", "vocabularies").iterdir():
+ for path in version.iterdir():
+ vocabulary = json.loads(path.read_text())
+ _VOCABULARIES.append((vocabulary["$id"], vocabulary))
+ return [
+ (id, validator.META_SCHEMA) for id, validator in _META_SCHEMAS.items()
+ ] + _VOCABULARIES
+
+
+def create(
+ meta_schema,
+ validators=(),
+ version=None,
+ type_checker=_types.draft202012_type_checker,
+ format_checker=_format.draft202012_format_checker,
+ id_of=_id_of,
+ applicable_validators=methodcaller("items"),
+):
+ """
+ Create a new validator class.
+
+ Arguments:
+
+ meta_schema (collections.abc.Mapping):
+
+ the meta schema for the new validator class
+
+ validators (collections.abc.Mapping):
+
+ a mapping from names to callables, where each callable will
+ validate the schema property with the given name.
+
+ Each callable should take 4 arguments:
+
+ 1. a validator instance,
+ 2. the value of the property being validated within the
+ instance
+ 3. the instance
+ 4. the schema
+
+ 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.
+
+ type_checker (jsonschema.TypeChecker):
+
+ a type checker, used when applying the :kw:`type` keyword.
+
+ If unprovided, a `jsonschema.TypeChecker` will be created
+ with a set of default types typical of JSON Schema drafts.
+
+ format_checker (jsonschema.FormatChecker):
+
+ a format checker, used when applying the :kw:`format` keyword.
+
+ If unprovided, a `jsonschema.FormatChecker` will be created
+ with a set of default formats typical of JSON Schema drafts.
+
+ id_of (collections.abc.Callable):
+
+ A function that given a schema, returns its ID.
+
+ applicable_validators (collections.abc.Callable):
+
+ A function that given a schema, returns the list of
+ applicable validators (validation keywords and callables)
+ which will be used to validate the instance.
+
+ Returns:
+
+ a new `jsonschema.protocols.Validator` class
+ """
+ # preemptively don't shadow the `Validator.format_checker` local
+ format_checker_arg = format_checker
+
+ @attr.s
+ class Validator:
+
+ VALIDATORS = dict(validators)
+ META_SCHEMA = dict(meta_schema)
+ TYPE_CHECKER = type_checker
+ FORMAT_CHECKER = format_checker_arg
+ ID_OF = staticmethod(id_of)
+
+ schema = attr.ib(repr=reprlib.repr)
+ resolver = attr.ib(default=None, repr=False)
+ format_checker = attr.ib(default=None)
+
+ def __init_subclass__(cls):
+ warnings.warn(
+ (
+ "Subclassing validator classes is not intended to "
+ "be part of their public API. A future version "
+ "will make doing so an error, as the behavior of "
+ "subclasses isn't guaranteed to stay the same "
+ "between releases of jsonschema. Instead, prefer "
+ "composition of validators, wrapping them in an object "
+ "owned entirely by the downstream library."
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ def __attrs_post_init__(self):
+ if self.resolver is None:
+ self.resolver = RefResolver.from_schema(
+ self.schema,
+ id_of=id_of,
+ )
+
+ @classmethod
+ def check_schema(cls, schema, format_checker=_UNSET):
+ Validator = validator_for(cls.META_SCHEMA, default=cls)
+ if format_checker is _UNSET:
+ format_checker = Validator.FORMAT_CHECKER
+ validator = Validator(
+ schema=cls.META_SCHEMA,
+ format_checker=format_checker,
+ )
+ for error in validator.iter_errors(schema):
+ raise exceptions.SchemaError.create_from(error)
+
+ def evolve(self, **changes):
+ # Essentially reproduces attr.evolve, but may involve instantiating
+ # a different class than this one.
+ cls = self.__class__
+
+ schema = changes.setdefault("schema", self.schema)
+ NewValidator = validator_for(schema, default=cls)
+
+ for field in attr.fields(cls):
+ if not field.init:
+ continue
+ attr_name = field.name # To deal with private attributes.
+ init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
+ if init_name not in changes:
+ changes[init_name] = getattr(self, attr_name)
+
+ return NewValidator(**changes)
+
+ def iter_errors(self, instance, _schema=None):
+ if _schema is not None:
+ warnings.warn(
+ (
+ "Passing a schema to Validator.iter_errors "
+ "is deprecated and will be removed in a future "
+ "release. Call validator.evolve(schema=new_schema)."
+ "iter_errors(...) instead."
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ else:
+ _schema = self.schema
+
+ if _schema is True:
+ return
+ elif _schema is False:
+ yield exceptions.ValidationError(
+ f"False schema does not allow {instance!r}",
+ validator=None,
+ validator_value=None,
+ instance=instance,
+ schema=_schema,
+ )
+ return
+
+ scope = id_of(_schema)
+ if scope:
+ self.resolver.push_scope(scope)
+ try:
+ for k, v in applicable_validators(_schema):
+ validator = self.VALIDATORS.get(k)
+ if validator is None:
+ continue
+
+ errors = validator(self, v, instance, _schema) or ()
+ for error in errors:
+ # set details if not already set by the called fn
+ error._set(
+ validator=k,
+ validator_value=v,
+ instance=instance,
+ schema=_schema,
+ type_checker=self.TYPE_CHECKER,
+ )
+ if k not in {"if", "$ref"}:
+ error.schema_path.appendleft(k)
+ yield error
+ finally:
+ if scope:
+ self.resolver.pop_scope()
+
+ def descend(self, instance, schema, path=None, schema_path=None):
+ for error in self.evolve(schema=schema).iter_errors(instance):
+ if path is not None:
+ error.path.appendleft(path)
+ if schema_path is not None:
+ error.schema_path.appendleft(schema_path)
+ yield error
+
+ def validate(self, *args, **kwargs):
+ for error in self.iter_errors(*args, **kwargs):
+ raise error
+
+ def is_type(self, instance, type):
+ try:
+ return self.TYPE_CHECKER.is_type(instance, type)
+ except exceptions.UndefinedTypeCheck:
+ raise exceptions.UnknownType(type, instance, self.schema)
+
+ def is_valid(self, instance, _schema=None):
+ if _schema is not None:
+ warnings.warn(
+ (
+ "Passing a schema to Validator.is_valid is deprecated "
+ "and will be removed in a future release. Call "
+ "validator.evolve(schema=new_schema).is_valid(...) "
+ "instead."
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ self = self.evolve(schema=_schema)
+
+ error = next(self.iter_errors(instance), None)
+ return error is None
+
+ if version is not None:
+ safe = version.title().replace(" ", "").replace("-", "")
+ Validator.__name__ = Validator.__qualname__ = f"{safe}Validator"
+ Validator = validates(version)(Validator)
+
+ return Validator
+
+
+def extend(
+ validator,
+ validators=(),
+ version=None,
+ type_checker=None,
+ format_checker=None,
+):
+ """
+ Create a new validator class by extending an existing one.
+
+ Arguments:
+
+ validator (jsonschema.protocols.Validator):
+
+ an existing validator class
+
+ validators (collections.abc.Mapping):
+
+ a mapping of new validator callables to extend with, whose
+ structure is as in `create`.
+
+ .. 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.
+
+ 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
+ ``OldValidator.VALIDATORS["validation_keyword_name"]``.
+
+ version (str):
+
+ a version for the new validator class
+
+ type_checker (jsonschema.TypeChecker):
+
+ a type checker, used when applying the :kw:`type` keyword.
+
+ If unprovided, the type checker of the extended
+ `jsonschema.protocols.Validator` will be carried along.
+
+ format_checker (jsonschema.FormatChecker):
+
+ a format checker, used when applying the :kw:`format` keyword.
+
+ If unprovided, the format checker of the extended
+ `jsonschema.protocols.Validator` will be carried along.
+
+ Returns:
+
+ a new `jsonschema.protocols.Validator` class extending the one
+ provided
+
+ .. note:: Meta Schemas
+
+ The new validator class will have its parent's meta schema.
+
+ If you wish to change or extend the meta schema in the new
+ validator class, modify ``META_SCHEMA`` directly on the returned
+ class. Note that no implicit copying is done, so a copy should
+ likely be made before modifying it, in order to not affect the
+ old validator.
+ """
+
+ all_validators = dict(validator.VALIDATORS)
+ all_validators.update(validators)
+
+ if type_checker is None:
+ type_checker = validator.TYPE_CHECKER
+ if format_checker is None:
+ format_checker = validator.FORMAT_CHECKER
+ return create(
+ meta_schema=validator.META_SCHEMA,
+ validators=all_validators,
+ version=version,
+ type_checker=type_checker,
+ format_checker=format_checker,
+ id_of=validator.ID_OF,
+ )
+
+
+Draft3Validator = create(
+ meta_schema=_utils.load_schema("draft3"),
+ validators={
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "dependencies": _legacy_validators.dependencies_draft3,
+ "disallow": _legacy_validators.disallow_draft3,
+ "divisibleBy": _validators.multipleOf,
+ "enum": _validators.enum,
+ "extends": _legacy_validators.extends_draft3,
+ "format": _validators.format,
+ "items": _legacy_validators.items_draft3_draft4,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maximum": _legacy_validators.maximum_draft3_draft4,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minimum": _legacy_validators.minimum_draft3_draft4,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "properties": _legacy_validators.properties_draft3,
+ "type": _legacy_validators.type_draft3,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft3_type_checker,
+ format_checker=_format.draft3_format_checker,
+ version="draft3",
+ id_of=_legacy_validators.id_of_ignore_ref(property="id"),
+ applicable_validators=_legacy_validators.ignore_ref_siblings,
+)
+
+Draft4Validator = create(
+ meta_schema=_utils.load_schema("draft4"),
+ validators={
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "allOf": _validators.allOf,
+ "anyOf": _validators.anyOf,
+ "dependencies": _legacy_validators.dependencies_draft4_draft6_draft7,
+ "enum": _validators.enum,
+ "format": _validators.format,
+ "items": _legacy_validators.items_draft3_draft4,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maxProperties": _validators.maxProperties,
+ "maximum": _legacy_validators.maximum_draft3_draft4,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minProperties": _validators.minProperties,
+ "minimum": _legacy_validators.minimum_draft3_draft4,
+ "multipleOf": _validators.multipleOf,
+ "not": _validators.not_,
+ "oneOf": _validators.oneOf,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "properties": _validators.properties,
+ "required": _validators.required,
+ "type": _validators.type,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft4_type_checker,
+ format_checker=_format.draft4_format_checker,
+ version="draft4",
+ id_of=_legacy_validators.id_of_ignore_ref(property="id"),
+ applicable_validators=_legacy_validators.ignore_ref_siblings,
+)
+
+Draft6Validator = create(
+ meta_schema=_utils.load_schema("draft6"),
+ validators={
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "allOf": _validators.allOf,
+ "anyOf": _validators.anyOf,
+ "const": _validators.const,
+ "contains": _legacy_validators.contains_draft6_draft7,
+ "dependencies": _legacy_validators.dependencies_draft4_draft6_draft7,
+ "enum": _validators.enum,
+ "exclusiveMaximum": _validators.exclusiveMaximum,
+ "exclusiveMinimum": _validators.exclusiveMinimum,
+ "format": _validators.format,
+ "items": _legacy_validators.items_draft6_draft7_draft201909,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maxProperties": _validators.maxProperties,
+ "maximum": _validators.maximum,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minProperties": _validators.minProperties,
+ "minimum": _validators.minimum,
+ "multipleOf": _validators.multipleOf,
+ "not": _validators.not_,
+ "oneOf": _validators.oneOf,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "properties": _validators.properties,
+ "propertyNames": _validators.propertyNames,
+ "required": _validators.required,
+ "type": _validators.type,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft6_type_checker,
+ format_checker=_format.draft6_format_checker,
+ version="draft6",
+ id_of=_legacy_validators.id_of_ignore_ref(),
+ applicable_validators=_legacy_validators.ignore_ref_siblings,
+)
+
+Draft7Validator = create(
+ meta_schema=_utils.load_schema("draft7"),
+ validators={
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "allOf": _validators.allOf,
+ "anyOf": _validators.anyOf,
+ "const": _validators.const,
+ "contains": _legacy_validators.contains_draft6_draft7,
+ "dependencies": _legacy_validators.dependencies_draft4_draft6_draft7,
+ "enum": _validators.enum,
+ "exclusiveMaximum": _validators.exclusiveMaximum,
+ "exclusiveMinimum": _validators.exclusiveMinimum,
+ "format": _validators.format,
+ "if": _validators.if_,
+ "items": _legacy_validators.items_draft6_draft7_draft201909,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maxProperties": _validators.maxProperties,
+ "maximum": _validators.maximum,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minProperties": _validators.minProperties,
+ "minimum": _validators.minimum,
+ "multipleOf": _validators.multipleOf,
+ "not": _validators.not_,
+ "oneOf": _validators.oneOf,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "properties": _validators.properties,
+ "propertyNames": _validators.propertyNames,
+ "required": _validators.required,
+ "type": _validators.type,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft7_type_checker,
+ format_checker=_format.draft7_format_checker,
+ version="draft7",
+ id_of=_legacy_validators.id_of_ignore_ref(),
+ applicable_validators=_legacy_validators.ignore_ref_siblings,
+)
+
+Draft201909Validator = create(
+ meta_schema=_utils.load_schema("draft2019-09"),
+ validators={
+ "$recursiveRef": _legacy_validators.recursiveRef,
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "allOf": _validators.allOf,
+ "anyOf": _validators.anyOf,
+ "const": _validators.const,
+ "contains": _validators.contains,
+ "dependentRequired": _validators.dependentRequired,
+ "dependentSchemas": _validators.dependentSchemas,
+ "enum": _validators.enum,
+ "exclusiveMaximum": _validators.exclusiveMaximum,
+ "exclusiveMinimum": _validators.exclusiveMinimum,
+ "format": _validators.format,
+ "if": _validators.if_,
+ "items": _legacy_validators.items_draft6_draft7_draft201909,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maxProperties": _validators.maxProperties,
+ "maximum": _validators.maximum,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minProperties": _validators.minProperties,
+ "minimum": _validators.minimum,
+ "multipleOf": _validators.multipleOf,
+ "not": _validators.not_,
+ "oneOf": _validators.oneOf,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "properties": _validators.properties,
+ "propertyNames": _validators.propertyNames,
+ "required": _validators.required,
+ "type": _validators.type,
+ "unevaluatedItems": _legacy_validators.unevaluatedItems_draft2019,
+ "unevaluatedProperties": _validators.unevaluatedProperties,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft201909_type_checker,
+ format_checker=_format.draft201909_format_checker,
+ version="draft2019-09",
+)
+
+Draft202012Validator = create(
+ meta_schema=_utils.load_schema("draft2020-12"),
+ validators={
+ "$dynamicRef": _validators.dynamicRef,
+ "$ref": _validators.ref,
+ "additionalItems": _validators.additionalItems,
+ "additionalProperties": _validators.additionalProperties,
+ "allOf": _validators.allOf,
+ "anyOf": _validators.anyOf,
+ "const": _validators.const,
+ "contains": _validators.contains,
+ "dependentRequired": _validators.dependentRequired,
+ "dependentSchemas": _validators.dependentSchemas,
+ "enum": _validators.enum,
+ "exclusiveMaximum": _validators.exclusiveMaximum,
+ "exclusiveMinimum": _validators.exclusiveMinimum,
+ "format": _validators.format,
+ "if": _validators.if_,
+ "items": _validators.items,
+ "maxItems": _validators.maxItems,
+ "maxLength": _validators.maxLength,
+ "maxProperties": _validators.maxProperties,
+ "maximum": _validators.maximum,
+ "minItems": _validators.minItems,
+ "minLength": _validators.minLength,
+ "minProperties": _validators.minProperties,
+ "minimum": _validators.minimum,
+ "multipleOf": _validators.multipleOf,
+ "not": _validators.not_,
+ "oneOf": _validators.oneOf,
+ "pattern": _validators.pattern,
+ "patternProperties": _validators.patternProperties,
+ "prefixItems": _validators.prefixItems,
+ "properties": _validators.properties,
+ "propertyNames": _validators.propertyNames,
+ "required": _validators.required,
+ "type": _validators.type,
+ "unevaluatedItems": _validators.unevaluatedItems,
+ "unevaluatedProperties": _validators.unevaluatedProperties,
+ "uniqueItems": _validators.uniqueItems,
+ },
+ type_checker=_types.draft202012_type_checker,
+ format_checker=_format.draft202012_format_checker,
+ version="draft2020-12",
+)
+
+_LATEST_VERSION = Draft202012Validator
+
+
+class RefResolver:
+ """
+ Resolve JSON References.
+
+ Arguments:
+
+ base_uri (str):
+
+ The URI of the referring document
+
+ referrer:
+
+ The actual referring document
+
+ store (dict):
+
+ A mapping from URIs to documents to cache
+
+ cache_remote (bool):
+
+ Whether remote refs should be cached after first resolution
+
+ handlers (dict):
+
+ A mapping from URI schemes to functions that should be used
+ to retrieve them
+
+ 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 (:func:`functools.lru_cache`):
+
+ A cache that will be used for caching the results of
+ resolved remote URLs.
+
+ Attributes:
+
+ cache_remote (bool):
+
+ Whether remote refs should be cached after first resolution
+ """
+
+ def __init__(
+ self,
+ base_uri,
+ referrer,
+ store=m(),
+ cache_remote=True,
+ handlers=(),
+ urljoin_cache=None,
+ remote_cache=None,
+ ):
+ if urljoin_cache is None:
+ urljoin_cache = lru_cache(1024)(urljoin)
+ if remote_cache is None:
+ remote_cache = lru_cache(1024)(self.resolve_from_url)
+
+ self.referrer = referrer
+ self.cache_remote = cache_remote
+ self.handlers = dict(handlers)
+
+ self._scopes_stack = [base_uri]
+
+ self.store = _utils.URIDict(_store_schema_list())
+ self.store.update(store)
+ self.store.update(
+ (schema["$id"], schema)
+ for schema in store.values()
+ if isinstance(schema, Mapping) and "$id" in schema
+ )
+ self.store[base_uri] = referrer
+
+ self._urljoin_cache = urljoin_cache
+ self._remote_cache = remote_cache
+
+ @classmethod
+ def from_schema(cls, schema, id_of=_id_of, *args, **kwargs):
+ """
+ Construct a resolver from a JSON schema object.
+
+ Arguments:
+
+ schema:
+
+ the referring schema
+
+ Returns:
+
+ `RefResolver`
+ """
+
+ return cls(base_uri=id_of(schema), referrer=schema, *args, **kwargs) # noqa: B026, E501
+
+ 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:
+ raise exceptions.RefResolutionError(
+ "Failed to pop the scope from an empty stack. "
+ "`pop_scope()` should only be called once for every "
+ "`push_scope()`",
+ )
+
+ @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.
+
+ .. deprecated:: v4.0.0
+ """
+ warnings.warn(
+ "jsonschema.RefResolver.in_scope is deprecated and will be "
+ "removed in a future release.",
+ DeprecationWarning,
+ stacklevel=3,
+ )
+ self.push_scope(scope)
+ try:
+ yield
+ finally:
+ self.pop_scope()
+
+ @contextlib.contextmanager
+ def resolving(self, ref):
+ """
+ Resolve the given ``ref`` and enter its resolution scope.
+
+ Exits the scope on exit of this context manager.
+
+ Arguments:
+
+ ref (str):
+
+ The reference to resolve
+ """
+
+ url, resolved = self.resolve(ref)
+ self.push_scope(url)
+ try:
+ yield resolved
+ finally:
+ self.pop_scope()
+
+ def _find_in_referrer(self, key):
+ return self._get_subschemas_cache()[key]
+
+ @lru_cache() # noqa: B019
+ def _get_subschemas_cache(self):
+ cache = {key: [] for key in _SUBSCHEMAS_KEYWORDS}
+ for keyword, subschema in _search_schema(
+ self.referrer, _match_subschema_keywords,
+ ):
+ cache[keyword].append(subschema)
+ return cache
+
+ @lru_cache() # noqa: B019
+ def _find_in_subschemas(self, url):
+ subschemas = self._get_subschemas_cache()["$id"]
+ if not subschemas:
+ return None
+ uri, fragment = urldefrag(url)
+ for subschema in subschemas:
+ target_uri = self._urljoin_cache(
+ self.resolution_scope, subschema["$id"],
+ )
+ if target_uri.rstrip("/") == uri.rstrip("/"):
+ if fragment:
+ subschema = self.resolve_fragment(subschema, fragment)
+ self.store[url] = subschema
+ return url, subschema
+ return None
+
+ def resolve(self, ref):
+ """
+ Resolve the given reference.
+ """
+ url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/")
+
+ match = self._find_in_subschemas(url)
+ if match is not None:
+ return match
+
+ return url, self._remote_cache(url)
+
+ def resolve_from_url(self, url):
+ """
+ Resolve the given URL.
+ """
+ url, fragment = urldefrag(url)
+ if not url:
+ url = self.base_uri
+
+ try:
+ document = self.store[url]
+ except KeyError:
+ try:
+ document = self.resolve_remote(url)
+ except Exception as exc:
+ raise exceptions.RefResolutionError(exc)
+
+ return self.resolve_fragment(document, fragment)
+
+ def resolve_fragment(self, document, fragment):
+ """
+ Resolve a ``fragment`` within the referenced ``document``.
+
+ Arguments:
+
+ document:
+
+ The referent document
+
+ fragment (str):
+
+ a URI fragment to resolve within it
+ """
+
+ fragment = fragment.lstrip("/")
+
+ if not fragment:
+ return document
+
+ if document is self.referrer:
+ find = self._find_in_referrer
+ else:
+
+ def find(key):
+ yield from _search_schema(document, _match_keyword(key))
+
+ for keyword in ["$anchor", "$dynamicAnchor"]:
+ for subschema in find(keyword):
+ if fragment == subschema[keyword]:
+ return subschema
+ for keyword in ["id", "$id"]:
+ for subschema in find(keyword):
+ if "#" + fragment == subschema[keyword]:
+ return subschema
+
+ # Resolve via path
+ parts = unquote(fragment).split("/") if fragment else []
+ for part in parts:
+ part = part.replace("~1", "/").replace("~0", "~")
+
+ if isinstance(document, Sequence):
+ # Array indexes should be turned into integers
+ try:
+ part = int(part)
+ except ValueError:
+ pass
+ try:
+ document = document[part]
+ except (TypeError, LookupError):
+ raise exceptions.RefResolutionError(
+ f"Unresolvable JSON pointer: {fragment!r}",
+ )
+
+ return document
+
+ def resolve_remote(self, uri):
+ """
+ Resolve a remote ``uri``.
+
+ If called directly, does not check the store first, but after
+ retrieving the document at the specified URI it will be saved in
+ the store if :attr:`cache_remote` is True.
+
+ .. note::
+
+ If the requests_ library is present, ``jsonschema`` will use it to
+ request the remote ``uri``, so that the correct encoding is
+ detected and used.
+
+ If it isn't, or if the scheme of the ``uri`` is not ``http`` or
+ ``https``, UTF-8 is assumed.
+
+ Arguments:
+
+ uri (str):
+
+ The URI to resolve
+
+ Returns:
+
+ The retrieved document
+
+ .. _requests: https://pypi.org/project/requests/
+ """
+ try:
+ import requests
+ except ImportError:
+ requests = None
+
+ scheme = urlsplit(uri).scheme
+
+ if scheme in self.handlers:
+ result = self.handlers[scheme](uri)
+ elif scheme in ["http", "https"] and requests:
+ # Requests has support for detecting the correct encoding of
+ # json over http
+ result = requests.get(uri).json()
+ else:
+ # Otherwise, pass off to urllib and assume utf-8
+ with urlopen(uri) as url:
+ result = json.loads(url.read().decode("utf-8"))
+
+ if self.cache_remote:
+ self.store[uri] = result
+ return result
+
+
+_SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor")
+
+
+def _match_keyword(keyword):
+
+ def matcher(value):
+ if keyword in value:
+ yield value
+
+ return matcher
+
+
+def _match_subschema_keywords(value):
+ for keyword in _SUBSCHEMAS_KEYWORDS:
+ if keyword in value:
+ yield keyword, value
+
+
+def _search_schema(schema, matcher):
+ """Breadth-first search routine."""
+ values = deque([schema])
+ while values:
+ value = values.pop()
+ if not isinstance(value, dict):
+ continue
+ yield from matcher(value)
+ values.extendleft(value.values())
+
+
+def validate(instance, schema, cls=None, *args, **kwargs):
+ """
+ Validate an instance under the given schema.
+
+ >>> validate([2, 3, 4], {"maxItems": 2})
+ Traceback (most recent call last):
+ ...
+ ValidationError: [2, 3, 4] is too long
+
+ :func:`~jsonschema.validators.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 likely would prefer using the
+ `jsonschema.protocols.Validator.validate` method directly on a
+ specific validator (e.g. ``Draft20212Validator.validate``).
+
+
+ Arguments:
+
+ instance:
+
+ The instance to validate
+
+ schema:
+
+ The schema to validate with
+
+ cls (jsonschema.protocols.Validator):
+
+ 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
+ :kw:`$schema` keyword containing a known meta-schema [#]_ then the
+ proper validator will be used. The specification recommends that
+ all schemas contain :kw:`$schema` properties for this reason. If no
+ :kw:`$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``.
+
+ Raises:
+
+ `jsonschema.exceptions.ValidationError`:
+
+ if the instance is invalid
+
+ `jsonschema.exceptions.SchemaError`:
+
+ if the schema itself is invalid
+
+ .. rubric:: Footnotes
+ .. [#] known by a validator registered with
+ `jsonschema.validators.validates`
+ """
+ if cls is None:
+ cls = validator_for(schema)
+
+ cls.check_schema(schema)
+ validator = cls(schema, *args, **kwargs)
+ error = exceptions.best_match(validator.iter_errors(instance))
+ if error is not None:
+ raise error
+
+
+def validator_for(schema, default=_UNSET):
+ """
+ Retrieve the validator class appropriate for validating the given schema.
+
+ Uses the :kw:`$schema` keyword that should be present in the given
+ schema to look up the appropriate validator class.
+
+ Arguments:
+
+ schema (collections.abc.Mapping or bool):
+
+ the schema to look at
+
+ default:
+
+ the default to return if the appropriate validator class
+ cannot be determined.
+
+ If unprovided, the default is to return the latest supported
+ draft.
+ """
+
+ DefaultValidator = _LATEST_VERSION if default is _UNSET else default
+
+ if schema is True or schema is False or "$schema" not in schema:
+ return DefaultValidator
+ if schema["$schema"] not in _META_SCHEMAS:
+ if default is _UNSET:
+ warn(
+ (
+ "The metaschema specified by $schema was not found. "
+ "Using the latest draft to validate, but this will raise "
+ "an error in the future."
+ ),
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return _META_SCHEMAS.get(schema["$schema"], DefaultValidator)