Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
34
venv/Lib/site-packages/jsonschema/__init__.py
Normal file
34
venv/Lib/site-packages/jsonschema/__init__.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
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, `validate` is the quickest way to simply validate a given
|
||||
instance under a schema, and will create a validator for you.
|
||||
"""
|
||||
|
||||
from jsonschema.exceptions import (
|
||||
ErrorTree, FormatError, RefResolutionError, SchemaError, ValidationError
|
||||
)
|
||||
from jsonschema._format import (
|
||||
FormatChecker,
|
||||
draft3_format_checker,
|
||||
draft4_format_checker,
|
||||
draft6_format_checker,
|
||||
draft7_format_checker,
|
||||
)
|
||||
from jsonschema._types import TypeChecker
|
||||
from jsonschema.validators import (
|
||||
Draft3Validator,
|
||||
Draft4Validator,
|
||||
Draft6Validator,
|
||||
Draft7Validator,
|
||||
RefResolver,
|
||||
validate,
|
||||
)
|
||||
try:
|
||||
from importlib import metadata
|
||||
except ImportError: # for Python<3.8
|
||||
import importlib_metadata as metadata
|
||||
__version__ = metadata.version("jsonschema")
|
2
venv/Lib/site-packages/jsonschema/__main__.py
Normal file
2
venv/Lib/site-packages/jsonschema/__main__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from jsonschema.cli import main
|
||||
main()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/jsonschema/__pycache__/cli.cpython-36.pyc
Normal file
BIN
venv/Lib/site-packages/jsonschema/__pycache__/cli.cpython-36.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
425
venv/Lib/site-packages/jsonschema/_format.py
Normal file
425
venv/Lib/site-packages/jsonschema/_format.py
Normal file
|
@ -0,0 +1,425 @@
|
|||
import datetime
|
||||
import re
|
||||
import socket
|
||||
import struct
|
||||
|
||||
from jsonschema.compat import str_types
|
||||
from jsonschema.exceptions import FormatError
|
||||
|
||||
|
||||
class FormatChecker(object):
|
||||
"""
|
||||
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 check a custom format using a function that takes an instance and
|
||||
returns a ``bool``, use the `FormatChecker.checks` or
|
||||
`FormatChecker.cls_checks` decorators.
|
||||
|
||||
Arguments:
|
||||
|
||||
formats (~collections.Iterable):
|
||||
|
||||
The known formats to validate. This argument can be used to
|
||||
limit which formats will be used during validation.
|
||||
"""
|
||||
|
||||
checkers = {}
|
||||
|
||||
def __init__(self, formats=None):
|
||||
if formats is None:
|
||||
self.checkers = self.checkers.copy()
|
||||
else:
|
||||
self.checkers = dict((k, self.checkers[k]) for k in formats)
|
||||
|
||||
def __repr__(self):
|
||||
return "<FormatChecker checkers={}>".format(sorted(self.checkers))
|
||||
|
||||
def checks(self, format, raises=()):
|
||||
"""
|
||||
Register a decorated function as validating a new format.
|
||||
|
||||
Arguments:
|
||||
|
||||
format (str):
|
||||
|
||||
The format that the decorated function will check.
|
||||
|
||||
raises (Exception):
|
||||
|
||||
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):
|
||||
self.checkers[format] = (func, raises)
|
||||
return func
|
||||
return _checks
|
||||
|
||||
cls_checks = classmethod(checks)
|
||||
|
||||
def check(self, instance, format):
|
||||
"""
|
||||
Check whether the instance conforms to the given format.
|
||||
|
||||
Arguments:
|
||||
|
||||
instance (*any primitive type*, i.e. str, number, bool):
|
||||
|
||||
The instance to check
|
||||
|
||||
format (str):
|
||||
|
||||
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(
|
||||
"%r is not a %r" % (instance, format), cause=cause,
|
||||
)
|
||||
|
||||
def conforms(self, instance, format):
|
||||
"""
|
||||
Check whether the instance conforms to the given format.
|
||||
|
||||
Arguments:
|
||||
|
||||
instance (*any primitive type*, i.e. str, number, bool):
|
||||
|
||||
The instance to check
|
||||
|
||||
format (str):
|
||||
|
||||
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()
|
||||
|
||||
|
||||
_draft_checkers = dict(
|
||||
draft3=draft3_format_checker,
|
||||
draft4=draft4_format_checker,
|
||||
draft6=draft6_format_checker,
|
||||
draft7=draft7_format_checker,
|
||||
)
|
||||
|
||||
|
||||
def _checks_drafts(
|
||||
name=None,
|
||||
draft3=None,
|
||||
draft4=None,
|
||||
draft6=None,
|
||||
draft7=None,
|
||||
raises=(),
|
||||
):
|
||||
draft3 = draft3 or name
|
||||
draft4 = draft4 or name
|
||||
draft6 = draft6 or name
|
||||
draft7 = draft7 or name
|
||||
|
||||
def wrap(func):
|
||||
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)
|
||||
|
||||
# Oy. This is bad global state, but relied upon for now, until
|
||||
# deprecation. See https://github.com/Julian/jsonschema/issues/519
|
||||
# and test_format_checkers_come_with_defaults
|
||||
FormatChecker.cls_checks(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):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return "@" in instance
|
||||
|
||||
|
||||
_ipv4_re = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
|
||||
|
||||
|
||||
@_checks_drafts(
|
||||
draft3="ip-address", draft4="ipv4", draft6="ipv4", draft7="ipv4",
|
||||
)
|
||||
def is_ipv4(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
if not _ipv4_re.match(instance):
|
||||
return False
|
||||
return all(0 <= int(component) <= 255 for component in instance.split("."))
|
||||
|
||||
|
||||
if hasattr(socket, "inet_pton"):
|
||||
# FIXME: Really this only should raise struct.error, but see the sadness
|
||||
# that is https://twistedmatrix.com/trac/ticket/9409
|
||||
@_checks_drafts(
|
||||
name="ipv6", raises=(socket.error, struct.error, ValueError),
|
||||
)
|
||||
def is_ipv6(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return socket.inet_pton(socket.AF_INET6, instance)
|
||||
|
||||
|
||||
_host_name_re = re.compile(r"^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$")
|
||||
|
||||
|
||||
@_checks_drafts(
|
||||
draft3="host-name",
|
||||
draft4="hostname",
|
||||
draft6="hostname",
|
||||
draft7="hostname",
|
||||
)
|
||||
def is_host_name(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
if not _host_name_re.match(instance):
|
||||
return False
|
||||
components = instance.split(".")
|
||||
for component in components:
|
||||
if len(component) > 63:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
try:
|
||||
# The built-in `idna` codec only implements RFC 3890, so we go elsewhere.
|
||||
import idna
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@_checks_drafts(draft7="idn-hostname", raises=idna.IDNAError)
|
||||
def is_idn_host_name(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
idna.encode(instance)
|
||||
return True
|
||||
|
||||
|
||||
try:
|
||||
import rfc3987
|
||||
except ImportError:
|
||||
try:
|
||||
from rfc3986_validator import validate_rfc3986
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@_checks_drafts(name="uri")
|
||||
def is_uri(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return validate_rfc3986(instance, rule="URI")
|
||||
|
||||
@_checks_drafts(
|
||||
draft6="uri-reference",
|
||||
draft7="uri-reference",
|
||||
raises=ValueError,
|
||||
)
|
||||
def is_uri_reference(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return validate_rfc3986(instance, rule="URI_reference")
|
||||
|
||||
else:
|
||||
@_checks_drafts(draft7="iri", raises=ValueError)
|
||||
def is_iri(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return rfc3987.parse(instance, rule="IRI")
|
||||
|
||||
@_checks_drafts(draft7="iri-reference", raises=ValueError)
|
||||
def is_iri_reference(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return rfc3987.parse(instance, rule="IRI_reference")
|
||||
|
||||
@_checks_drafts(name="uri", raises=ValueError)
|
||||
def is_uri(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return rfc3987.parse(instance, rule="URI")
|
||||
|
||||
@_checks_drafts(
|
||||
draft6="uri-reference",
|
||||
draft7="uri-reference",
|
||||
raises=ValueError,
|
||||
)
|
||||
def is_uri_reference(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return rfc3987.parse(instance, rule="URI_reference")
|
||||
|
||||
|
||||
try:
|
||||
from strict_rfc3339 import validate_rfc3339
|
||||
except ImportError:
|
||||
try:
|
||||
from rfc3339_validator import validate_rfc3339
|
||||
except ImportError:
|
||||
validate_rfc3339 = None
|
||||
|
||||
if validate_rfc3339:
|
||||
@_checks_drafts(name="date-time")
|
||||
def is_datetime(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return validate_rfc3339(instance)
|
||||
|
||||
@_checks_drafts(draft7="time")
|
||||
def is_time(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return is_datetime("1970-01-01T" + instance)
|
||||
|
||||
|
||||
@_checks_drafts(name="regex", raises=re.error)
|
||||
def is_regex(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return re.compile(instance)
|
||||
|
||||
|
||||
@_checks_drafts(draft3="date", draft7="date", raises=ValueError)
|
||||
def is_date(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return datetime.datetime.strptime(instance, "%Y-%m-%d")
|
||||
|
||||
|
||||
@_checks_drafts(draft3="time", raises=ValueError)
|
||||
def is_draft3_time(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return datetime.datetime.strptime(instance, "%H:%M:%S")
|
||||
|
||||
|
||||
try:
|
||||
import webcolors
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
def is_css_color_code(instance):
|
||||
return webcolors.normalize_hex(instance)
|
||||
|
||||
@_checks_drafts(draft3="color", raises=(ValueError, TypeError))
|
||||
def is_css21_color(instance):
|
||||
if (
|
||||
not isinstance(instance, str_types) or
|
||||
instance.lower() in webcolors.css21_names_to_hex
|
||||
):
|
||||
return True
|
||||
return is_css_color_code(instance)
|
||||
|
||||
def is_css3_color(instance):
|
||||
if instance.lower() in webcolors.css3_names_to_hex:
|
||||
return True
|
||||
return is_css_color_code(instance)
|
||||
|
||||
|
||||
try:
|
||||
import jsonpointer
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@_checks_drafts(
|
||||
draft6="json-pointer",
|
||||
draft7="json-pointer",
|
||||
raises=jsonpointer.JsonPointerException,
|
||||
)
|
||||
def is_json_pointer(instance):
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
return 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",
|
||||
raises=jsonpointer.JsonPointerException,
|
||||
)
|
||||
def is_relative_json_pointer(instance):
|
||||
# Definition taken from:
|
||||
# https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3
|
||||
if not isinstance(instance, str_types):
|
||||
return True
|
||||
non_negative_integer, rest = [], ""
|
||||
for i, character in enumerate(instance):
|
||||
if character.isdigit():
|
||||
non_negative_integer.append(character)
|
||||
continue
|
||||
|
||||
if not non_negative_integer:
|
||||
return False
|
||||
|
||||
rest = instance[i:]
|
||||
break
|
||||
return (rest == "#") or jsonpointer.JsonPointer(rest)
|
||||
|
||||
|
||||
try:
|
||||
import uritemplate.exceptions
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@_checks_drafts(
|
||||
draft6="uri-template",
|
||||
draft7="uri-template",
|
||||
raises=uritemplate.exceptions.InvalidTemplate,
|
||||
)
|
||||
def is_uri_template(
|
||||
instance,
|
||||
template_validator=uritemplate.Validator().force_balanced_braces(),
|
||||
):
|
||||
template = uritemplate.URITemplate(instance)
|
||||
return template_validator.validate(template)
|
141
venv/Lib/site-packages/jsonschema/_legacy_validators.py
Normal file
141
venv/Lib/site-packages/jsonschema/_legacy_validators.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
from jsonschema import _utils
|
||||
from jsonschema.compat import iteritems
|
||||
from jsonschema.exceptions import ValidationError
|
||||
|
||||
|
||||
def dependencies_draft3(validator, dependencies, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for property, dependency in iteritems(dependencies):
|
||||
if property not in instance:
|
||||
continue
|
||||
|
||||
if validator.is_type(dependency, "object"):
|
||||
for error in validator.descend(
|
||||
instance, dependency, schema_path=property,
|
||||
):
|
||||
yield error
|
||||
elif validator.is_type(dependency, "string"):
|
||||
if dependency not in instance:
|
||||
yield ValidationError(
|
||||
"%r is a dependency of %r" % (dependency, property)
|
||||
)
|
||||
else:
|
||||
for each in dependency:
|
||||
if each not in instance:
|
||||
message = "%r is a dependency of %r"
|
||||
yield ValidationError(message % (each, property))
|
||||
|
||||
|
||||
def disallow_draft3(validator, disallow, instance, schema):
|
||||
for disallowed in _utils.ensure_list(disallow):
|
||||
if validator.is_valid(instance, {"type": [disallowed]}):
|
||||
yield ValidationError(
|
||||
"%r is disallowed for %r" % (disallowed, instance)
|
||||
)
|
||||
|
||||
|
||||
def extends_draft3(validator, extends, instance, schema):
|
||||
if validator.is_type(extends, "object"):
|
||||
for error in validator.descend(instance, extends):
|
||||
yield error
|
||||
return
|
||||
for index, subschema in enumerate(extends):
|
||||
for error in validator.descend(instance, subschema, schema_path=index):
|
||||
yield error
|
||||
|
||||
|
||||
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):
|
||||
for error in validator.descend(item, items, path=index):
|
||||
yield error
|
||||
else:
|
||||
for (index, item), subschema in zip(enumerate(instance), items):
|
||||
for error in validator.descend(
|
||||
item, subschema, path=index, schema_path=index,
|
||||
):
|
||||
yield error
|
||||
|
||||
|
||||
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:
|
||||
yield ValidationError(
|
||||
"%r is %s the minimum of %r" % (instance, cmp, minimum)
|
||||
)
|
||||
|
||||
|
||||
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:
|
||||
yield ValidationError(
|
||||
"%r is %s the maximum of %r" % (instance, cmp, maximum)
|
||||
)
|
||||
|
||||
|
||||
def properties_draft3(validator, properties, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for property, subschema in iteritems(properties):
|
||||
if property in instance:
|
||||
for error in validator.descend(
|
||||
instance[property],
|
||||
subschema,
|
||||
path=property,
|
||||
schema_path=property,
|
||||
):
|
||||
yield error
|
||||
elif subschema.get("required", False):
|
||||
error = ValidationError("%r is a required property" % 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:
|
||||
yield ValidationError(
|
||||
_utils.types_msg(instance, types), context=all_errors,
|
||||
)
|
155
venv/Lib/site-packages/jsonschema/_reflect.py
Normal file
155
venv/Lib/site-packages/jsonschema/_reflect.py
Normal file
|
@ -0,0 +1,155 @@
|
|||
# -*- test-case-name: twisted.test.test_reflect -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Standardized versions of various cool and/or strange things that you can do
|
||||
with Python's reflection capabilities.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from jsonschema.compat import PY3
|
||||
|
||||
|
||||
class _NoModuleFound(Exception):
|
||||
"""
|
||||
No module was found because none exists.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class InvalidName(ValueError):
|
||||
"""
|
||||
The given name is not a dot-separated list of Python objects.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class ModuleNotFound(InvalidName):
|
||||
"""
|
||||
The module associated with the given name doesn't exist and it can't be
|
||||
imported.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class ObjectNotFound(InvalidName):
|
||||
"""
|
||||
The object associated with the given name doesn't exist and it can't be
|
||||
imported.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
if PY3:
|
||||
def reraise(exception, traceback):
|
||||
raise exception.with_traceback(traceback)
|
||||
else:
|
||||
exec("""def reraise(exception, traceback):
|
||||
raise exception.__class__, exception, traceback""")
|
||||
|
||||
reraise.__doc__ = """
|
||||
Re-raise an exception, with an optional traceback, in a way that is compatible
|
||||
with both Python 2 and Python 3.
|
||||
|
||||
Note that on Python 3, re-raised exceptions will be mutated, with their
|
||||
C{__traceback__} attribute being set.
|
||||
|
||||
@param exception: The exception instance.
|
||||
@param traceback: The traceback to use, or C{None} indicating a new traceback.
|
||||
"""
|
||||
|
||||
|
||||
def _importAndCheckStack(importName):
|
||||
"""
|
||||
Import the given name as a module, then walk the stack to determine whether
|
||||
the failure was the module not existing, or some code in the module (for
|
||||
example a dependent import) failing. This can be helpful to determine
|
||||
whether any actual application code was run. For example, to distiguish
|
||||
administrative error (entering the wrong module name), from programmer
|
||||
error (writing buggy code in a module that fails to import).
|
||||
|
||||
@param importName: The name of the module to import.
|
||||
@type importName: C{str}
|
||||
@raise Exception: if something bad happens. This can be any type of
|
||||
exception, since nobody knows what loading some arbitrary code might
|
||||
do.
|
||||
@raise _NoModuleFound: if no module was found.
|
||||
"""
|
||||
try:
|
||||
return __import__(importName)
|
||||
except ImportError:
|
||||
excType, excValue, excTraceback = sys.exc_info()
|
||||
while excTraceback:
|
||||
execName = excTraceback.tb_frame.f_globals["__name__"]
|
||||
# in Python 2 execName is None when an ImportError is encountered,
|
||||
# where in Python 3 execName is equal to the importName.
|
||||
if execName is None or execName == importName:
|
||||
reraise(excValue, excTraceback)
|
||||
excTraceback = excTraceback.tb_next
|
||||
raise _NoModuleFound()
|
||||
|
||||
|
||||
|
||||
def namedAny(name):
|
||||
"""
|
||||
Retrieve a Python object by its fully qualified name from the global Python
|
||||
module namespace. The first part of the name, that describes a module,
|
||||
will be discovered and imported. Each subsequent part of the name is
|
||||
treated as the name of an attribute of the object specified by all of the
|
||||
name which came before it. For example, the fully-qualified name of this
|
||||
object is 'twisted.python.reflect.namedAny'.
|
||||
|
||||
@type name: L{str}
|
||||
@param name: The name of the object to return.
|
||||
|
||||
@raise InvalidName: If the name is an empty string, starts or ends with
|
||||
a '.', or is otherwise syntactically incorrect.
|
||||
|
||||
@raise ModuleNotFound: If the name is syntactically correct but the
|
||||
module it specifies cannot be imported because it does not appear to
|
||||
exist.
|
||||
|
||||
@raise ObjectNotFound: If the name is syntactically correct, includes at
|
||||
least one '.', but the module it specifies cannot be imported because
|
||||
it does not appear to exist.
|
||||
|
||||
@raise AttributeError: If an attribute of an object along the way cannot be
|
||||
accessed, or a module along the way is not found.
|
||||
|
||||
@return: the Python object identified by 'name'.
|
||||
"""
|
||||
if not name:
|
||||
raise InvalidName('Empty module name')
|
||||
|
||||
names = name.split('.')
|
||||
|
||||
# if the name starts or ends with a '.' or contains '..', the __import__
|
||||
# will raise an 'Empty module name' error. This will provide a better error
|
||||
# message.
|
||||
if '' in names:
|
||||
raise InvalidName(
|
||||
"name must be a string giving a '.'-separated list of Python "
|
||||
"identifiers, not %r" % (name,))
|
||||
|
||||
topLevelPackage = None
|
||||
moduleNames = names[:]
|
||||
while not topLevelPackage:
|
||||
if moduleNames:
|
||||
trialname = '.'.join(moduleNames)
|
||||
try:
|
||||
topLevelPackage = _importAndCheckStack(trialname)
|
||||
except _NoModuleFound:
|
||||
moduleNames.pop()
|
||||
else:
|
||||
if len(names) == 1:
|
||||
raise ModuleNotFound("No module named %r" % (name,))
|
||||
else:
|
||||
raise ObjectNotFound('%r does not name an object' % (name,))
|
||||
|
||||
obj = topLevelPackage
|
||||
for n in names[1:]:
|
||||
obj = getattr(obj, n)
|
||||
|
||||
return obj
|
188
venv/Lib/site-packages/jsonschema/_types.py
Normal file
188
venv/Lib/site-packages/jsonschema/_types.py
Normal file
|
@ -0,0 +1,188 @@
|
|||
import numbers
|
||||
|
||||
from pyrsistent import pmap
|
||||
import attr
|
||||
|
||||
from jsonschema.compat import int_types, str_types
|
||||
from jsonschema.exceptions import UndefinedTypeCheck
|
||||
|
||||
|
||||
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_types)
|
||||
|
||||
|
||||
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_types)
|
||||
|
||||
|
||||
def is_any(checker, instance):
|
||||
return True
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class TypeChecker(object):
|
||||
"""
|
||||
A ``type`` property checker.
|
||||
|
||||
A `TypeChecker` performs type checking for an `IValidator`. Type
|
||||
checks to perform are updated using `TypeChecker.redefine` or
|
||||
`TypeChecker.redefine_many` and removed via `TypeChecker.remove`.
|
||||
Each of these return a new `TypeChecker` object.
|
||||
|
||||
Arguments:
|
||||
|
||||
type_checkers (dict):
|
||||
|
||||
The initial mapping of types to their checking functions.
|
||||
"""
|
||||
_type_checkers = attr.ib(default=pmap(), converter=pmap)
|
||||
|
||||
def is_type(self, instance, type):
|
||||
"""
|
||||
Check if the instance is of the appropriate type.
|
||||
|
||||
Arguments:
|
||||
|
||||
instance (object):
|
||||
|
||||
The instance to check
|
||||
|
||||
type (str):
|
||||
|
||||
The name of the type that is expected.
|
||||
|
||||
Returns:
|
||||
|
||||
bool: Whether it conformed.
|
||||
|
||||
|
||||
Raises:
|
||||
|
||||
`jsonschema.exceptions.UndefinedTypeCheck`:
|
||||
if type is unknown to this object.
|
||||
"""
|
||||
try:
|
||||
fn = self._type_checkers[type]
|
||||
except KeyError:
|
||||
raise UndefinedTypeCheck(type)
|
||||
|
||||
return fn(self, instance)
|
||||
|
||||
def redefine(self, type, fn):
|
||||
"""
|
||||
Produce a new checker with the given type redefined.
|
||||
|
||||
Arguments:
|
||||
|
||||
type (str):
|
||||
|
||||
The name of the type to check.
|
||||
|
||||
fn (collections.Callable):
|
||||
|
||||
A function 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.
|
||||
|
||||
Returns:
|
||||
|
||||
A new `TypeChecker` instance.
|
||||
"""
|
||||
return self.redefine_many({type: fn})
|
||||
|
||||
def redefine_many(self, definitions=()):
|
||||
"""
|
||||
Produce a new checker with the given types redefined.
|
||||
|
||||
Arguments:
|
||||
|
||||
definitions (dict):
|
||||
|
||||
A dictionary mapping types to their checking functions.
|
||||
|
||||
Returns:
|
||||
|
||||
A new `TypeChecker` instance.
|
||||
"""
|
||||
return attr.evolve(
|
||||
self, type_checkers=self._type_checkers.update(definitions),
|
||||
)
|
||||
|
||||
def remove(self, *types):
|
||||
"""
|
||||
Produce a new checker with the given types forgotten.
|
||||
|
||||
Arguments:
|
||||
|
||||
types (~collections.Iterable):
|
||||
|
||||
the names of the types to remove.
|
||||
|
||||
Returns:
|
||||
|
||||
A new `TypeChecker` instance
|
||||
|
||||
Raises:
|
||||
|
||||
`jsonschema.exceptions.UndefinedTypeCheck`:
|
||||
|
||||
if any given type is unknown to this object
|
||||
"""
|
||||
|
||||
checkers = self._type_checkers
|
||||
for each in types:
|
||||
try:
|
||||
checkers = checkers.remove(each)
|
||||
except KeyError:
|
||||
raise UndefinedTypeCheck(each)
|
||||
return attr.evolve(self, type_checkers=checkers)
|
||||
|
||||
|
||||
draft3_type_checker = TypeChecker(
|
||||
{
|
||||
u"any": is_any,
|
||||
u"array": is_array,
|
||||
u"boolean": is_bool,
|
||||
u"integer": is_integer,
|
||||
u"object": is_object,
|
||||
u"null": is_null,
|
||||
u"number": is_number,
|
||||
u"string": is_string,
|
||||
},
|
||||
)
|
||||
draft4_type_checker = draft3_type_checker.remove(u"any")
|
||||
draft6_type_checker = draft4_type_checker.redefine(
|
||||
u"integer",
|
||||
lambda checker, instance: (
|
||||
is_integer(checker, instance) or
|
||||
isinstance(instance, float) and instance.is_integer()
|
||||
),
|
||||
)
|
||||
draft7_type_checker = draft6_type_checker
|
212
venv/Lib/site-packages/jsonschema/_utils.py
Normal file
212
venv/Lib/site-packages/jsonschema/_utils.py
Normal file
|
@ -0,0 +1,212 @@
|
|||
import itertools
|
||||
import json
|
||||
import pkgutil
|
||||
import re
|
||||
|
||||
from jsonschema.compat import MutableMapping, str_types, urlsplit
|
||||
|
||||
|
||||
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(object):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
data = pkgutil.get_data("jsonschema", "schemas/{0}.json".format(name))
|
||||
return json.loads(data.decode("utf-8"))
|
||||
|
||||
|
||||
def indent(string, times=1):
|
||||
"""
|
||||
A dumb version of `textwrap.indent` from Python 3.3.
|
||||
"""
|
||||
|
||||
return "\n".join(" " * (4 * times) + line for line in string.splitlines())
|
||||
|
||||
|
||||
def format_as_index(indices):
|
||||
"""
|
||||
Construct a single string containing indexing operations for the indices.
|
||||
|
||||
For example, [1, 2, "foo"] -> [1][2]["foo"]
|
||||
|
||||
Arguments:
|
||||
|
||||
indices (sequence):
|
||||
|
||||
The indices to format.
|
||||
"""
|
||||
|
||||
if not indices:
|
||||
return ""
|
||||
return "[%s]" % "][".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 extras), verb
|
||||
|
||||
|
||||
def types_msg(instance, types):
|
||||
"""
|
||||
Create an error message for a failure to match the given types.
|
||||
|
||||
If the ``instance`` is an object and contains a ``name`` property, it will
|
||||
be considered to be a description of that object and used as its type.
|
||||
|
||||
Otherwise the message is simply the reprs of the given ``types``.
|
||||
"""
|
||||
|
||||
reprs = []
|
||||
for type in types:
|
||||
try:
|
||||
reprs.append(repr(type["name"]))
|
||||
except Exception:
|
||||
reprs.append(repr(type))
|
||||
return "%r is not of type %s" % (instance, ", ".join(reprs))
|
||||
|
||||
|
||||
def flatten(suitable_for_isinstance):
|
||||
"""
|
||||
isinstance() can accept a bunch of really annoying different types:
|
||||
* a single type
|
||||
* a tuple of types
|
||||
* an arbitrary nested tree of tuples
|
||||
|
||||
Return a flattened tuple of the given argument.
|
||||
"""
|
||||
|
||||
types = set()
|
||||
|
||||
if not isinstance(suitable_for_isinstance, tuple):
|
||||
suitable_for_isinstance = (suitable_for_isinstance,)
|
||||
for thing in suitable_for_isinstance:
|
||||
if isinstance(thing, tuple):
|
||||
types.update(flatten(thing))
|
||||
else:
|
||||
types.add(thing)
|
||||
return tuple(types)
|
||||
|
||||
|
||||
def ensure_list(thing):
|
||||
"""
|
||||
Wrap ``thing`` in a list if it's a single str.
|
||||
|
||||
Otherwise, return it unchanged.
|
||||
"""
|
||||
|
||||
if isinstance(thing, str_types):
|
||||
return [thing]
|
||||
return thing
|
||||
|
||||
|
||||
def equal(one, two):
|
||||
"""
|
||||
Check if two things are equal, but evade booleans and ints being equal.
|
||||
"""
|
||||
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.
|
||||
|
||||
Successively tries first to rely that the elements are hashable, then
|
||||
falls back on them being sortable, and finally falls back on brute
|
||||
force.
|
||||
"""
|
||||
|
||||
try:
|
||||
return len(set(unbool(i) for i in container)) == len(container)
|
||||
except TypeError:
|
||||
try:
|
||||
sort = sorted(unbool(i) for i in container)
|
||||
sliced = itertools.islice(sort, 1, None)
|
||||
for i, j in zip(sort, sliced):
|
||||
if i == j:
|
||||
return False
|
||||
except (NotImplementedError, TypeError):
|
||||
seen = []
|
||||
for e in container:
|
||||
e = unbool(e)
|
||||
if e in seen:
|
||||
return False
|
||||
seen.append(e)
|
||||
return True
|
373
venv/Lib/site-packages/jsonschema/_validators.py
Normal file
373
venv/Lib/site-packages/jsonschema/_validators.py
Normal file
|
@ -0,0 +1,373 @@
|
|||
import re
|
||||
|
||||
from jsonschema._utils import (
|
||||
ensure_list,
|
||||
equal,
|
||||
extras_msg,
|
||||
find_additional_properties,
|
||||
types_msg,
|
||||
unbool,
|
||||
uniq,
|
||||
)
|
||||
from jsonschema.exceptions import FormatError, ValidationError
|
||||
from jsonschema.compat import iteritems
|
||||
|
||||
|
||||
def patternProperties(validator, patternProperties, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for pattern, subschema in iteritems(patternProperties):
|
||||
for k, v in iteritems(instance):
|
||||
if re.search(pattern, k):
|
||||
for error in validator.descend(
|
||||
v, subschema, path=k, schema_path=pattern,
|
||||
):
|
||||
yield error
|
||||
|
||||
|
||||
def propertyNames(validator, propertyNames, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for property in instance:
|
||||
for error in validator.descend(
|
||||
instance=property,
|
||||
schema=propertyNames,
|
||||
):
|
||||
yield error
|
||||
|
||||
|
||||
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:
|
||||
for error in validator.descend(instance[extra], aP, path=extra):
|
||||
yield error
|
||||
elif not aP and extras:
|
||||
if "patternProperties" in schema:
|
||||
patterns = sorted(schema["patternProperties"])
|
||||
if len(extras) == 1:
|
||||
verb = "does"
|
||||
else:
|
||||
verb = "do"
|
||||
error = "%s %s not match any of the regexes: %s" % (
|
||||
", ".join(map(repr, sorted(extras))),
|
||||
verb,
|
||||
", ".join(map(repr, 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
|
||||
|
||||
if validator.is_type(items, "array"):
|
||||
for (index, item), subschema in zip(enumerate(instance), items):
|
||||
for error in validator.descend(
|
||||
item, subschema, path=index, schema_path=index,
|
||||
):
|
||||
yield error
|
||||
else:
|
||||
for index, item in enumerate(instance):
|
||||
for error in validator.descend(item, items, path=index):
|
||||
yield error
|
||||
|
||||
|
||||
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):
|
||||
for error in validator.descend(item, aI, path=index):
|
||||
yield error
|
||||
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("%r was expected" % (const,))
|
||||
|
||||
|
||||
def contains(validator, contains, instance, schema):
|
||||
if not validator.is_type(instance, "array"):
|
||||
return
|
||||
|
||||
if not any(validator.is_valid(element, contains) for element in instance):
|
||||
yield ValidationError(
|
||||
"None of %r are valid under the given schema" % (instance,)
|
||||
)
|
||||
|
||||
|
||||
def exclusiveMinimum(validator, minimum, instance, schema):
|
||||
if not validator.is_type(instance, "number"):
|
||||
return
|
||||
|
||||
if instance <= minimum:
|
||||
yield ValidationError(
|
||||
"%r is less than or equal to the minimum of %r" % (
|
||||
instance, minimum,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def exclusiveMaximum(validator, maximum, instance, schema):
|
||||
if not validator.is_type(instance, "number"):
|
||||
return
|
||||
|
||||
if instance >= maximum:
|
||||
yield ValidationError(
|
||||
"%r is greater than or equal to the maximum of %r" % (
|
||||
instance, maximum,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def minimum(validator, minimum, instance, schema):
|
||||
if not validator.is_type(instance, "number"):
|
||||
return
|
||||
|
||||
if instance < minimum:
|
||||
yield ValidationError(
|
||||
"%r is less than the minimum of %r" % (instance, minimum)
|
||||
)
|
||||
|
||||
|
||||
def maximum(validator, maximum, instance, schema):
|
||||
if not validator.is_type(instance, "number"):
|
||||
return
|
||||
|
||||
if instance > maximum:
|
||||
yield ValidationError(
|
||||
"%r is greater than the maximum of %r" % (instance, maximum)
|
||||
)
|
||||
|
||||
|
||||
def multipleOf(validator, dB, instance, schema):
|
||||
if not validator.is_type(instance, "number"):
|
||||
return
|
||||
|
||||
if isinstance(dB, float):
|
||||
quotient = instance / dB
|
||||
failed = int(quotient) != quotient
|
||||
else:
|
||||
failed = instance % dB
|
||||
|
||||
if failed:
|
||||
yield ValidationError("%r is not a multiple of %r" % (instance, dB))
|
||||
|
||||
|
||||
def minItems(validator, mI, instance, schema):
|
||||
if validator.is_type(instance, "array") and len(instance) < mI:
|
||||
yield ValidationError("%r is too short" % (instance,))
|
||||
|
||||
|
||||
def maxItems(validator, mI, instance, schema):
|
||||
if validator.is_type(instance, "array") and len(instance) > mI:
|
||||
yield ValidationError("%r is too long" % (instance,))
|
||||
|
||||
|
||||
def uniqueItems(validator, uI, instance, schema):
|
||||
if (
|
||||
uI and
|
||||
validator.is_type(instance, "array") and
|
||||
not uniq(instance)
|
||||
):
|
||||
yield ValidationError("%r has non-unique elements" % (instance,))
|
||||
|
||||
|
||||
def pattern(validator, patrn, instance, schema):
|
||||
if (
|
||||
validator.is_type(instance, "string") and
|
||||
not re.search(patrn, instance)
|
||||
):
|
||||
yield ValidationError("%r does not match %r" % (instance, patrn))
|
||||
|
||||
|
||||
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("%r is too short" % (instance,))
|
||||
|
||||
|
||||
def maxLength(validator, mL, instance, schema):
|
||||
if validator.is_type(instance, "string") and len(instance) > mL:
|
||||
yield ValidationError("%r is too long" % (instance,))
|
||||
|
||||
|
||||
def dependencies(validator, dependencies, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for property, dependency in iteritems(dependencies):
|
||||
if property not in instance:
|
||||
continue
|
||||
|
||||
if validator.is_type(dependency, "array"):
|
||||
for each in dependency:
|
||||
if each not in instance:
|
||||
message = "%r is a dependency of %r"
|
||||
yield ValidationError(message % (each, property))
|
||||
else:
|
||||
for error in validator.descend(
|
||||
instance, dependency, schema_path=property,
|
||||
):
|
||||
yield error
|
||||
|
||||
|
||||
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("%r is not one of %r" % (instance, enums))
|
||||
elif instance not in enums:
|
||||
yield ValidationError("%r is not one of %r" % (instance, enums))
|
||||
|
||||
|
||||
def ref(validator, ref, instance, schema):
|
||||
resolve = getattr(validator.resolver, "resolve", None)
|
||||
if resolve is None:
|
||||
with validator.resolver.resolving(ref) as resolved:
|
||||
for error in validator.descend(instance, resolved):
|
||||
yield error
|
||||
else:
|
||||
scope, resolved = validator.resolver.resolve(ref)
|
||||
validator.resolver.push_scope(scope)
|
||||
|
||||
try:
|
||||
for error in validator.descend(instance, resolved):
|
||||
yield error
|
||||
finally:
|
||||
validator.resolver.pop_scope()
|
||||
|
||||
|
||||
def type(validator, types, instance, schema):
|
||||
types = ensure_list(types)
|
||||
|
||||
if not any(validator.is_type(instance, type) for type in types):
|
||||
yield ValidationError(types_msg(instance, types))
|
||||
|
||||
|
||||
def properties(validator, properties, instance, schema):
|
||||
if not validator.is_type(instance, "object"):
|
||||
return
|
||||
|
||||
for property, subschema in iteritems(properties):
|
||||
if property in instance:
|
||||
for error in validator.descend(
|
||||
instance[property],
|
||||
subschema,
|
||||
path=property,
|
||||
schema_path=property,
|
||||
):
|
||||
yield error
|
||||
|
||||
|
||||
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("%r is a required property" % property)
|
||||
|
||||
|
||||
def minProperties(validator, mP, instance, schema):
|
||||
if validator.is_type(instance, "object") and len(instance) < mP:
|
||||
yield ValidationError(
|
||||
"%r does not have enough properties" % (instance,)
|
||||
)
|
||||
|
||||
|
||||
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("%r has too many properties" % (instance,))
|
||||
|
||||
|
||||
def allOf(validator, allOf, instance, schema):
|
||||
for index, subschema in enumerate(allOf):
|
||||
for error in validator.descend(instance, subschema, schema_path=index):
|
||||
yield error
|
||||
|
||||
|
||||
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(
|
||||
"%r is not valid under any of the given schemas" % (instance,),
|
||||
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(
|
||||
"%r is not valid under any of the given schemas" % (instance,),
|
||||
context=all_errors,
|
||||
)
|
||||
|
||||
more_valid = [s for i, s in subschemas if validator.is_valid(instance, s)]
|
||||
if more_valid:
|
||||
more_valid.append(first_valid)
|
||||
reprs = ", ".join(repr(schema) for schema in more_valid)
|
||||
yield ValidationError(
|
||||
"%r is valid under each of %s" % (instance, reprs)
|
||||
)
|
||||
|
||||
|
||||
def not_(validator, not_schema, instance, schema):
|
||||
if validator.is_valid(instance, not_schema):
|
||||
yield ValidationError(
|
||||
"%r is not allowed for %r" % (not_schema, instance)
|
||||
)
|
||||
|
||||
|
||||
def if_(validator, if_schema, instance, schema):
|
||||
if validator.is_valid(instance, if_schema):
|
||||
if u"then" in schema:
|
||||
then = schema[u"then"]
|
||||
for error in validator.descend(instance, then, schema_path="then"):
|
||||
yield error
|
||||
elif u"else" in schema:
|
||||
else_ = schema[u"else"]
|
||||
for error in validator.descend(instance, else_, schema_path="else"):
|
||||
yield error
|
5
venv/Lib/site-packages/jsonschema/benchmarks/__init__.py
Normal file
5
venv/Lib/site-packages/jsonschema/benchmarks/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
"""
|
||||
Benchmarks for validation.
|
||||
|
||||
This package is *not* public API.
|
||||
"""
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
26
venv/Lib/site-packages/jsonschema/benchmarks/issue232.py
Normal file
26
venv/Lib/site-packages/jsonschema/benchmarks/issue232.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
A performance benchmark using the example from issue #232.
|
||||
|
||||
See https://github.com/Julian/jsonschema/pull/232.
|
||||
"""
|
||||
from twisted.python.filepath import FilePath
|
||||
from pyperf import Runner
|
||||
from pyrsistent import m
|
||||
|
||||
from jsonschema.tests._suite import Version
|
||||
import jsonschema
|
||||
|
||||
|
||||
issue232 = Version(
|
||||
path=FilePath(__file__).sibling("issue232"),
|
||||
remotes=m(),
|
||||
name="issue232",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
issue232.benchmark(
|
||||
runner=Runner(),
|
||||
Validator=jsonschema.Draft4Validator,
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
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())
|
90
venv/Lib/site-packages/jsonschema/cli.py
Normal file
90
venv/Lib/site-packages/jsonschema/cli.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
The ``jsonschema`` command line.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
from jsonschema import __version__
|
||||
from jsonschema._reflect import namedAny
|
||||
from jsonschema.validators import validator_for
|
||||
|
||||
|
||||
def _namedAnyWithDefault(name):
|
||||
if "." not in name:
|
||||
name = "jsonschema." + name
|
||||
return namedAny(name)
|
||||
|
||||
|
||||
def _json_file(path):
|
||||
with open(path) as file:
|
||||
return json.load(file)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="JSON Schema Validation CLI",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i", "--instance",
|
||||
action="append",
|
||||
dest="instances",
|
||||
type=_json_file,
|
||||
help=(
|
||||
"a path to a JSON instance (i.e. filename.json) "
|
||||
"to validate (may be specified multiple times)"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-F", "--error-format",
|
||||
default="{error.instance}: {error.message}\n",
|
||||
help=(
|
||||
"the format to use for each error output message, specified in "
|
||||
"a form suitable for passing to str.format, which will be called "
|
||||
"with 'error' for each error"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-V", "--validator",
|
||||
type=_namedAnyWithDefault,
|
||||
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(
|
||||
"--version",
|
||||
action="version",
|
||||
version=__version__,
|
||||
)
|
||||
parser.add_argument(
|
||||
"schema",
|
||||
help="the JSON Schema to validate with (i.e. schema.json)",
|
||||
type=_json_file,
|
||||
)
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
arguments = vars(parser.parse_args(args=args or ["--help"]))
|
||||
if arguments["validator"] is None:
|
||||
arguments["validator"] = validator_for(arguments["schema"])
|
||||
return arguments
|
||||
|
||||
|
||||
def main(args=sys.argv[1:]):
|
||||
sys.exit(run(arguments=parse_args(args=args)))
|
||||
|
||||
|
||||
def run(arguments, stdout=sys.stdout, stderr=sys.stderr):
|
||||
error_format = arguments["error_format"]
|
||||
validator = arguments["validator"](schema=arguments["schema"])
|
||||
|
||||
validator.check_schema(arguments["schema"])
|
||||
|
||||
errored = False
|
||||
for instance in arguments["instances"] or ():
|
||||
for error in validator.iter_errors(instance):
|
||||
stderr.write(error_format.format(error=error))
|
||||
errored = True
|
||||
return errored
|
55
venv/Lib/site-packages/jsonschema/compat.py
Normal file
55
venv/Lib/site-packages/jsonschema/compat.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""
|
||||
Python 2/3 compatibility helpers.
|
||||
|
||||
Note: This module is *not* public API.
|
||||
"""
|
||||
import contextlib
|
||||
import operator
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
from collections.abc import MutableMapping, Sequence # noqa
|
||||
except ImportError:
|
||||
from collections import MutableMapping, Sequence # noqa
|
||||
|
||||
PY3 = sys.version_info[0] >= 3
|
||||
|
||||
if PY3:
|
||||
zip = zip
|
||||
from functools import lru_cache
|
||||
from io import StringIO as NativeIO
|
||||
from urllib.parse import (
|
||||
unquote, urljoin, urlunsplit, SplitResult, urlsplit
|
||||
)
|
||||
from urllib.request import pathname2url, urlopen
|
||||
str_types = str,
|
||||
int_types = int,
|
||||
iteritems = operator.methodcaller("items")
|
||||
else:
|
||||
from itertools import izip as zip # noqa
|
||||
from io import BytesIO as NativeIO
|
||||
from urlparse import urljoin, urlunsplit, SplitResult, urlsplit
|
||||
from urllib import pathname2url, unquote # noqa
|
||||
import urllib2 # noqa
|
||||
def urlopen(*args, **kwargs):
|
||||
return contextlib.closing(urllib2.urlopen(*args, **kwargs))
|
||||
|
||||
str_types = basestring
|
||||
int_types = int, long
|
||||
iteritems = operator.methodcaller("iteritems")
|
||||
|
||||
from functools32 import lru_cache
|
||||
|
||||
|
||||
def urldefrag(url):
|
||||
if "#" in url:
|
||||
s, n, p, q, frag = urlsplit(url)
|
||||
defrag = urlunsplit((s, n, p, q, ""))
|
||||
else:
|
||||
defrag = url
|
||||
frag = ""
|
||||
return defrag, frag
|
||||
|
||||
|
||||
# flake8: noqa
|
374
venv/Lib/site-packages/jsonschema/exceptions.py
Normal file
374
venv/Lib/site-packages/jsonschema/exceptions.py
Normal file
|
@ -0,0 +1,374 @@
|
|||
"""
|
||||
Validation errors, and some surrounding helpers.
|
||||
"""
|
||||
from collections import defaultdict, deque
|
||||
import itertools
|
||||
import pprint
|
||||
import textwrap
|
||||
|
||||
import attr
|
||||
|
||||
from jsonschema import _utils
|
||||
from jsonschema.compat import PY3, iteritems
|
||||
|
||||
|
||||
WEAK_MATCHES = frozenset(["anyOf", "oneOf"])
|
||||
STRONG_MATCHES = 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,
|
||||
):
|
||||
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
|
||||
|
||||
for error in context:
|
||||
error.parent = self
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %r>" % (self.__class__.__name__, self.message)
|
||||
|
||||
def __unicode__(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
|
||||
|
||||
pschema = pprint.pformat(self.schema, width=72)
|
||||
pinstance = pprint.pformat(self.instance, width=72)
|
||||
return self.message + textwrap.dedent("""
|
||||
|
||||
Failed validating %r in %s%s:
|
||||
%s
|
||||
|
||||
On %s%s:
|
||||
%s
|
||||
""".rstrip()
|
||||
) % (
|
||||
self.validator,
|
||||
self._word_for_schema_in_error_message,
|
||||
_utils.format_as_index(list(self.relative_schema_path)[:-1]),
|
||||
_utils.indent(pschema),
|
||||
self._word_for_instance_in_error_message,
|
||||
_utils.format_as_index(self.relative_path),
|
||||
_utils.indent(pinstance),
|
||||
)
|
||||
|
||||
if PY3:
|
||||
__str__ = __unicode__
|
||||
else:
|
||||
def __str__(self):
|
||||
return unicode(self).encode("utf-8")
|
||||
|
||||
@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
|
||||
|
||||
def _set(self, **kwargs):
|
||||
for k, v in iteritems(kwargs):
|
||||
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)
|
||||
|
||||
|
||||
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 __unicode__(self):
|
||||
return "Type %r is unknown to this type checker" % self.type
|
||||
|
||||
if PY3:
|
||||
__str__ = __unicode__
|
||||
else:
|
||||
def __str__(self):
|
||||
return unicode(self).encode("utf-8")
|
||||
|
||||
|
||||
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 __unicode__(self):
|
||||
pschema = pprint.pformat(self.schema, width=72)
|
||||
pinstance = pprint.pformat(self.instance, width=72)
|
||||
return textwrap.dedent("""
|
||||
Unknown type %r for validator with schema:
|
||||
%s
|
||||
|
||||
While checking instance:
|
||||
%s
|
||||
""".rstrip()
|
||||
) % (self.type, _utils.indent(pschema), _utils.indent(pinstance))
|
||||
|
||||
if PY3:
|
||||
__str__ = __unicode__
|
||||
else:
|
||||
def __str__(self):
|
||||
return unicode(self).encode("utf-8")
|
||||
|
||||
|
||||
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 __unicode__(self):
|
||||
return self.message
|
||||
|
||||
if PY3:
|
||||
__str__ = __unicode__
|
||||
else:
|
||||
def __str__(self):
|
||||
return self.message.encode("utf-8")
|
||||
|
||||
|
||||
class ErrorTree(object):
|
||||
"""
|
||||
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 `exceptions.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):
|
||||
return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
|
||||
|
||||
@property
|
||||
def total_errors(self):
|
||||
"""
|
||||
The total number of errors in the entire tree, including children.
|
||||
"""
|
||||
|
||||
child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
|
||||
return len(self.errors) + child_errors
|
||||
|
||||
|
||||
def by_relevance(weak=WEAK_MATCHES, strong=STRONG_MATCHES):
|
||||
"""
|
||||
Create a key function that can be used to sort errors by relevance.
|
||||
|
||||
Arguments:
|
||||
weak (set):
|
||||
a collection of validator names to consider to be "weak".
|
||||
If there are two errors at the same level of the instance
|
||||
and one is in the set of weak validator names, the other
|
||||
error will take priority. By default, :validator:`anyOf` and
|
||||
:validator:`oneOf` are considered weak validators and will
|
||||
be superseded by other same-level validation errors.
|
||||
|
||||
strong (set):
|
||||
a collection of validator names to consider to be "strong"
|
||||
"""
|
||||
def relevance(error):
|
||||
validator = error.validator
|
||||
return -len(error.path), validator not in weak, validator in strong
|
||||
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 :validator:`oneOf` or :validator:`anyOf`,
|
||||
the *opposite* assumption is made -- i.e. the deepest error is picked,
|
||||
since these validators only need to match once, and any other errors may
|
||||
not be relevant.
|
||||
|
||||
Arguments:
|
||||
errors (collections.Iterable):
|
||||
|
||||
the errors to select from. Do not provide a mixture of
|
||||
errors from different validation attempts (i.e. from
|
||||
different instances or schemas), since it won't produce
|
||||
sensical output.
|
||||
|
||||
key (collections.Callable):
|
||||
|
||||
the key to use when sorting errors. See `relevance` and
|
||||
transitively `by_relevance` for more details (the default is
|
||||
to sort with the defaults of that function). Changing the
|
||||
default is only useful if you want to change the function
|
||||
that rates errors but still want the error context descent
|
||||
done by this function.
|
||||
|
||||
Returns:
|
||||
the best matching error, or ``None`` if the iterable was empty
|
||||
|
||||
.. note::
|
||||
|
||||
This function is a heuristic. Its return value may change for a given
|
||||
set of inputs from version to version if better heuristics are added.
|
||||
"""
|
||||
errors = iter(errors)
|
||||
best = next(errors, None)
|
||||
if best is None:
|
||||
return
|
||||
best = max(itertools.chain([best], errors), key=key)
|
||||
|
||||
while best.context:
|
||||
best = min(best.context, key=key)
|
||||
return best
|
199
venv/Lib/site-packages/jsonschema/schemas/draft3.json
Normal file
199
venv/Lib/site-packages/jsonschema/schemas/draft3.json
Normal file
|
@ -0,0 +1,199 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-03/schema#",
|
||||
"dependencies": {
|
||||
"exclusiveMaximum": "maximum",
|
||||
"exclusiveMinimum": "minimum"
|
||||
},
|
||||
"id": "http://json-schema.org/draft-03/schema#",
|
||||
"properties": {
|
||||
"$ref": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"$schema": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"additionalItems": {
|
||||
"default": {},
|
||||
"type": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
"boolean"
|
||||
]
|
||||
},
|
||||
"additionalProperties": {
|
||||
"default": {},
|
||||
"type": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
"boolean"
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"type": "any"
|
||||
},
|
||||
"dependencies": {
|
||||
"additionalProperties": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": [
|
||||
"string",
|
||||
"array",
|
||||
{
|
||||
"$ref": "#"
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {},
|
||||
"type": [
|
||||
"string",
|
||||
"array",
|
||||
"object"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"disallow": {
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
{
|
||||
"$ref": "#"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"uniqueItems": true
|
||||
},
|
||||
"divisibleBy": {
|
||||
"default": 1,
|
||||
"exclusiveMinimum": true,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"enum": {
|
||||
"type": "array"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"extends": {
|
||||
"default": {},
|
||||
"items": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
"array"
|
||||
]
|
||||
},
|
||||
"format": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"default": {},
|
||||
"items": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"type": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
"array"
|
||||
]
|
||||
},
|
||||
"maxDecimal": {
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"maxItems": {
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"maxLength": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": {
|
||||
"default": 0,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"minLength": {
|
||||
"default": 0,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"pattern": {
|
||||
"format": "regex",
|
||||
"type": "string"
|
||||
},
|
||||
"patternProperties": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"default": {},
|
||||
"type": "object"
|
||||
},
|
||||
"properties": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#",
|
||||
"type": "object"
|
||||
},
|
||||
"default": {},
|
||||
"type": "object"
|
||||
},
|
||||
"required": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"default": "any",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
{
|
||||
"$ref": "#"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"uniqueItems": true
|
||||
},
|
||||
"uniqueItems": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
222
venv/Lib/site-packages/jsonschema/schemas/draft4.json
Normal file
222
venv/Lib/site-packages/jsonschema/schemas/draft4.json
Normal file
|
@ -0,0 +1,222 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"default": {},
|
||||
"definitions": {
|
||||
"positiveInteger": {
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"positiveIntegerDefault0": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/positiveInteger"
|
||||
},
|
||||
{
|
||||
"default": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemaArray": {
|
||||
"items": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"simpleTypes": {
|
||||
"enum": [
|
||||
"array",
|
||||
"boolean",
|
||||
"integer",
|
||||
"null",
|
||||
"number",
|
||||
"object",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"stringArray": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"exclusiveMaximum": [
|
||||
"maximum"
|
||||
],
|
||||
"exclusiveMinimum": [
|
||||
"minimum"
|
||||
]
|
||||
},
|
||||
"description": "Core schema meta-schema",
|
||||
"id": "http://json-schema.org/draft-04/schema#",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"additionalItems": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"$ref": "#"
|
||||
}
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"$ref": "#"
|
||||
}
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"allOf": {
|
||||
"$ref": "#/definitions/schemaArray"
|
||||
},
|
||||
"anyOf": {
|
||||
"$ref": "#/definitions/schemaArray"
|
||||
},
|
||||
"default": {},
|
||||
"definitions": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"default": {},
|
||||
"type": "object"
|
||||
},
|
||||
"dependencies": {
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/stringArray"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"enum": {
|
||||
"type": "array"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"format": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/schemaArray"
|
||||
}
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"maxItems": {
|
||||
"$ref": "#/definitions/positiveInteger"
|
||||
},
|
||||
"maxLength": {
|
||||
"$ref": "#/definitions/positiveInteger"
|
||||
},
|
||||
"maxProperties": {
|
||||
"$ref": "#/definitions/positiveInteger"
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": {
|
||||
"$ref": "#/definitions/positiveIntegerDefault0"
|
||||
},
|
||||
"minLength": {
|
||||
"$ref": "#/definitions/positiveIntegerDefault0"
|
||||
},
|
||||
"minProperties": {
|
||||
"$ref": "#/definitions/positiveIntegerDefault0"
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"multipleOf": {
|
||||
"exclusiveMinimum": true,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"not": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"oneOf": {
|
||||
"$ref": "#/definitions/schemaArray"
|
||||
},
|
||||
"pattern": {
|
||||
"format": "regex",
|
||||
"type": "string"
|
||||
},
|
||||
"patternProperties": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"default": {},
|
||||
"type": "object"
|
||||
},
|
||||
"properties": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#"
|
||||
},
|
||||
"default": {},
|
||||
"type": "object"
|
||||
},
|
||||
"required": {
|
||||
"$ref": "#/definitions/stringArray"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/simpleTypes"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/definitions/simpleTypes"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
153
venv/Lib/site-packages/jsonschema/schemas/draft6.json
Normal file
153
venv/Lib/site-packages/jsonschema/schemas/draft6.json
Normal file
|
@ -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": {}
|
||||
}
|
166
venv/Lib/site-packages/jsonschema/schemas/draft7.json
Normal file
166
venv/Lib/site-packages/jsonschema/schemas/draft7.json
Normal file
|
@ -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
|
||||
}
|
0
venv/Lib/site-packages/jsonschema/tests/__init__.py
Normal file
0
venv/Lib/site-packages/jsonschema/tests/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
venv/Lib/site-packages/jsonschema/tests/_helpers.py
Normal file
5
venv/Lib/site-packages/jsonschema/tests/_helpers.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
def bug(issue=None):
|
||||
message = "A known bug."
|
||||
if issue is not None:
|
||||
message += " See issue #{issue}.".format(issue=issue)
|
||||
return message
|
239
venv/Lib/site-packages/jsonschema/tests/_suite.py
Normal file
239
venv/Lib/site-packages/jsonschema/tests/_suite.py
Normal file
|
@ -0,0 +1,239 @@
|
|||
"""
|
||||
Python representations of the JSON Schema Test Suite tests.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from twisted.python.filepath import FilePath
|
||||
import attr
|
||||
|
||||
from jsonschema.compat import PY3
|
||||
from jsonschema.validators import validators
|
||||
import jsonschema
|
||||
|
||||
|
||||
def _find_suite():
|
||||
root = os.environ.get("JSON_SCHEMA_TEST_SUITE")
|
||||
if root is not None:
|
||||
return FilePath(root)
|
||||
|
||||
root = FilePath(jsonschema.__file__).parent().sibling("json")
|
||||
if not root.isdir(): # pragma: no cover
|
||||
raise ValueError(
|
||||
(
|
||||
"Can't find the JSON-Schema-Test-Suite directory. "
|
||||
"Set the 'JSON_SCHEMA_TEST_SUITE' environment "
|
||||
"variable or run the tests from alongside a checkout "
|
||||
"of the suite."
|
||||
),
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class Suite(object):
|
||||
|
||||
_root = attr.ib(default=attr.Factory(_find_suite))
|
||||
|
||||
def _remotes(self):
|
||||
jsonschema_suite = self._root.descendant(["bin", "jsonschema_suite"])
|
||||
remotes = subprocess.check_output(
|
||||
[sys.executable, jsonschema_suite.path, "remotes"],
|
||||
)
|
||||
return {
|
||||
"http://localhost:1234/" + name: schema
|
||||
for name, schema in json.loads(remotes.decode("utf-8")).items()
|
||||
}
|
||||
|
||||
def benchmark(self, runner): # pragma: no cover
|
||||
for name in validators:
|
||||
self.version(name=name).benchmark(runner=runner)
|
||||
|
||||
def version(self, name):
|
||||
return Version(
|
||||
name=name,
|
||||
path=self._root.descendant(["tests", name]),
|
||||
remotes=self._remotes(),
|
||||
)
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class Version(object):
|
||||
|
||||
_path = attr.ib()
|
||||
_remotes = attr.ib()
|
||||
|
||||
name = attr.ib()
|
||||
|
||||
def benchmark(self, runner, **kwargs): # pragma: no cover
|
||||
for suite in self.tests():
|
||||
for test in suite:
|
||||
runner.bench_func(
|
||||
test.fully_qualified_name,
|
||||
partial(test.validate_ignoring_errors, **kwargs),
|
||||
)
|
||||
|
||||
def tests(self):
|
||||
return (
|
||||
test
|
||||
for child in self._path.globChildren("*.json")
|
||||
for test in self._tests_in(
|
||||
subject=child.basename()[:-5],
|
||||
path=child,
|
||||
)
|
||||
)
|
||||
|
||||
def format_tests(self):
|
||||
path = self._path.descendant(["optional", "format"])
|
||||
return (
|
||||
test
|
||||
for child in path.globChildren("*.json")
|
||||
for test in self._tests_in(
|
||||
subject=child.basename()[:-5],
|
||||
path=child,
|
||||
)
|
||||
)
|
||||
|
||||
def tests_of(self, name):
|
||||
return self._tests_in(
|
||||
subject=name,
|
||||
path=self._path.child(name + ".json"),
|
||||
)
|
||||
|
||||
def optional_tests_of(self, name):
|
||||
return self._tests_in(
|
||||
subject=name,
|
||||
path=self._path.descendant(["optional", name + ".json"]),
|
||||
)
|
||||
|
||||
def to_unittest_testcase(self, *suites, **kwargs):
|
||||
name = kwargs.pop("name", "Test" + self.name.title())
|
||||
methods = {
|
||||
test.method_name: test.to_unittest_method(**kwargs)
|
||||
for suite in suites
|
||||
for tests in suite
|
||||
for test in tests
|
||||
}
|
||||
cls = type(name, (unittest.TestCase,), methods)
|
||||
|
||||
try:
|
||||
cls.__module__ = _someone_save_us_the_module_of_the_caller()
|
||||
except Exception: # pragma: no cover
|
||||
# We're doing crazy things, so if they go wrong, like a function
|
||||
# behaving differently on some other interpreter, just make them
|
||||
# not happen.
|
||||
pass
|
||||
|
||||
return cls
|
||||
|
||||
def _tests_in(self, subject, path):
|
||||
for each in json.loads(path.getContent().decode("utf-8")):
|
||||
yield (
|
||||
_Test(
|
||||
version=self,
|
||||
subject=subject,
|
||||
case_description=each["description"],
|
||||
schema=each["schema"],
|
||||
remotes=self._remotes,
|
||||
**test
|
||||
) for test in each["tests"]
|
||||
)
|
||||
|
||||
|
||||
@attr.s(hash=True, repr=False)
|
||||
class _Test(object):
|
||||
|
||||
version = attr.ib()
|
||||
|
||||
subject = attr.ib()
|
||||
case_description = attr.ib()
|
||||
description = attr.ib()
|
||||
|
||||
data = attr.ib()
|
||||
schema = attr.ib(repr=False)
|
||||
|
||||
valid = attr.ib()
|
||||
|
||||
_remotes = attr.ib()
|
||||
|
||||
def __repr__(self): # pragma: no cover
|
||||
return "<Test {}>".format(self.fully_qualified_name)
|
||||
|
||||
@property
|
||||
def fully_qualified_name(self): # pragma: no cover
|
||||
return " > ".join(
|
||||
[
|
||||
self.version.name,
|
||||
self.subject,
|
||||
self.case_description,
|
||||
self.description,
|
||||
]
|
||||
)
|
||||
|
||||
@property
|
||||
def method_name(self):
|
||||
delimiters = r"[\W\- ]+"
|
||||
name = "test_%s_%s_%s" % (
|
||||
re.sub(delimiters, "_", self.subject),
|
||||
re.sub(delimiters, "_", self.case_description),
|
||||
re.sub(delimiters, "_", self.description),
|
||||
)
|
||||
|
||||
if not PY3: # pragma: no cover
|
||||
name = name.encode("utf-8")
|
||||
return name
|
||||
|
||||
def to_unittest_method(self, skip=lambda test: None, **kwargs):
|
||||
if self.valid:
|
||||
def fn(this):
|
||||
self.validate(**kwargs)
|
||||
else:
|
||||
def fn(this):
|
||||
with this.assertRaises(jsonschema.ValidationError):
|
||||
self.validate(**kwargs)
|
||||
|
||||
fn.__name__ = self.method_name
|
||||
reason = skip(self)
|
||||
return unittest.skipIf(reason is not None, reason)(fn)
|
||||
|
||||
def validate(self, Validator, **kwargs):
|
||||
resolver = jsonschema.RefResolver.from_schema(
|
||||
schema=self.schema,
|
||||
store=self._remotes,
|
||||
id_of=Validator.ID_OF,
|
||||
)
|
||||
jsonschema.validate(
|
||||
instance=self.data,
|
||||
schema=self.schema,
|
||||
cls=Validator,
|
||||
resolver=resolver,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def validate_ignoring_errors(self, Validator): # pragma: no cover
|
||||
try:
|
||||
self.validate(Validator=Validator)
|
||||
except jsonschema.ValidationError:
|
||||
pass
|
||||
|
||||
|
||||
def _someone_save_us_the_module_of_the_caller():
|
||||
"""
|
||||
The FQON of the module 2nd stack frames up from here.
|
||||
|
||||
This is intended to allow us to dynamicallly return test case classes that
|
||||
are indistinguishable from being defined in the module that wants them.
|
||||
|
||||
Otherwise, trial will mis-print the FQON, and copy pasting it won't re-run
|
||||
the class that really is running.
|
||||
|
||||
Save us all, this is all so so so so so terrible.
|
||||
"""
|
||||
|
||||
return sys._getframe(2).f_globals["__name__"]
|
151
venv/Lib/site-packages/jsonschema/tests/test_cli.py
Normal file
151
venv/Lib/site-packages/jsonschema/tests/test_cli.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
from unittest import TestCase
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from jsonschema import Draft4Validator, ValidationError, cli, __version__
|
||||
from jsonschema.compat import NativeIO
|
||||
from jsonschema.exceptions import SchemaError
|
||||
|
||||
|
||||
def fake_validator(*errors):
|
||||
errors = list(reversed(errors))
|
||||
|
||||
class FakeValidator(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def iter_errors(self, instance):
|
||||
if errors:
|
||||
return errors.pop()
|
||||
return []
|
||||
|
||||
def check_schema(self, schema):
|
||||
pass
|
||||
|
||||
return FakeValidator
|
||||
|
||||
|
||||
class TestParser(TestCase):
|
||||
|
||||
FakeValidator = fake_validator()
|
||||
instance_file = "foo.json"
|
||||
schema_file = "schema.json"
|
||||
|
||||
def setUp(self):
|
||||
cli.open = self.fake_open
|
||||
self.addCleanup(delattr, cli, "open")
|
||||
|
||||
def fake_open(self, path):
|
||||
if path == self.instance_file:
|
||||
contents = ""
|
||||
elif path == self.schema_file:
|
||||
contents = {}
|
||||
else: # pragma: no cover
|
||||
self.fail("What is {!r}".format(path))
|
||||
return NativeIO(json.dumps(contents))
|
||||
|
||||
def test_find_validator_by_fully_qualified_object_name(self):
|
||||
arguments = cli.parse_args(
|
||||
[
|
||||
"--validator",
|
||||
"jsonschema.tests.test_cli.TestParser.FakeValidator",
|
||||
"--instance", self.instance_file,
|
||||
self.schema_file,
|
||||
]
|
||||
)
|
||||
self.assertIs(arguments["validator"], self.FakeValidator)
|
||||
|
||||
def test_find_validator_in_jsonschema(self):
|
||||
arguments = cli.parse_args(
|
||||
[
|
||||
"--validator", "Draft4Validator",
|
||||
"--instance", self.instance_file,
|
||||
self.schema_file,
|
||||
]
|
||||
)
|
||||
self.assertIs(arguments["validator"], Draft4Validator)
|
||||
|
||||
|
||||
class TestCLI(TestCase):
|
||||
def test_draft3_schema_draft4_validator(self):
|
||||
stdout, stderr = NativeIO(), NativeIO()
|
||||
with self.assertRaises(SchemaError):
|
||||
cli.run(
|
||||
{
|
||||
"validator": Draft4Validator,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{"minimum": 20},
|
||||
{"type": "string"},
|
||||
{"required": True},
|
||||
],
|
||||
},
|
||||
"instances": [1],
|
||||
"error_format": "{error.message}",
|
||||
},
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
)
|
||||
|
||||
def test_successful_validation(self):
|
||||
stdout, stderr = NativeIO(), NativeIO()
|
||||
exit_code = cli.run(
|
||||
{
|
||||
"validator": fake_validator(),
|
||||
"schema": {},
|
||||
"instances": [1],
|
||||
"error_format": "{error.message}",
|
||||
},
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
)
|
||||
self.assertFalse(stdout.getvalue())
|
||||
self.assertFalse(stderr.getvalue())
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
def test_unsuccessful_validation(self):
|
||||
error = ValidationError("I am an error!", instance=1)
|
||||
stdout, stderr = NativeIO(), NativeIO()
|
||||
exit_code = cli.run(
|
||||
{
|
||||
"validator": fake_validator([error]),
|
||||
"schema": {},
|
||||
"instances": [1],
|
||||
"error_format": "{error.instance} - {error.message}",
|
||||
},
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
)
|
||||
self.assertFalse(stdout.getvalue())
|
||||
self.assertEqual(stderr.getvalue(), "1 - I am an error!")
|
||||
self.assertEqual(exit_code, 1)
|
||||
|
||||
def test_unsuccessful_validation_multiple_instances(self):
|
||||
first_errors = [
|
||||
ValidationError("9", instance=1),
|
||||
ValidationError("8", instance=1),
|
||||
]
|
||||
second_errors = [ValidationError("7", instance=2)]
|
||||
stdout, stderr = NativeIO(), NativeIO()
|
||||
exit_code = cli.run(
|
||||
{
|
||||
"validator": fake_validator(first_errors, second_errors),
|
||||
"schema": {},
|
||||
"instances": [1, 2],
|
||||
"error_format": "{error.instance} - {error.message}\t",
|
||||
},
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
)
|
||||
self.assertFalse(stdout.getvalue())
|
||||
self.assertEqual(stderr.getvalue(), "1 - 9\t1 - 8\t2 - 7\t")
|
||||
self.assertEqual(exit_code, 1)
|
||||
|
||||
def test_version(self):
|
||||
version = subprocess.check_output(
|
||||
[sys.executable, "-m", "jsonschema", "--version"],
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
version = version.decode("utf-8").strip()
|
||||
self.assertEqual(version, __version__)
|
462
venv/Lib/site-packages/jsonschema/tests/test_exceptions.py
Normal file
462
venv/Lib/site-packages/jsonschema/tests/test_exceptions.py
Normal file
|
@ -0,0 +1,462 @@
|
|||
from unittest import TestCase
|
||||
import textwrap
|
||||
|
||||
from jsonschema import Draft4Validator, exceptions
|
||||
from jsonschema.compat import PY3
|
||||
|
||||
|
||||
class TestBestMatch(TestCase):
|
||||
def best_match(self, errors):
|
||||
errors = list(errors)
|
||||
best = exceptions.best_match(errors)
|
||||
reversed_best = exceptions.best_match(reversed(errors))
|
||||
msg = "Didn't return a consistent best match!\nGot: {0}\n\nThen: {1}"
|
||||
self.assertEqual(
|
||||
best._contents(), reversed_best._contents(),
|
||||
msg=msg.format(best, reversed_best),
|
||||
)
|
||||
return best
|
||||
|
||||
def test_shallower_errors_are_better_matches(self):
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"properties": {
|
||||
"foo": {
|
||||
"minProperties": 2,
|
||||
"properties": {"bar": {"type": "object"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({"foo": {"bar": []}}))
|
||||
self.assertEqual(best.validator, "minProperties")
|
||||
|
||||
def test_oneOf_and_anyOf_are_weak_matches(self):
|
||||
"""
|
||||
A property you *must* match is probably better than one you have to
|
||||
match a part of.
|
||||
"""
|
||||
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"minProperties": 2,
|
||||
"anyOf": [{"type": "string"}, {"type": "number"}],
|
||||
"oneOf": [{"type": "string"}, {"type": "number"}],
|
||||
}
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({}))
|
||||
self.assertEqual(best.validator, "minProperties")
|
||||
|
||||
def test_if_the_most_relevant_error_is_anyOf_it_is_traversed(self):
|
||||
"""
|
||||
If the most relevant error is an anyOf, then we traverse its context
|
||||
and select the otherwise *least* relevant error, since in this case
|
||||
that means the most specific, deep, error inside the instance.
|
||||
|
||||
I.e. since only one of the schemas must match, we look for the most
|
||||
relevant one.
|
||||
"""
|
||||
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"properties": {
|
||||
"foo": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"properties": {"bar": {"type": "array"}}},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({"foo": {"bar": 12}}))
|
||||
self.assertEqual(best.validator_value, "array")
|
||||
|
||||
def test_if_the_most_relevant_error_is_oneOf_it_is_traversed(self):
|
||||
"""
|
||||
If the most relevant error is an oneOf, then we traverse its context
|
||||
and select the otherwise *least* relevant error, since in this case
|
||||
that means the most specific, deep, error inside the instance.
|
||||
|
||||
I.e. since only one of the schemas must match, we look for the most
|
||||
relevant one.
|
||||
"""
|
||||
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"properties": {
|
||||
"foo": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"properties": {"bar": {"type": "array"}}},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({"foo": {"bar": 12}}))
|
||||
self.assertEqual(best.validator_value, "array")
|
||||
|
||||
def test_if_the_most_relevant_error_is_allOf_it_is_traversed(self):
|
||||
"""
|
||||
Now, if the error is allOf, we traverse but select the *most* relevant
|
||||
error from the context, because all schemas here must match anyways.
|
||||
"""
|
||||
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"properties": {
|
||||
"foo": {
|
||||
"allOf": [
|
||||
{"type": "string"},
|
||||
{"properties": {"bar": {"type": "array"}}},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({"foo": {"bar": 12}}))
|
||||
self.assertEqual(best.validator_value, "string")
|
||||
|
||||
def test_nested_context_for_oneOf(self):
|
||||
validator = Draft4Validator(
|
||||
{
|
||||
"properties": {
|
||||
"foo": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"properties": {
|
||||
"bar": {"type": "array"},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
best = self.best_match(validator.iter_errors({"foo": {"bar": 12}}))
|
||||
self.assertEqual(best.validator_value, "array")
|
||||
|
||||
def test_one_error(self):
|
||||
validator = Draft4Validator({"minProperties": 2})
|
||||
error, = validator.iter_errors({})
|
||||
self.assertEqual(
|
||||
exceptions.best_match(validator.iter_errors({})).validator,
|
||||
"minProperties",
|
||||
)
|
||||
|
||||
def test_no_errors(self):
|
||||
validator = Draft4Validator({})
|
||||
self.assertIsNone(exceptions.best_match(validator.iter_errors({})))
|
||||
|
||||
|
||||
class TestByRelevance(TestCase):
|
||||
def test_short_paths_are_better_matches(self):
|
||||
shallow = exceptions.ValidationError("Oh no!", path=["baz"])
|
||||
deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"])
|
||||
match = max([shallow, deep], key=exceptions.relevance)
|
||||
self.assertIs(match, shallow)
|
||||
|
||||
match = max([deep, shallow], key=exceptions.relevance)
|
||||
self.assertIs(match, shallow)
|
||||
|
||||
def test_global_errors_are_even_better_matches(self):
|
||||
shallow = exceptions.ValidationError("Oh no!", path=[])
|
||||
deep = exceptions.ValidationError("Oh yes!", path=["foo"])
|
||||
|
||||
errors = sorted([shallow, deep], key=exceptions.relevance)
|
||||
self.assertEqual(
|
||||
[list(error.path) for error in errors],
|
||||
[["foo"], []],
|
||||
)
|
||||
|
||||
errors = sorted([deep, shallow], key=exceptions.relevance)
|
||||
self.assertEqual(
|
||||
[list(error.path) for error in errors],
|
||||
[["foo"], []],
|
||||
)
|
||||
|
||||
def test_weak_validators_are_lower_priority(self):
|
||||
weak = exceptions.ValidationError("Oh no!", path=[], validator="a")
|
||||
normal = exceptions.ValidationError("Oh yes!", path=[], validator="b")
|
||||
|
||||
best_match = exceptions.by_relevance(weak="a")
|
||||
|
||||
match = max([weak, normal], key=best_match)
|
||||
self.assertIs(match, normal)
|
||||
|
||||
match = max([normal, weak], key=best_match)
|
||||
self.assertIs(match, normal)
|
||||
|
||||
def test_strong_validators_are_higher_priority(self):
|
||||
weak = exceptions.ValidationError("Oh no!", path=[], validator="a")
|
||||
normal = exceptions.ValidationError("Oh yes!", path=[], validator="b")
|
||||
strong = exceptions.ValidationError("Oh fine!", path=[], validator="c")
|
||||
|
||||
best_match = exceptions.by_relevance(weak="a", strong="c")
|
||||
|
||||
match = max([weak, normal, strong], key=best_match)
|
||||
self.assertIs(match, strong)
|
||||
|
||||
match = max([strong, normal, weak], key=best_match)
|
||||
self.assertIs(match, strong)
|
||||
|
||||
|
||||
class TestErrorTree(TestCase):
|
||||
def test_it_knows_how_many_total_errors_it_contains(self):
|
||||
# FIXME: https://github.com/Julian/jsonschema/issues/442
|
||||
errors = [
|
||||
exceptions.ValidationError("Something", validator=i)
|
||||
for i in range(8)
|
||||
]
|
||||
tree = exceptions.ErrorTree(errors)
|
||||
self.assertEqual(tree.total_errors, 8)
|
||||
|
||||
def test_it_contains_an_item_if_the_item_had_an_error(self):
|
||||
errors = [exceptions.ValidationError("a message", path=["bar"])]
|
||||
tree = exceptions.ErrorTree(errors)
|
||||
self.assertIn("bar", tree)
|
||||
|
||||
def test_it_does_not_contain_an_item_if_the_item_had_no_error(self):
|
||||
errors = [exceptions.ValidationError("a message", path=["bar"])]
|
||||
tree = exceptions.ErrorTree(errors)
|
||||
self.assertNotIn("foo", tree)
|
||||
|
||||
def test_validators_that_failed_appear_in_errors_dict(self):
|
||||
error = exceptions.ValidationError("a message", validator="foo")
|
||||
tree = exceptions.ErrorTree([error])
|
||||
self.assertEqual(tree.errors, {"foo": error})
|
||||
|
||||
def test_it_creates_a_child_tree_for_each_nested_path(self):
|
||||
errors = [
|
||||
exceptions.ValidationError("a bar message", path=["bar"]),
|
||||
exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]),
|
||||
]
|
||||
tree = exceptions.ErrorTree(errors)
|
||||
self.assertIn(0, tree["bar"])
|
||||
self.assertNotIn(1, tree["bar"])
|
||||
|
||||
def test_children_have_their_errors_dicts_built(self):
|
||||
e1, e2 = (
|
||||
exceptions.ValidationError("1", validator="foo", path=["bar", 0]),
|
||||
exceptions.ValidationError("2", validator="quux", path=["bar", 0]),
|
||||
)
|
||||
tree = exceptions.ErrorTree([e1, e2])
|
||||
self.assertEqual(tree["bar"][0].errors, {"foo": e1, "quux": e2})
|
||||
|
||||
def test_multiple_errors_with_instance(self):
|
||||
e1, e2 = (
|
||||
exceptions.ValidationError(
|
||||
"1",
|
||||
validator="foo",
|
||||
path=["bar", "bar2"],
|
||||
instance="i1"),
|
||||
exceptions.ValidationError(
|
||||
"2",
|
||||
validator="quux",
|
||||
path=["foobar", 2],
|
||||
instance="i2"),
|
||||
)
|
||||
exceptions.ErrorTree([e1, e2])
|
||||
|
||||
def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self):
|
||||
error = exceptions.ValidationError("123", validator="foo", instance=[])
|
||||
tree = exceptions.ErrorTree([error])
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
tree[0]
|
||||
|
||||
def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self):
|
||||
"""
|
||||
If a validator is dumb (like :validator:`required` in draft 3) and
|
||||
refers to a path that isn't in the instance, the tree still properly
|
||||
returns a subtree for that path.
|
||||
"""
|
||||
|
||||
error = exceptions.ValidationError(
|
||||
"a message", validator="foo", instance={}, path=["foo"],
|
||||
)
|
||||
tree = exceptions.ErrorTree([error])
|
||||
self.assertIsInstance(tree["foo"], exceptions.ErrorTree)
|
||||
|
||||
|
||||
class TestErrorInitReprStr(TestCase):
|
||||
def make_error(self, **kwargs):
|
||||
defaults = dict(
|
||||
message=u"hello",
|
||||
validator=u"type",
|
||||
validator_value=u"string",
|
||||
instance=5,
|
||||
schema={u"type": u"string"},
|
||||
)
|
||||
defaults.update(kwargs)
|
||||
return exceptions.ValidationError(**defaults)
|
||||
|
||||
def assertShows(self, expected, **kwargs):
|
||||
if PY3: # pragma: no cover
|
||||
expected = expected.replace("u'", "'")
|
||||
expected = textwrap.dedent(expected).rstrip("\n")
|
||||
|
||||
error = self.make_error(**kwargs)
|
||||
message_line, _, rest = str(error).partition("\n")
|
||||
self.assertEqual(message_line, error.message)
|
||||
self.assertEqual(rest, expected)
|
||||
|
||||
def test_it_calls_super_and_sets_args(self):
|
||||
error = self.make_error()
|
||||
self.assertGreater(len(error.args), 1)
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(
|
||||
repr(exceptions.ValidationError(message="Hello!")),
|
||||
"<ValidationError: %r>" % "Hello!",
|
||||
)
|
||||
|
||||
def test_unset_error(self):
|
||||
error = exceptions.ValidationError("message")
|
||||
self.assertEqual(str(error), "message")
|
||||
|
||||
kwargs = {
|
||||
"validator": "type",
|
||||
"validator_value": "string",
|
||||
"instance": 5,
|
||||
"schema": {"type": "string"},
|
||||
}
|
||||
# Just the message should show if any of the attributes are unset
|
||||
for attr in kwargs:
|
||||
k = dict(kwargs)
|
||||
del k[attr]
|
||||
error = exceptions.ValidationError("message", **k)
|
||||
self.assertEqual(str(error), "message")
|
||||
|
||||
def test_empty_paths(self):
|
||||
self.assertShows(
|
||||
"""
|
||||
Failed validating u'type' in schema:
|
||||
{u'type': u'string'}
|
||||
|
||||
On instance:
|
||||
5
|
||||
""",
|
||||
path=[],
|
||||
schema_path=[],
|
||||
)
|
||||
|
||||
def test_one_item_paths(self):
|
||||
self.assertShows(
|
||||
"""
|
||||
Failed validating u'type' in schema:
|
||||
{u'type': u'string'}
|
||||
|
||||
On instance[0]:
|
||||
5
|
||||
""",
|
||||
path=[0],
|
||||
schema_path=["items"],
|
||||
)
|
||||
|
||||
def test_multiple_item_paths(self):
|
||||
self.assertShows(
|
||||
"""
|
||||
Failed validating u'type' in schema[u'items'][0]:
|
||||
{u'type': u'string'}
|
||||
|
||||
On instance[0][u'a']:
|
||||
5
|
||||
""",
|
||||
path=[0, u"a"],
|
||||
schema_path=[u"items", 0, 1],
|
||||
)
|
||||
|
||||
def test_uses_pprint(self):
|
||||
self.assertShows(
|
||||
"""
|
||||
Failed validating u'maxLength' in schema:
|
||||
{0: 0,
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
5: 5,
|
||||
6: 6,
|
||||
7: 7,
|
||||
8: 8,
|
||||
9: 9,
|
||||
10: 10,
|
||||
11: 11,
|
||||
12: 12,
|
||||
13: 13,
|
||||
14: 14,
|
||||
15: 15,
|
||||
16: 16,
|
||||
17: 17,
|
||||
18: 18,
|
||||
19: 19}
|
||||
|
||||
On instance:
|
||||
[0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24]
|
||||
""",
|
||||
instance=list(range(25)),
|
||||
schema=dict(zip(range(20), range(20))),
|
||||
validator=u"maxLength",
|
||||
)
|
||||
|
||||
def test_str_works_with_instances_having_overriden_eq_operator(self):
|
||||
"""
|
||||
Check for https://github.com/Julian/jsonschema/issues/164 which
|
||||
rendered exceptions unusable when a `ValidationError` involved
|
||||
instances with an `__eq__` method that returned truthy values.
|
||||
"""
|
||||
|
||||
class DontEQMeBro(object):
|
||||
def __eq__(this, other): # pragma: no cover
|
||||
self.fail("Don't!")
|
||||
|
||||
def __ne__(this, other): # pragma: no cover
|
||||
self.fail("Don't!")
|
||||
|
||||
instance = DontEQMeBro()
|
||||
error = exceptions.ValidationError(
|
||||
"a message",
|
||||
validator="foo",
|
||||
instance=instance,
|
||||
validator_value="some",
|
||||
schema="schema",
|
||||
)
|
||||
self.assertIn(repr(instance), str(error))
|
||||
|
||||
|
||||
class TestHashable(TestCase):
|
||||
def test_hashable(self):
|
||||
set([exceptions.ValidationError("")])
|
||||
set([exceptions.SchemaError("")])
|
89
venv/Lib/site-packages/jsonschema/tests/test_format.py
Normal file
89
venv/Lib/site-packages/jsonschema/tests/test_format.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
"""
|
||||
Tests for the parts of jsonschema related to the :validator:`format` property.
|
||||
"""
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from jsonschema import FormatError, ValidationError, FormatChecker
|
||||
from jsonschema.validators import Draft4Validator
|
||||
|
||||
|
||||
BOOM = ValueError("Boom!")
|
||||
BANG = ZeroDivisionError("Bang!")
|
||||
|
||||
|
||||
def boom(thing):
|
||||
if thing == "bang":
|
||||
raise BANG
|
||||
raise BOOM
|
||||
|
||||
|
||||
class TestFormatChecker(TestCase):
|
||||
def test_it_can_validate_no_formats(self):
|
||||
checker = FormatChecker(formats=())
|
||||
self.assertFalse(checker.checkers)
|
||||
|
||||
def test_it_raises_a_key_error_for_unknown_formats(self):
|
||||
with self.assertRaises(KeyError):
|
||||
FormatChecker(formats=["o noes"])
|
||||
|
||||
def test_it_can_register_cls_checkers(self):
|
||||
original = dict(FormatChecker.checkers)
|
||||
self.addCleanup(FormatChecker.checkers.pop, "boom")
|
||||
FormatChecker.cls_checks("boom")(boom)
|
||||
self.assertEqual(
|
||||
FormatChecker.checkers,
|
||||
dict(original, boom=(boom, ())),
|
||||
)
|
||||
|
||||
def test_it_can_register_checkers(self):
|
||||
checker = FormatChecker()
|
||||
checker.checks("boom")(boom)
|
||||
self.assertEqual(
|
||||
checker.checkers,
|
||||
dict(FormatChecker.checkers, boom=(boom, ()))
|
||||
)
|
||||
|
||||
def test_it_catches_registered_errors(self):
|
||||
checker = FormatChecker()
|
||||
checker.checks("boom", raises=type(BOOM))(boom)
|
||||
|
||||
with self.assertRaises(FormatError) as cm:
|
||||
checker.check(instance=12, format="boom")
|
||||
|
||||
self.assertIs(cm.exception.cause, BOOM)
|
||||
self.assertIs(cm.exception.__cause__, BOOM)
|
||||
|
||||
# Unregistered errors should not be caught
|
||||
with self.assertRaises(type(BANG)):
|
||||
checker.check(instance="bang", format="boom")
|
||||
|
||||
def test_format_error_causes_become_validation_error_causes(self):
|
||||
checker = FormatChecker()
|
||||
checker.checks("boom", raises=ValueError)(boom)
|
||||
validator = Draft4Validator({"format": "boom"}, format_checker=checker)
|
||||
|
||||
with self.assertRaises(ValidationError) as cm:
|
||||
validator.validate("BOOM")
|
||||
|
||||
self.assertIs(cm.exception.cause, BOOM)
|
||||
self.assertIs(cm.exception.__cause__, BOOM)
|
||||
|
||||
def test_format_checkers_come_with_defaults(self):
|
||||
# This is bad :/ but relied upon.
|
||||
# The docs for quite awhile recommended people do things like
|
||||
# validate(..., format_checker=FormatChecker())
|
||||
# We should change that, but we can't without deprecation...
|
||||
checker = FormatChecker()
|
||||
with self.assertRaises(FormatError):
|
||||
checker.check(instance="not-an-ipv4", format="ipv4")
|
||||
|
||||
def test_repr(self):
|
||||
checker = FormatChecker(formats=())
|
||||
checker.checks("foo")(lambda thing: True)
|
||||
checker.checks("bar")(lambda thing: True)
|
||||
checker.checks("baz")(lambda thing: True)
|
||||
self.assertEqual(
|
||||
repr(checker),
|
||||
"<FormatChecker checkers=['bar', 'baz', 'foo']>",
|
||||
)
|
|
@ -0,0 +1,277 @@
|
|||
"""
|
||||
Test runner for the JSON Schema official test suite
|
||||
|
||||
Tests comprehensive correctness of each draft's validator.
|
||||
|
||||
See https://github.com/json-schema-org/JSON-Schema-Test-Suite for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from jsonschema import (
|
||||
Draft3Validator,
|
||||
Draft4Validator,
|
||||
Draft6Validator,
|
||||
Draft7Validator,
|
||||
draft3_format_checker,
|
||||
draft4_format_checker,
|
||||
draft6_format_checker,
|
||||
draft7_format_checker,
|
||||
)
|
||||
from jsonschema.tests._helpers import bug
|
||||
from jsonschema.tests._suite import Suite
|
||||
from jsonschema.validators import _DEPRECATED_DEFAULT_TYPES, create
|
||||
|
||||
|
||||
SUITE = Suite()
|
||||
DRAFT3 = SUITE.version(name="draft3")
|
||||
DRAFT4 = SUITE.version(name="draft4")
|
||||
DRAFT6 = SUITE.version(name="draft6")
|
||||
DRAFT7 = SUITE.version(name="draft7")
|
||||
|
||||
|
||||
def skip(message, **kwargs):
|
||||
def skipper(test):
|
||||
if all(value == getattr(test, attr) for attr, value in kwargs.items()):
|
||||
return message
|
||||
return skipper
|
||||
|
||||
|
||||
def missing_format(checker):
|
||||
def missing_format(test):
|
||||
schema = test.schema
|
||||
if schema is True or schema is False or "format" not in schema:
|
||||
return
|
||||
|
||||
if schema["format"] not in checker.checkers:
|
||||
return "Format checker {0!r} not found.".format(schema["format"])
|
||||
return missing_format
|
||||
|
||||
|
||||
is_narrow_build = sys.maxunicode == 2 ** 16 - 1
|
||||
if is_narrow_build: # pragma: no cover
|
||||
message = "Not running surrogate Unicode case, this Python is narrow."
|
||||
|
||||
def narrow_unicode_build(test): # pragma: no cover
|
||||
return skip(
|
||||
message=message,
|
||||
description="one supplementary Unicode code point is not long enough",
|
||||
)(test) or skip(
|
||||
message=message,
|
||||
description="two supplementary Unicode code points is long enough",
|
||||
)(test)
|
||||
else:
|
||||
def narrow_unicode_build(test): # pragma: no cover
|
||||
return
|
||||
|
||||
|
||||
TestDraft3 = DRAFT3.to_unittest_testcase(
|
||||
DRAFT3.tests(),
|
||||
DRAFT3.optional_tests_of(name="bignum"),
|
||||
DRAFT3.optional_tests_of(name="format"),
|
||||
DRAFT3.optional_tests_of(name="zeroTerminatedFloats"),
|
||||
Validator=Draft3Validator,
|
||||
format_checker=draft3_format_checker,
|
||||
skip=lambda test: (
|
||||
narrow_unicode_build(test)
|
||||
or missing_format(draft3_format_checker)(test)
|
||||
or skip(
|
||||
message="Upstream bug in strict_rfc3339",
|
||||
subject="format",
|
||||
description="case-insensitive T and Z",
|
||||
)(test)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
TestDraft4 = DRAFT4.to_unittest_testcase(
|
||||
DRAFT4.tests(),
|
||||
DRAFT4.optional_tests_of(name="bignum"),
|
||||
DRAFT4.optional_tests_of(name="format"),
|
||||
DRAFT4.optional_tests_of(name="zeroTerminatedFloats"),
|
||||
Validator=Draft4Validator,
|
||||
format_checker=draft4_format_checker,
|
||||
skip=lambda test: (
|
||||
narrow_unicode_build(test)
|
||||
or missing_format(draft4_format_checker)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="ref",
|
||||
case_description="Recursive references between schemas",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description="Location-independent identifier",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with absolute URI"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with base URI change in subschema"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="refRemote",
|
||||
case_description="base URI change - change folder in subschema",
|
||||
)(test)
|
||||
or skip(
|
||||
message="Upstream bug in strict_rfc3339",
|
||||
subject="format",
|
||||
description="case-insensitive T and Z",
|
||||
)(test)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
TestDraft6 = DRAFT6.to_unittest_testcase(
|
||||
DRAFT6.tests(),
|
||||
DRAFT6.optional_tests_of(name="bignum"),
|
||||
DRAFT6.optional_tests_of(name="format"),
|
||||
DRAFT6.optional_tests_of(name="zeroTerminatedFloats"),
|
||||
Validator=Draft6Validator,
|
||||
format_checker=draft6_format_checker,
|
||||
skip=lambda test: (
|
||||
narrow_unicode_build(test)
|
||||
or missing_format(draft6_format_checker)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="ref",
|
||||
case_description="Recursive references between schemas",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description="Location-independent identifier",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with absolute URI"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with base URI change in subschema"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="refRemote",
|
||||
case_description="base URI change - change folder in subschema",
|
||||
)(test)
|
||||
or skip(
|
||||
message="Upstream bug in strict_rfc3339",
|
||||
subject="format",
|
||||
description="case-insensitive T and Z",
|
||||
)(test)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
TestDraft7 = DRAFT7.to_unittest_testcase(
|
||||
DRAFT7.tests(),
|
||||
DRAFT7.format_tests(),
|
||||
DRAFT7.optional_tests_of(name="bignum"),
|
||||
DRAFT7.optional_tests_of(name="content"),
|
||||
DRAFT7.optional_tests_of(name="zeroTerminatedFloats"),
|
||||
Validator=Draft7Validator,
|
||||
format_checker=draft7_format_checker,
|
||||
skip=lambda test: (
|
||||
narrow_unicode_build(test)
|
||||
or missing_format(draft7_format_checker)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="ref",
|
||||
case_description="Recursive references between schemas",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description="Location-independent identifier",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with absolute URI"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(371),
|
||||
subject="ref",
|
||||
case_description=(
|
||||
"Location-independent identifier with base URI change in subschema"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(),
|
||||
subject="refRemote",
|
||||
case_description="base URI change - change folder in subschema",
|
||||
)(test)
|
||||
or skip(
|
||||
message="Upstream bug in strict_rfc3339",
|
||||
subject="date-time",
|
||||
description="case-insensitive T and Z",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(593),
|
||||
subject="content",
|
||||
case_description=(
|
||||
"validation of string-encoded content based on media type"
|
||||
),
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(593),
|
||||
subject="content",
|
||||
case_description="validation of binary string-encoding",
|
||||
)(test)
|
||||
or skip(
|
||||
message=bug(593),
|
||||
subject="content",
|
||||
case_description=(
|
||||
"validation of binary-encoded media type documents"
|
||||
),
|
||||
)(test)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
|
||||
TestDraft3LegacyTypeCheck = DRAFT3.to_unittest_testcase(
|
||||
# Interestingly the any part couldn't really be done w/the old API.
|
||||
(
|
||||
(test for test in each if test.schema != {"type": "any"})
|
||||
for each in DRAFT3.tests_of(name="type")
|
||||
),
|
||||
name="TestDraft3LegacyTypeCheck",
|
||||
Validator=create(
|
||||
meta_schema=Draft3Validator.META_SCHEMA,
|
||||
validators=Draft3Validator.VALIDATORS,
|
||||
default_types=_DEPRECATED_DEFAULT_TYPES,
|
||||
),
|
||||
)
|
||||
|
||||
TestDraft4LegacyTypeCheck = DRAFT4.to_unittest_testcase(
|
||||
DRAFT4.tests_of(name="type"),
|
||||
name="TestDraft4LegacyTypeCheck",
|
||||
Validator=create(
|
||||
meta_schema=Draft4Validator.META_SCHEMA,
|
||||
validators=Draft4Validator.VALIDATORS,
|
||||
default_types=_DEPRECATED_DEFAULT_TYPES,
|
||||
),
|
||||
)
|
190
venv/Lib/site-packages/jsonschema/tests/test_types.py
Normal file
190
venv/Lib/site-packages/jsonschema/tests/test_types.py
Normal file
|
@ -0,0 +1,190 @@
|
|||
"""
|
||||
Tests on the new type interface. The actual correctness of the type checking
|
||||
is handled in test_jsonschema_test_suite; these tests check that TypeChecker
|
||||
functions correctly and can facilitate extensions to type checking
|
||||
"""
|
||||
from collections import namedtuple
|
||||
from unittest import TestCase
|
||||
|
||||
from jsonschema import ValidationError, _validators
|
||||
from jsonschema._types import TypeChecker
|
||||
from jsonschema.exceptions import UndefinedTypeCheck
|
||||
from jsonschema.validators import Draft4Validator, extend
|
||||
|
||||
|
||||
def equals_2(checker, instance):
|
||||
return instance == 2
|
||||
|
||||
|
||||
def is_namedtuple(instance):
|
||||
return isinstance(instance, tuple) and getattr(instance, "_fields", None)
|
||||
|
||||
|
||||
def is_object_or_named_tuple(checker, instance):
|
||||
if Draft4Validator.TYPE_CHECKER.is_type(instance, "object"):
|
||||
return True
|
||||
return is_namedtuple(instance)
|
||||
|
||||
|
||||
def coerce_named_tuple(fn):
|
||||
def coerced(validator, value, instance, schema):
|
||||
if is_namedtuple(instance):
|
||||
instance = instance._asdict()
|
||||
return fn(validator, value, instance, schema)
|
||||
return coerced
|
||||
|
||||
|
||||
required = coerce_named_tuple(_validators.required)
|
||||
properties = coerce_named_tuple(_validators.properties)
|
||||
|
||||
|
||||
class TestTypeChecker(TestCase):
|
||||
def test_is_type(self):
|
||||
checker = TypeChecker({"two": equals_2})
|
||||
self.assertEqual(
|
||||
(
|
||||
checker.is_type(instance=2, type="two"),
|
||||
checker.is_type(instance="bar", type="two"),
|
||||
),
|
||||
(True, False),
|
||||
)
|
||||
|
||||
def test_is_unknown_type(self):
|
||||
with self.assertRaises(UndefinedTypeCheck) as context:
|
||||
TypeChecker().is_type(4, "foobar")
|
||||
self.assertIn("foobar", str(context.exception))
|
||||
|
||||
def test_checks_can_be_added_at_init(self):
|
||||
checker = TypeChecker({"two": equals_2})
|
||||
self.assertEqual(checker, TypeChecker().redefine("two", equals_2))
|
||||
|
||||
def test_redefine_existing_type(self):
|
||||
self.assertEqual(
|
||||
TypeChecker().redefine("two", object()).redefine("two", equals_2),
|
||||
TypeChecker().redefine("two", equals_2),
|
||||
)
|
||||
|
||||
def test_remove(self):
|
||||
self.assertEqual(
|
||||
TypeChecker({"two": equals_2}).remove("two"),
|
||||
TypeChecker(),
|
||||
)
|
||||
|
||||
def test_remove_unknown_type(self):
|
||||
with self.assertRaises(UndefinedTypeCheck) as context:
|
||||
TypeChecker().remove("foobar")
|
||||
self.assertIn("foobar", str(context.exception))
|
||||
|
||||
def test_redefine_many(self):
|
||||
self.assertEqual(
|
||||
TypeChecker().redefine_many({"foo": int, "bar": str}),
|
||||
TypeChecker().redefine("foo", int).redefine("bar", str),
|
||||
)
|
||||
|
||||
def test_remove_multiple(self):
|
||||
self.assertEqual(
|
||||
TypeChecker({"foo": int, "bar": str}).remove("foo", "bar"),
|
||||
TypeChecker(),
|
||||
)
|
||||
|
||||
def test_type_check_can_raise_key_error(self):
|
||||
"""
|
||||
Make sure no one writes:
|
||||
|
||||
try:
|
||||
self._type_checkers[type](...)
|
||||
except KeyError:
|
||||
|
||||
ignoring the fact that the function itself can raise that.
|
||||
"""
|
||||
|
||||
error = KeyError("Stuff")
|
||||
|
||||
def raises_keyerror(checker, instance):
|
||||
raise error
|
||||
|
||||
with self.assertRaises(KeyError) as context:
|
||||
TypeChecker({"foo": raises_keyerror}).is_type(4, "foo")
|
||||
|
||||
self.assertIs(context.exception, error)
|
||||
|
||||
|
||||
class TestCustomTypes(TestCase):
|
||||
def test_simple_type_can_be_extended(self):
|
||||
def int_or_str_int(checker, instance):
|
||||
if not isinstance(instance, (int, str)):
|
||||
return False
|
||||
try:
|
||||
int(instance)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
CustomValidator = extend(
|
||||
Draft4Validator,
|
||||
type_checker=Draft4Validator.TYPE_CHECKER.redefine(
|
||||
"integer", int_or_str_int,
|
||||
),
|
||||
)
|
||||
validator = CustomValidator({"type": "integer"})
|
||||
|
||||
validator.validate(4)
|
||||
validator.validate("4")
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
validator.validate(4.4)
|
||||
|
||||
def test_object_can_be_extended(self):
|
||||
schema = {"type": "object"}
|
||||
|
||||
Point = namedtuple("Point", ["x", "y"])
|
||||
|
||||
type_checker = Draft4Validator.TYPE_CHECKER.redefine(
|
||||
u"object", is_object_or_named_tuple,
|
||||
)
|
||||
|
||||
CustomValidator = extend(Draft4Validator, type_checker=type_checker)
|
||||
validator = CustomValidator(schema)
|
||||
|
||||
validator.validate(Point(x=4, y=5))
|
||||
|
||||
def test_object_extensions_require_custom_validators(self):
|
||||
schema = {"type": "object", "required": ["x"]}
|
||||
|
||||
type_checker = Draft4Validator.TYPE_CHECKER.redefine(
|
||||
u"object", is_object_or_named_tuple,
|
||||
)
|
||||
|
||||
CustomValidator = extend(Draft4Validator, type_checker=type_checker)
|
||||
validator = CustomValidator(schema)
|
||||
|
||||
Point = namedtuple("Point", ["x", "y"])
|
||||
# Cannot handle required
|
||||
with self.assertRaises(ValidationError):
|
||||
validator.validate(Point(x=4, y=5))
|
||||
|
||||
def test_object_extensions_can_handle_custom_validators(self):
|
||||
schema = {
|
||||
"type": "object",
|
||||
"required": ["x"],
|
||||
"properties": {"x": {"type": "integer"}},
|
||||
}
|
||||
|
||||
type_checker = Draft4Validator.TYPE_CHECKER.redefine(
|
||||
u"object", is_object_or_named_tuple,
|
||||
)
|
||||
|
||||
CustomValidator = extend(
|
||||
Draft4Validator,
|
||||
type_checker=type_checker,
|
||||
validators={"required": required, "properties": properties},
|
||||
)
|
||||
|
||||
validator = CustomValidator(schema)
|
||||
|
||||
Point = namedtuple("Point", ["x", "y"])
|
||||
# Can now process required and properties
|
||||
validator.validate(Point(x=4, y=5))
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
validator.validate(Point(x="not an integer", y=5))
|
1762
venv/Lib/site-packages/jsonschema/tests/test_validators.py
Normal file
1762
venv/Lib/site-packages/jsonschema/tests/test_validators.py
Normal file
File diff suppressed because it is too large
Load diff
970
venv/Lib/site-packages/jsonschema/validators.py
Normal file
970
venv/Lib/site-packages/jsonschema/validators.py
Normal file
|
@ -0,0 +1,970 @@
|
|||
"""
|
||||
Creation and extension of validators, with implementations for existing drafts.
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
from warnings import warn
|
||||
import contextlib
|
||||
import json
|
||||
import numbers
|
||||
|
||||
from six import add_metaclass
|
||||
|
||||
from jsonschema import (
|
||||
_legacy_validators,
|
||||
_types,
|
||||
_utils,
|
||||
_validators,
|
||||
exceptions,
|
||||
)
|
||||
from jsonschema.compat import (
|
||||
Sequence,
|
||||
int_types,
|
||||
iteritems,
|
||||
lru_cache,
|
||||
str_types,
|
||||
unquote,
|
||||
urldefrag,
|
||||
urljoin,
|
||||
urlopen,
|
||||
urlsplit,
|
||||
)
|
||||
|
||||
# Sigh. https://gitlab.com/pycqa/flake8/issues/280
|
||||
# https://github.com/pyga/ebb-lint/issues/7
|
||||
# Imported for backwards compatibility.
|
||||
from jsonschema.exceptions import ErrorTree
|
||||
ErrorTree
|
||||
|
||||
|
||||
class _DontDoThat(Exception):
|
||||
"""
|
||||
Raised when a Validators with non-default type checker is misused.
|
||||
|
||||
Asking one for DEFAULT_TYPES doesn't make sense, since type checkers
|
||||
exist for the unrepresentable cases where DEFAULT_TYPES can't
|
||||
represent the type relationship.
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return "DEFAULT_TYPES cannot be used on Validators using TypeCheckers"
|
||||
|
||||
|
||||
validators = {}
|
||||
meta_schemas = _utils.URIDict()
|
||||
|
||||
|
||||
def _generate_legacy_type_checks(types=()):
|
||||
"""
|
||||
Generate newer-style type checks out of JSON-type-name-to-type mappings.
|
||||
|
||||
Arguments:
|
||||
|
||||
types (dict):
|
||||
|
||||
A mapping of type names to their Python types
|
||||
|
||||
Returns:
|
||||
|
||||
A dictionary of definitions to pass to `TypeChecker`
|
||||
"""
|
||||
types = dict(types)
|
||||
|
||||
def gen_type_check(pytypes):
|
||||
pytypes = _utils.flatten(pytypes)
|
||||
|
||||
def type_check(checker, instance):
|
||||
if isinstance(instance, bool):
|
||||
if bool not in pytypes:
|
||||
return False
|
||||
return isinstance(instance, pytypes)
|
||||
|
||||
return type_check
|
||||
|
||||
definitions = {}
|
||||
for typename, pytypes in iteritems(types):
|
||||
definitions[typename] = gen_type_check(pytypes)
|
||||
|
||||
return definitions
|
||||
|
||||
|
||||
_DEPRECATED_DEFAULT_TYPES = {
|
||||
u"array": list,
|
||||
u"boolean": bool,
|
||||
u"integer": int_types,
|
||||
u"null": type(None),
|
||||
u"number": numbers.Number,
|
||||
u"object": dict,
|
||||
u"string": str_types,
|
||||
}
|
||||
_TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES = _types.TypeChecker(
|
||||
type_checkers=_generate_legacy_type_checks(_DEPRECATED_DEFAULT_TYPES),
|
||||
)
|
||||
|
||||
|
||||
def validates(version):
|
||||
"""
|
||||
Register the decorated validator for a ``version`` of the specification.
|
||||
|
||||
Registered validators and their meta schemas will be considered when
|
||||
parsing ``$schema`` properties' URIs.
|
||||
|
||||
Arguments:
|
||||
|
||||
version (str):
|
||||
|
||||
An identifier to use as the version's name
|
||||
|
||||
Returns:
|
||||
|
||||
collections.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)
|
||||
if meta_schema_id:
|
||||
meta_schemas[meta_schema_id] = cls
|
||||
return cls
|
||||
return _validates
|
||||
|
||||
|
||||
def _DEFAULT_TYPES(self):
|
||||
if self._CREATED_WITH_DEFAULT_TYPES is None:
|
||||
raise _DontDoThat()
|
||||
|
||||
warn(
|
||||
(
|
||||
"The DEFAULT_TYPES attribute is deprecated. "
|
||||
"See the type checker attached to this validator instead."
|
||||
),
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self._DEFAULT_TYPES
|
||||
|
||||
|
||||
class _DefaultTypesDeprecatingMetaClass(type):
|
||||
DEFAULT_TYPES = property(_DEFAULT_TYPES)
|
||||
|
||||
|
||||
def _id_of(schema):
|
||||
if schema is True or schema is False:
|
||||
return u""
|
||||
return schema.get(u"$id", u"")
|
||||
|
||||
|
||||
def create(
|
||||
meta_schema,
|
||||
validators=(),
|
||||
version=None,
|
||||
default_types=None,
|
||||
type_checker=None,
|
||||
id_of=_id_of,
|
||||
):
|
||||
"""
|
||||
Create a new validator class.
|
||||
|
||||
Arguments:
|
||||
|
||||
meta_schema (collections.Mapping):
|
||||
|
||||
the meta schema for the new validator class
|
||||
|
||||
validators (collections.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 :validator:`type` validator.
|
||||
|
||||
If unprovided, a `jsonschema.TypeChecker` will be created
|
||||
with a set of default types typical of JSON Schema drafts.
|
||||
|
||||
default_types (collections.Mapping):
|
||||
|
||||
.. deprecated:: 3.0.0
|
||||
|
||||
Please use the type_checker argument instead.
|
||||
|
||||
If set, it provides mappings of JSON types to Python types
|
||||
that will be converted to functions and redefined in this
|
||||
object's `jsonschema.TypeChecker`.
|
||||
|
||||
id_of (collections.Callable):
|
||||
|
||||
A function that given a schema, returns its ID.
|
||||
|
||||
Returns:
|
||||
|
||||
a new `jsonschema.IValidator` class
|
||||
"""
|
||||
|
||||
if default_types is not None:
|
||||
if type_checker is not None:
|
||||
raise TypeError(
|
||||
"Do not specify default_types when providing a type checker.",
|
||||
)
|
||||
_created_with_default_types = True
|
||||
warn(
|
||||
(
|
||||
"The default_types argument is deprecated. "
|
||||
"Use the type_checker argument instead."
|
||||
),
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
type_checker = _types.TypeChecker(
|
||||
type_checkers=_generate_legacy_type_checks(default_types),
|
||||
)
|
||||
else:
|
||||
default_types = _DEPRECATED_DEFAULT_TYPES
|
||||
if type_checker is None:
|
||||
_created_with_default_types = False
|
||||
type_checker = _TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES
|
||||
elif type_checker is _TYPE_CHECKER_FOR_DEPRECATED_DEFAULT_TYPES:
|
||||
_created_with_default_types = False
|
||||
else:
|
||||
_created_with_default_types = None
|
||||
|
||||
@add_metaclass(_DefaultTypesDeprecatingMetaClass)
|
||||
class Validator(object):
|
||||
|
||||
VALIDATORS = dict(validators)
|
||||
META_SCHEMA = dict(meta_schema)
|
||||
TYPE_CHECKER = type_checker
|
||||
ID_OF = staticmethod(id_of)
|
||||
|
||||
DEFAULT_TYPES = property(_DEFAULT_TYPES)
|
||||
_DEFAULT_TYPES = dict(default_types)
|
||||
_CREATED_WITH_DEFAULT_TYPES = _created_with_default_types
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
schema,
|
||||
types=(),
|
||||
resolver=None,
|
||||
format_checker=None,
|
||||
):
|
||||
if types:
|
||||
warn(
|
||||
(
|
||||
"The types argument is deprecated. Provide "
|
||||
"a type_checker to jsonschema.validators.extend "
|
||||
"instead."
|
||||
),
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self.TYPE_CHECKER = self.TYPE_CHECKER.redefine_many(
|
||||
_generate_legacy_type_checks(types),
|
||||
)
|
||||
|
||||
if resolver is None:
|
||||
resolver = RefResolver.from_schema(schema, id_of=id_of)
|
||||
|
||||
self.resolver = resolver
|
||||
self.format_checker = format_checker
|
||||
self.schema = schema
|
||||
|
||||
@classmethod
|
||||
def check_schema(cls, schema):
|
||||
for error in cls(cls.META_SCHEMA).iter_errors(schema):
|
||||
raise exceptions.SchemaError.create_from(error)
|
||||
|
||||
def iter_errors(self, instance, _schema=None):
|
||||
if _schema is None:
|
||||
_schema = self.schema
|
||||
|
||||
if _schema is True:
|
||||
return
|
||||
elif _schema is False:
|
||||
yield exceptions.ValidationError(
|
||||
"False schema does not allow %r" % (instance,),
|
||||
validator=None,
|
||||
validator_value=None,
|
||||
instance=instance,
|
||||
schema=_schema,
|
||||
)
|
||||
return
|
||||
|
||||
scope = id_of(_schema)
|
||||
if scope:
|
||||
self.resolver.push_scope(scope)
|
||||
try:
|
||||
ref = _schema.get(u"$ref")
|
||||
if ref is not None:
|
||||
validators = [(u"$ref", ref)]
|
||||
else:
|
||||
validators = iteritems(_schema)
|
||||
|
||||
for k, v in validators:
|
||||
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,
|
||||
)
|
||||
if k != u"$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.iter_errors(instance, schema):
|
||||
if path is not None:
|
||||
error.path.appendleft(path)
|
||||
if schema_path is not None:
|
||||
error.schema_path.appendleft(schema_path)
|
||||
yield error
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
for error in self.iter_errors(*args, **kwargs):
|
||||
raise error
|
||||
|
||||
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):
|
||||
error = next(self.iter_errors(instance, _schema), None)
|
||||
return error is None
|
||||
|
||||
if version is not None:
|
||||
Validator = validates(version)(Validator)
|
||||
Validator.__name__ = version.title().replace(" ", "") + "Validator"
|
||||
|
||||
return Validator
|
||||
|
||||
|
||||
def extend(validator, validators=(), version=None, type_checker=None):
|
||||
"""
|
||||
Create a new validator class by extending an existing one.
|
||||
|
||||
Arguments:
|
||||
|
||||
validator (jsonschema.IValidator):
|
||||
|
||||
an existing validator class
|
||||
|
||||
validators (collections.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["validator_name"]``.
|
||||
|
||||
version (str):
|
||||
|
||||
a version for the new validator class
|
||||
|
||||
type_checker (jsonschema.TypeChecker):
|
||||
|
||||
a type checker, used when applying the :validator:`type` validator.
|
||||
|
||||
If unprovided, the type checker of the extended
|
||||
`jsonschema.IValidator` will be carried along.`
|
||||
|
||||
Returns:
|
||||
|
||||
a new `jsonschema.IValidator` 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
|
||||
elif validator._CREATED_WITH_DEFAULT_TYPES:
|
||||
raise TypeError(
|
||||
"Cannot extend a validator created with default_types "
|
||||
"with a type_checker. Update the validator to use a "
|
||||
"type_checker when created."
|
||||
)
|
||||
return create(
|
||||
meta_schema=validator.META_SCHEMA,
|
||||
validators=all_validators,
|
||||
version=version,
|
||||
type_checker=type_checker,
|
||||
id_of=validator.ID_OF,
|
||||
)
|
||||
|
||||
|
||||
Draft3Validator = create(
|
||||
meta_schema=_utils.load_schema("draft3"),
|
||||
validators={
|
||||
u"$ref": _validators.ref,
|
||||
u"additionalItems": _validators.additionalItems,
|
||||
u"additionalProperties": _validators.additionalProperties,
|
||||
u"dependencies": _legacy_validators.dependencies_draft3,
|
||||
u"disallow": _legacy_validators.disallow_draft3,
|
||||
u"divisibleBy": _validators.multipleOf,
|
||||
u"enum": _validators.enum,
|
||||
u"extends": _legacy_validators.extends_draft3,
|
||||
u"format": _validators.format,
|
||||
u"items": _legacy_validators.items_draft3_draft4,
|
||||
u"maxItems": _validators.maxItems,
|
||||
u"maxLength": _validators.maxLength,
|
||||
u"maximum": _legacy_validators.maximum_draft3_draft4,
|
||||
u"minItems": _validators.minItems,
|
||||
u"minLength": _validators.minLength,
|
||||
u"minimum": _legacy_validators.minimum_draft3_draft4,
|
||||
u"pattern": _validators.pattern,
|
||||
u"patternProperties": _validators.patternProperties,
|
||||
u"properties": _legacy_validators.properties_draft3,
|
||||
u"type": _legacy_validators.type_draft3,
|
||||
u"uniqueItems": _validators.uniqueItems,
|
||||
},
|
||||
type_checker=_types.draft3_type_checker,
|
||||
version="draft3",
|
||||
id_of=lambda schema: schema.get(u"id", ""),
|
||||
)
|
||||
|
||||
Draft4Validator = create(
|
||||
meta_schema=_utils.load_schema("draft4"),
|
||||
validators={
|
||||
u"$ref": _validators.ref,
|
||||
u"additionalItems": _validators.additionalItems,
|
||||
u"additionalProperties": _validators.additionalProperties,
|
||||
u"allOf": _validators.allOf,
|
||||
u"anyOf": _validators.anyOf,
|
||||
u"dependencies": _validators.dependencies,
|
||||
u"enum": _validators.enum,
|
||||
u"format": _validators.format,
|
||||
u"items": _legacy_validators.items_draft3_draft4,
|
||||
u"maxItems": _validators.maxItems,
|
||||
u"maxLength": _validators.maxLength,
|
||||
u"maxProperties": _validators.maxProperties,
|
||||
u"maximum": _legacy_validators.maximum_draft3_draft4,
|
||||
u"minItems": _validators.minItems,
|
||||
u"minLength": _validators.minLength,
|
||||
u"minProperties": _validators.minProperties,
|
||||
u"minimum": _legacy_validators.minimum_draft3_draft4,
|
||||
u"multipleOf": _validators.multipleOf,
|
||||
u"not": _validators.not_,
|
||||
u"oneOf": _validators.oneOf,
|
||||
u"pattern": _validators.pattern,
|
||||
u"patternProperties": _validators.patternProperties,
|
||||
u"properties": _validators.properties,
|
||||
u"required": _validators.required,
|
||||
u"type": _validators.type,
|
||||
u"uniqueItems": _validators.uniqueItems,
|
||||
},
|
||||
type_checker=_types.draft4_type_checker,
|
||||
version="draft4",
|
||||
id_of=lambda schema: schema.get(u"id", ""),
|
||||
)
|
||||
|
||||
Draft6Validator = create(
|
||||
meta_schema=_utils.load_schema("draft6"),
|
||||
validators={
|
||||
u"$ref": _validators.ref,
|
||||
u"additionalItems": _validators.additionalItems,
|
||||
u"additionalProperties": _validators.additionalProperties,
|
||||
u"allOf": _validators.allOf,
|
||||
u"anyOf": _validators.anyOf,
|
||||
u"const": _validators.const,
|
||||
u"contains": _validators.contains,
|
||||
u"dependencies": _validators.dependencies,
|
||||
u"enum": _validators.enum,
|
||||
u"exclusiveMaximum": _validators.exclusiveMaximum,
|
||||
u"exclusiveMinimum": _validators.exclusiveMinimum,
|
||||
u"format": _validators.format,
|
||||
u"items": _validators.items,
|
||||
u"maxItems": _validators.maxItems,
|
||||
u"maxLength": _validators.maxLength,
|
||||
u"maxProperties": _validators.maxProperties,
|
||||
u"maximum": _validators.maximum,
|
||||
u"minItems": _validators.minItems,
|
||||
u"minLength": _validators.minLength,
|
||||
u"minProperties": _validators.minProperties,
|
||||
u"minimum": _validators.minimum,
|
||||
u"multipleOf": _validators.multipleOf,
|
||||
u"not": _validators.not_,
|
||||
u"oneOf": _validators.oneOf,
|
||||
u"pattern": _validators.pattern,
|
||||
u"patternProperties": _validators.patternProperties,
|
||||
u"properties": _validators.properties,
|
||||
u"propertyNames": _validators.propertyNames,
|
||||
u"required": _validators.required,
|
||||
u"type": _validators.type,
|
||||
u"uniqueItems": _validators.uniqueItems,
|
||||
},
|
||||
type_checker=_types.draft6_type_checker,
|
||||
version="draft6",
|
||||
)
|
||||
|
||||
Draft7Validator = create(
|
||||
meta_schema=_utils.load_schema("draft7"),
|
||||
validators={
|
||||
u"$ref": _validators.ref,
|
||||
u"additionalItems": _validators.additionalItems,
|
||||
u"additionalProperties": _validators.additionalProperties,
|
||||
u"allOf": _validators.allOf,
|
||||
u"anyOf": _validators.anyOf,
|
||||
u"const": _validators.const,
|
||||
u"contains": _validators.contains,
|
||||
u"dependencies": _validators.dependencies,
|
||||
u"enum": _validators.enum,
|
||||
u"exclusiveMaximum": _validators.exclusiveMaximum,
|
||||
u"exclusiveMinimum": _validators.exclusiveMinimum,
|
||||
u"format": _validators.format,
|
||||
u"if": _validators.if_,
|
||||
u"items": _validators.items,
|
||||
u"maxItems": _validators.maxItems,
|
||||
u"maxLength": _validators.maxLength,
|
||||
u"maxProperties": _validators.maxProperties,
|
||||
u"maximum": _validators.maximum,
|
||||
u"minItems": _validators.minItems,
|
||||
u"minLength": _validators.minLength,
|
||||
u"minProperties": _validators.minProperties,
|
||||
u"minimum": _validators.minimum,
|
||||
u"multipleOf": _validators.multipleOf,
|
||||
u"oneOf": _validators.oneOf,
|
||||
u"not": _validators.not_,
|
||||
u"pattern": _validators.pattern,
|
||||
u"patternProperties": _validators.patternProperties,
|
||||
u"properties": _validators.properties,
|
||||
u"propertyNames": _validators.propertyNames,
|
||||
u"required": _validators.required,
|
||||
u"type": _validators.type,
|
||||
u"uniqueItems": _validators.uniqueItems,
|
||||
},
|
||||
type_checker=_types.draft7_type_checker,
|
||||
version="draft7",
|
||||
)
|
||||
|
||||
_LATEST_VERSION = Draft7Validator
|
||||
|
||||
|
||||
class RefResolver(object):
|
||||
"""
|
||||
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=(),
|
||||
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(
|
||||
(id, validator.META_SCHEMA)
|
||||
for id, validator in iteritems(meta_schemas)
|
||||
)
|
||||
self.store.update(store)
|
||||
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)
|
||||
|
||||
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.
|
||||
"""
|
||||
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 resolve(self, ref):
|
||||
"""
|
||||
Resolve the given reference.
|
||||
"""
|
||||
url = self._urljoin_cache(self.resolution_scope, ref)
|
||||
return url, self._remote_cache(url)
|
||||
|
||||
def resolve_from_url(self, url):
|
||||
"""
|
||||
Resolve the given remote URL.
|
||||
"""
|
||||
url, fragment = urldefrag(url)
|
||||
try:
|
||||
document = self.store[url]
|
||||
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(u"/")
|
||||
parts = unquote(fragment).split(u"/") if fragment else []
|
||||
|
||||
for part in parts:
|
||||
part = part.replace(u"~1", u"/").replace(u"~0", u"~")
|
||||
|
||||
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(
|
||||
"Unresolvable JSON pointer: %r" % fragment
|
||||
)
|
||||
|
||||
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 [u"http", u"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
|
||||
|
||||
|
||||
def validate(instance, schema, cls=None, *args, **kwargs):
|
||||
"""
|
||||
Validate an instance under the given schema.
|
||||
|
||||
>>> validate([2, 3, 4], {"maxItems": 2})
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [2, 3, 4] is too long
|
||||
|
||||
:func:`validate` will first verify that the provided schema is
|
||||
itself valid, since not doing so can lead to less obvious error
|
||||
messages and fail in less obvious or consistent ways.
|
||||
|
||||
If you know you have a valid schema already, especially if you
|
||||
intend to validate multiple instances with the same schema, you
|
||||
likely would prefer using the `IValidator.validate` method directly
|
||||
on a specific validator (e.g. ``Draft7Validator.validate``).
|
||||
|
||||
|
||||
Arguments:
|
||||
|
||||
instance:
|
||||
|
||||
The instance to validate
|
||||
|
||||
schema:
|
||||
|
||||
The schema to validate with
|
||||
|
||||
cls (IValidator):
|
||||
|
||||
The class that will be used to validate the instance.
|
||||
|
||||
If the ``cls`` argument is not provided, two things will happen
|
||||
in accordance with the specification. First, if the schema has a
|
||||
:validator:`$schema` property containing a known meta-schema [#]_
|
||||
then the proper validator will be used. The specification recommends
|
||||
that all schemas contain :validator:`$schema` properties for this
|
||||
reason. If no :validator:`$schema` property is found, the default
|
||||
validator class is the latest released draft.
|
||||
|
||||
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=_LATEST_VERSION):
|
||||
"""
|
||||
Retrieve the validator class appropriate for validating the given schema.
|
||||
|
||||
Uses the :validator:`$schema` property that should be present in the
|
||||
given schema to look up the appropriate validator class.
|
||||
|
||||
Arguments:
|
||||
|
||||
schema (collections.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.
|
||||
"""
|
||||
if schema is True or schema is False or u"$schema" not in schema:
|
||||
return default
|
||||
if schema[u"$schema"] not in meta_schemas:
|
||||
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[u"$schema"], _LATEST_VERSION)
|
Loading…
Add table
Add a link
Reference in a new issue