Spaces:
Paused
Paused
| """ | |
| Python representations of the JSON Schema Test Suite tests. | |
| """ | |
| from __future__ import annotations | |
| from contextlib import suppress | |
| from functools import partial | |
| from pathlib import Path | |
| from typing import TYPE_CHECKING, Any | |
| import json | |
| import os | |
| import re | |
| import subprocess | |
| import sys | |
| import unittest | |
| from attrs import field, frozen | |
| from referencing import Registry | |
| import referencing.jsonschema | |
| if TYPE_CHECKING: | |
| from collections.abc import Iterable, Mapping | |
| import pyperf | |
| from jsonschema.validators import _VALIDATORS | |
| import jsonschema | |
| _DELIMITERS = re.compile(r"[\W\- ]+") | |
| def _find_suite(): | |
| root = os.environ.get("JSON_SCHEMA_TEST_SUITE") | |
| if root is not None: | |
| return Path(root) | |
| root = Path(jsonschema.__file__).parent.parent / "json" | |
| if not root.is_dir(): # 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 | |
| class Suite: | |
| _root: Path = field(factory=_find_suite) | |
| _remotes: referencing.jsonschema.SchemaRegistry = field(init=False) | |
| def __attrs_post_init__(self): | |
| jsonschema_suite = self._root.joinpath("bin", "jsonschema_suite") | |
| argv = [sys.executable, str(jsonschema_suite), "remotes"] | |
| remotes = subprocess.check_output(argv).decode("utf-8") | |
| resources = json.loads(remotes) | |
| li = "http://localhost:1234/locationIndependentIdentifierPre2019.json" | |
| li4 = "http://localhost:1234/locationIndependentIdentifierDraft4.json" | |
| registry = Registry().with_resources( | |
| [ | |
| ( | |
| li, | |
| referencing.jsonschema.DRAFT7.create_resource( | |
| contents=resources.pop(li), | |
| ), | |
| ), | |
| ( | |
| li4, | |
| referencing.jsonschema.DRAFT4.create_resource( | |
| contents=resources.pop(li4), | |
| ), | |
| ), | |
| ], | |
| ).with_contents( | |
| resources.items(), | |
| default_specification=referencing.jsonschema.DRAFT202012, | |
| ) | |
| object.__setattr__(self, "_remotes", registry) | |
| def benchmark(self, runner: pyperf.Runner): # pragma: no cover | |
| for name, Validator in _VALIDATORS.items(): | |
| self.version(name=name).benchmark( | |
| runner=runner, | |
| Validator=Validator, | |
| ) | |
| def version(self, name) -> Version: | |
| return Version( | |
| name=name, | |
| path=self._root / "tests" / name, | |
| remotes=self._remotes, | |
| ) | |
| class Version: | |
| _path: Path | |
| _remotes: referencing.jsonschema.SchemaRegistry | |
| name: str | |
| def benchmark(self, **kwargs): # pragma: no cover | |
| for case in self.cases(): | |
| case.benchmark(**kwargs) | |
| def cases(self) -> Iterable[_Case]: | |
| return self._cases_in(paths=self._path.glob("*.json")) | |
| def format_cases(self) -> Iterable[_Case]: | |
| return self._cases_in(paths=self._path.glob("optional/format/*.json")) | |
| def optional_cases_of(self, name: str) -> Iterable[_Case]: | |
| return self._cases_in(paths=[self._path / "optional" / f"{name}.json"]) | |
| def to_unittest_testcase(self, *groups, **kwargs): | |
| name = kwargs.pop("name", "Test" + self.name.title().replace("-", "")) | |
| methods = { | |
| method.__name__: method | |
| for method in ( | |
| test.to_unittest_method(**kwargs) | |
| for group in groups | |
| for case in group | |
| for test in case.tests | |
| ) | |
| } | |
| cls = type(name, (unittest.TestCase,), methods) | |
| # We're doing crazy things, so if they go wrong, like a function | |
| # behaving differently on some other interpreter, just make them | |
| # not happen. | |
| with suppress(Exception): | |
| cls.__module__ = _someone_save_us_the_module_of_the_caller() | |
| return cls | |
| def _cases_in(self, paths: Iterable[Path]) -> Iterable[_Case]: | |
| for path in paths: | |
| for case in json.loads(path.read_text(encoding="utf-8")): | |
| yield _Case.from_dict( | |
| case, | |
| version=self, | |
| subject=path.stem, | |
| remotes=self._remotes, | |
| ) | |
| class _Case: | |
| version: Version | |
| subject: str | |
| description: str | |
| schema: Mapping[str, Any] | bool | |
| tests: list[_Test] | |
| comment: str | None = None | |
| def from_dict(cls, data, remotes, **kwargs): | |
| data.update(kwargs) | |
| tests = [ | |
| _Test( | |
| version=data["version"], | |
| subject=data["subject"], | |
| case_description=data["description"], | |
| schema=data["schema"], | |
| remotes=remotes, | |
| **test, | |
| ) for test in data.pop("tests") | |
| ] | |
| return cls(tests=tests, **data) | |
| def benchmark(self, runner: pyperf.Runner, **kwargs): # pragma: no cover | |
| for test in self.tests: | |
| runner.bench_func( | |
| test.fully_qualified_name, | |
| partial(test.validate_ignoring_errors, **kwargs), | |
| ) | |
| class _Test: | |
| version: Version | |
| subject: str | |
| case_description: str | |
| description: str | |
| data: Any | |
| schema: Mapping[str, Any] | bool | |
| valid: bool | |
| _remotes: referencing.jsonschema.SchemaRegistry | |
| comment: str | None = None | |
| def __repr__(self): # pragma: no cover | |
| return f"<Test {self.fully_qualified_name}>" | |
| def fully_qualified_name(self): # pragma: no cover | |
| return " > ".join( # noqa: FLY002 | |
| [ | |
| self.version.name, | |
| self.subject, | |
| self.case_description, | |
| self.description, | |
| ], | |
| ) | |
| 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__ = "_".join( | |
| [ | |
| "test", | |
| _DELIMITERS.sub("_", self.subject), | |
| _DELIMITERS.sub("_", self.case_description), | |
| _DELIMITERS.sub("_", self.description), | |
| ], | |
| ) | |
| reason = skip(self) | |
| if reason is None or os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": | |
| return fn | |
| elif os.environ.get("JSON_SCHEMA_EXPECTED_FAILURES", "0") != "0": # pragma: no cover # noqa: E501 | |
| return unittest.expectedFailure(fn) | |
| else: | |
| return unittest.skip(reason)(fn) | |
| def validate(self, Validator, **kwargs): | |
| Validator.check_schema(self.schema) | |
| validator = Validator( | |
| schema=self.schema, | |
| registry=self._remotes, | |
| **kwargs, | |
| ) | |
| if os.environ.get("JSON_SCHEMA_DEBUG", "0") != "0": # pragma: no cover | |
| breakpoint() # noqa: T100 | |
| validator.validate(instance=self.data) | |
| def validate_ignoring_errors(self, Validator): # pragma: no cover | |
| with suppress(jsonschema.ValidationError): | |
| self.validate(Validator=Validator) | |
| 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 dynamically 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__"] | |