diff --git a/.gitmodules b/.gitmodules index c5b41b0..c46289c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "JSON-Schema-Test-Suite"] path = tests/JSON-Schema-Test-Suite url = https://github.com/json-schema-org/JSON-Schema-Test-Suite - branch = master + branch = main [submodule "json-schema-spec-2019-09"] path = jschon/catalog/json-schema-spec-2019-09 url = https://github.com/json-schema-org/json-schema-spec diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 294c993..91eedbd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,10 @@ Features: * JSON ``null``, ``true``, ``false`` literals * Relative JSON Pointer ``+``/``-`` array index adjustments * Unknown keywords are collected as annotations +* Automatically create metaschemas as referenced by ``"$schema"`` + on first access of the now-cached ``JSONSchema.metaschema`` property +* Automatically detect the core vocabulary in metaschemas, + but allow specifying a default to use when none is detectable Experimental: @@ -43,6 +47,11 @@ Miscellaneous: * Add ``JSONCompatible`` and ``Result`` classes to the top-level package API * Remove implicit fall-through to looking up a schema in the `__meta__` cache if not found in the parameterized cache, in ``Catalog.get_schema()`` (#40) +* Added ``Catalog.get_metaschema()``, analogous to ``Catalog.get_schema()`` +* ``Catalog.create_metaschema()`` and ``Catalog.create_vocabulary()`` return the created instance +* Rename ``core_vocabulary`` and ``core_vocabulary_uri`` parameters for + ``Metaschema.__init__()`` and ``Catalog.create_metaschema()`` respectively to + ``default_core_vocabulary`` and ``default_core_vocabulary_uri`` * Improve kwarg-based construction of ``RelativeJSONPointer`` * Allow passthrough of arguments to pytest when invoking tox * Add pytest command line options ``--testsuite-file`` and ``--testsuite-description`` diff --git a/examples/custom_keyword.py b/examples/custom_keyword.py index 126d87b..7b7ed6d 100644 --- a/examples/custom_keyword.py +++ b/examples/custom_keyword.py @@ -62,20 +62,6 @@ def evaluate(self, instance: JSON, result: Result) -> None: EnumRefKeyword, ) -# compile the enumRef metaschema, which enables any referencing schema -# to use the keyword implementations provided by its vocabularies -catalog.create_metaschema( - URI("https://example.com/enumRef/enumRef-metaschema"), - URI("https://json-schema.org/draft/2020-12/vocab/core"), - URI("https://json-schema.org/draft/2020-12/vocab/applicator"), - URI("https://json-schema.org/draft/2020-12/vocab/unevaluated"), - URI("https://json-schema.org/draft/2020-12/vocab/validation"), - URI("https://json-schema.org/draft/2020-12/vocab/format-annotation"), - URI("https://json-schema.org/draft/2020-12/vocab/meta-data"), - URI("https://json-schema.org/draft/2020-12/vocab/content"), - URI("https://example.com/enumRef"), -) - # create a schema for validating that a string is a member of a remote enumeration schema = JSONSchema({ "$schema": "https://example.com/enumRef/enumRef-metaschema", diff --git a/jschon/catalog/_2019_09.py b/jschon/catalog/_2019_09.py index 52e347d..4814088 100644 --- a/jschon/catalog/_2019_09.py +++ b/jschon/catalog/_2019_09.py @@ -96,13 +96,3 @@ def initialize(catalog: Catalog): ContentEncodingKeyword, ContentSchemaKeyword, ) - - catalog.create_metaschema( - URI("https://json-schema.org/draft/2019-09/schema"), - URI("https://json-schema.org/draft/2019-09/vocab/core"), - URI("https://json-schema.org/draft/2019-09/vocab/applicator"), - URI("https://json-schema.org/draft/2019-09/vocab/validation"), - URI("https://json-schema.org/draft/2019-09/vocab/format"), - URI("https://json-schema.org/draft/2019-09/vocab/meta-data"), - URI("https://json-schema.org/draft/2019-09/vocab/content"), - ) diff --git a/jschon/catalog/_2020_12.py b/jschon/catalog/_2020_12.py index 76d62a5..b3ebed8 100644 --- a/jschon/catalog/_2020_12.py +++ b/jschon/catalog/_2020_12.py @@ -99,14 +99,3 @@ def initialize(catalog: Catalog): ContentEncodingKeyword, ContentSchemaKeyword, ) - - catalog.create_metaschema( - URI("https://json-schema.org/draft/2020-12/schema"), - URI("https://json-schema.org/draft/2020-12/vocab/core"), - URI("https://json-schema.org/draft/2020-12/vocab/applicator"), - URI("https://json-schema.org/draft/2020-12/vocab/unevaluated"), - URI("https://json-schema.org/draft/2020-12/vocab/validation"), - URI("https://json-schema.org/draft/2020-12/vocab/format-annotation"), - URI("https://json-schema.org/draft/2020-12/vocab/meta-data"), - URI("https://json-schema.org/draft/2020-12/vocab/content"), - ) diff --git a/jschon/catalog/__init__.py b/jschon/catalog/__init__.py index ba854af..1200524 100644 --- a/jschon/catalog/__init__.py +++ b/jschon/catalog/__init__.py @@ -86,6 +86,10 @@ def __init__(self, name: str = 'catalog') -> None: self._schema_cache: Dict[Hashable, Dict[URI, JSONSchema]] = {} self._enabled_formats: Set[str] = set() + def __repr__(self) -> str: + """Return `repr(self)`.""" + return f'{self.__class__.__name__}({self.name!r})' + def add_uri_source(self, base_uri: URI, source: Source): """Register a source for URI-identified JSON resources. @@ -137,7 +141,7 @@ def load_json(self, uri: URI) -> JSONCompatible: raise CatalogError(f'A source is not available for "{uri}"') - def create_vocabulary(self, uri: URI, *kwclasses: KeywordClass) -> None: + def create_vocabulary(self, uri: URI, *kwclasses: KeywordClass) -> Vocabulary: """Create a :class:`~jschon.vocabulary.Vocabulary` object, which may be used by a :class:`~jschon.vocabulary.Metaschema` to provide keyword classes used in schema construction. @@ -145,8 +149,11 @@ def create_vocabulary(self, uri: URI, *kwclasses: KeywordClass) -> None: :param uri: the URI identifying the vocabulary :param kwclasses: the :class:`~jschon.vocabulary.Keyword` classes constituting the vocabulary + + :returns: the newly created :class:`~jschon.vocabulary.Vocabulary` instance """ self._vocabularies[uri] = Vocabulary(uri, *kwclasses) + return self._vocabularies[uri] def get_vocabulary(self, uri: URI) -> Vocabulary: """Get a :class:`~jschon.vocabulary.Vocabulary` by its `uri`. @@ -162,23 +169,33 @@ def get_vocabulary(self, uri: URI) -> Vocabulary: def create_metaschema( self, uri: URI, - core_vocabulary_uri: URI, + default_core_vocabulary_uri: URI = None, *default_vocabulary_uris: URI, **kwargs: Any, - ) -> None: + ) -> Metaschema: """Create, cache and validate a :class:`~jschon.vocabulary.Metaschema`. :param uri: the URI identifying the metaschema - :param core_vocabulary_uri: the URI identifying the metaschema's - core :class:`~jschon.vocabulary.Vocabulary` + :param default_core_vocabulary_uri: the URI identifying the metaschema's + core :class:`~jschon.vocabulary.Vocabulary`, used in the absence + of a ``"$vocabulary"`` keyword in the metaschema JSON file, or + if a known core vocabulary is not present under ``"$vocabulary"`` :param default_vocabulary_uris: default :class:`~jschon.vocabulary.Vocabulary` URIs, used in the absence of a ``"$vocabulary"`` keyword in the metaschema JSON file :param kwargs: additional keyword arguments to pass through to the :class:`~jschon.jsonschema.JSONSchema` constructor + + :returns: the newly created :class:`~jschon.vocabulary.Metaschema` instance + + :raise CatalogError: if the metaschema is not valid """ metaschema_doc = self.load_json(uri) - core_vocabulary = self.get_vocabulary(core_vocabulary_uri) + default_core_vocabulary = ( + self.get_vocabulary(default_core_vocabulary_uri) + if default_core_vocabulary_uri + else None + ) default_vocabularies = [ self.get_vocabulary(vocab_uri) for vocab_uri in default_vocabulary_uris @@ -186,13 +203,44 @@ def create_metaschema( metaschema = Metaschema( self, metaschema_doc, - core_vocabulary, + default_core_vocabulary, *default_vocabularies, **kwargs, uri=uri, ) if not metaschema.validate().valid: - raise CatalogError("The metaschema is invalid against itself") + raise CatalogError( + "The metaschema is invalid against its own metaschema " + f'"{metaschema_doc["$schema"]}"' + ) + return metaschema + + def get_metaschema(self, uri: URI) -> Metaschema: + """Get a metaschema identified by `uri` from the ``'__meta__'`` cache, or + load it from configured sources if not already cached. + + Note that metaschemas that do not declare a known core vocabulary + in ``"$vocabulary"`` must first be created using :meth:`create_metaschema`. + + :param uri: the URI identifying the metaschema + + :raise CatalogError: if the object referenced by `uri` is not + a :class:`~jschon.vocabulary.Metaschema`, or if it is not valid + :raise JSONSchemaError: if the metaschema is loaded from sources + but no known core vocabulary is present in ``"$vocabulary"`` + """ + try: + metaschema = self._schema_cache['__meta__'][uri] + except KeyError: + metaschema = None + + if not metaschema: + metaschema = self.create_metaschema(uri) + + if not isinstance(metaschema, Metaschema): + raise CatalogError(f"The schema referenced by {uri} is not a metaschema") + + return metaschema def enable_formats(self, *format_attr: str) -> None: """Enable validation of the specified format attributes. diff --git a/jschon/catalog/_next.py b/jschon/catalog/_next.py index 794ad49..4dd699d 100644 --- a/jschon/catalog/_next.py +++ b/jschon/catalog/_next.py @@ -99,14 +99,3 @@ def initialize(catalog: Catalog): ContentEncodingKeyword, ContentSchemaKeyword, ) - - catalog.create_metaschema( - URI("https://json-schema.org/draft/next/schema"), - URI("https://json-schema.org/draft/next/vocab/core"), - URI("https://json-schema.org/draft/next/vocab/applicator"), - URI("https://json-schema.org/draft/next/vocab/unevaluated"), - URI("https://json-schema.org/draft/next/vocab/validation"), - URI("https://json-schema.org/draft/next/vocab/format-annotation"), - URI("https://json-schema.org/draft/next/vocab/meta-data"), - URI("https://json-schema.org/draft/next/vocab/content"), - ) diff --git a/jschon/jsonschema.py b/jschon/jsonschema.py index 30b407e..bb4e867 100644 --- a/jschon/jsonschema.py +++ b/jschon/jsonschema.py @@ -218,21 +218,13 @@ def parentschema(self) -> Optional[JSONSchema]: return parent parent = parent.parent - @property + @cached_property def metaschema(self) -> Metaschema: """The schema's :class:`~jschon.vocabulary.Metaschema`.""" - from jschon.vocabulary import Metaschema - if (uri := self.metaschema_uri) is None: raise JSONSchemaError("The schema's metaschema URI has not been set") - if not isinstance( - metaschema := self.catalog.get_schema(uri, cacheid='__meta__'), - Metaschema, - ): - raise JSONSchemaError(f"The schema referenced by {uri} is not a metachema") - - return metaschema + return self.catalog.get_metaschema(uri) @property def metaschema_uri(self) -> Optional[URI]: diff --git a/jschon/vocabulary/__init__.py b/jschon/vocabulary/__init__.py index 49fb173..5c67b56 100644 --- a/jschon/vocabulary/__init__.py +++ b/jschon/vocabulary/__init__.py @@ -1,10 +1,12 @@ from __future__ import annotations +import re import inspect from typing import Any, Dict, Mapping, Optional, Sequence, TYPE_CHECKING, Tuple, Type from jschon.json import JSON, JSONCompatible from jschon.jsonschema import JSONSchema, Result +from jschon.exceptions import JSONSchemaError from jschon.uri import URI if TYPE_CHECKING: @@ -30,17 +32,54 @@ class Metaschema(JSONSchema): :class:`Metaschema` is itself a subclass of :class:`~jschon.jsonschema.JSONSchema`, and may be used to validate any referencing schema. """ + _CORE_VOCAB_RE = r'https://json-schema\.org/draft/[^/]+/vocab/core$' def __init__( self, catalog: Catalog, value: Mapping[str, JSONCompatible], - core_vocabulary: Vocabulary, + default_core_vocabulary: Vocabulary = None, *default_vocabularies: Vocabulary, **kwargs: Any, ): - self.core_vocabulary: Vocabulary = core_vocabulary + """Initialize a :class:`Metaschema` instance from the given + schema-compatible `value`. + + :param catalog: catalog instance + :param value: a schema-compatible Python object + :param default_core_vocabulary: the metaschema's + core :class:`~jschon.vocabulary.Vocabulary`, used in the absence + of a ``"$vocabulary"`` keyword in the metaschema JSON file, or + if a known core vocabulary is not present under ``"$vocabulary"`` + :param default_vocabulary: default :class:`~jschon.vocabulary.Vocabulary` + instances, used in the absence of a ``"$vocabulary"`` keyword in the + metaschema JSON file + :param kwargs: additional keyword arguments to pass through to the + :class:`~jschon.jsonschema.JSONSchema` constructor + + :raise JSONSchemaError: if no core vocabulary can be determined + :raise CatalogError: if the created metaschema is not valid + """ self.default_vocabularies: Tuple[Vocabulary, ...] = default_vocabularies + self.core_vocabulary: Vocabulary = default_core_vocabulary + + if vocabularies := value.get("$vocabulary"): + possible_cores = list(filter( + lambda v: re.match(self._CORE_VOCAB_RE, v), + vocabularies, + )) + if len(possible_cores) == 1: + self.core_vocabulary = catalog.get_vocabulary(URI(possible_cores[0])) + else: + raise JSONSchemaError( + 'Cannot determine unique known core vocabulary from ' + f'candidates "{vocabularies.keys()}"' + ) + if self.core_vocabulary is None: + raise JSONSchemaError( + f'No core vocabulary in "$vocabulary": {value}, and no default provided' + ) + self.kwclasses: Dict[str, KeywordClass] = {} super().__init__(value, catalog=catalog, cacheid='__meta__', **kwargs) @@ -80,6 +119,10 @@ def __init__(self, uri: URI, *kwclasses: KeywordClass): kwclass.key: kwclass for kwclass in kwclasses } + def __repr__(self) -> str: + """Return `repr(self)`.""" + return f'{self.__class__.__name__}({self.uri!r})' + class Keyword: key: str = ... diff --git a/tests/JSON-Schema-Test-Suite b/tests/JSON-Schema-Test-Suite index 060caae..19947ea 160000 --- a/tests/JSON-Schema-Test-Suite +++ b/tests/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit 060caae0dd58e34af0449baec1103606a0ef4977 +Subproject commit 19947eaa1289168a49edd21bb7a8aa2098069ae0 diff --git a/tests/__init__.py b/tests/__init__.py index 5a9a8cb..4238d3c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -4,6 +4,10 @@ metaschema_uri_2020_12 = URI("https://json-schema.org/draft/2020-12/schema") metaschema_uri_next = URI("https://json-schema.org/draft/next/schema") +core_vocab_uri_2019_09 = URI("https://json-schema.org/draft/2019-09/vocab/core") +core_vocab_uri_2020_12 = URI("https://json-schema.org/draft/2020-12/vocab/core") +core_vocab_uri_next = URI("https://json-schema.org/draft/next/vocab/core") + example_schema = { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "dynamicRef8_main.json", diff --git a/tests/conftest.py b/tests/conftest.py index 98e1ee4..8adeda0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,7 +43,7 @@ def pytest_addoption(parser): ) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(autouse=True) def catalog(): return create_catalog('2019-09', '2020-12', 'next') diff --git a/tests/data/meta_invalid.json b/tests/data/meta_invalid.json new file mode 100644 index 0000000..d454faa --- /dev/null +++ b/tests/data/meta_invalid.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/meta_invalid", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + }, + "type": {"lol": "cats"} +} diff --git a/tests/data/meta_no_vocabs.json b/tests/data/meta_no_vocabs.json new file mode 100644 index 0000000..b49a8ca --- /dev/null +++ b/tests/data/meta_no_vocabs.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/meta_no_vocabs" +} diff --git a/tests/data/meta_with_core.json b/tests/data/meta_with_core.json new file mode 100644 index 0000000..d8f76d9 --- /dev/null +++ b/tests/data/meta_with_core.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/meta_with_core", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true + } +} diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 3e6be6e..236c793 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -2,6 +2,7 @@ import pathlib import tempfile import uuid +import itertools import pytest @@ -16,11 +17,28 @@ LocalSource, RemoteSource, ) -from tests import example_schema, metaschema_uri_2020_12 +from jschon.vocabulary import Metaschema, Keyword +from tests import example_schema, metaschema_uri_2020_12, core_vocab_uri_2020_12 json_example = {"foo": "bar"} +@pytest.fixture +def local_catalog(): + catalog = create_catalog( + '2019-09', '2020-12', 'next', + name='local' + ) + catalog.add_uri_source( + URI('https://example.com/'), + LocalSource( + pathlib.Path(__file__).parent / 'data', + suffix='.json', + ), + ) + return catalog + + @pytest.fixture def new_catalog() -> Catalog: return Catalog(name=str(uuid.uuid4())) @@ -126,6 +144,17 @@ def test_get_vocabulary(uri, is_known, catalog): catalog.get_vocabulary(URI(uri)) +def test_create_vocabulary(catalog): + class CustomKeyword(Keyword): + key = 'custom' + + custom_uri = URI('https://example.com/custom') + custom_vocab = catalog.create_vocabulary(custom_uri, CustomKeyword) + assert custom_vocab.uri is custom_uri + assert custom_vocab.kwclasses == {CustomKeyword.key: CustomKeyword} + assert catalog.get_vocabulary(custom_uri) is custom_vocab + + @pytest.fixture def example_schema_uri(): schema = JSONSchema(example_schema) @@ -169,9 +198,6 @@ def test_cache_independence(catalog): def test_metaschema_isolation(): - new_catalog = create_catalog('2019-09', '2020-12', name=str(uuid.uuid4())) - assert new_catalog._schema_cache.keys() == {'__meta__'} - # mask the metaschema with a boolean false schema, in the fubar cache cached_schema(metaschema_uri_2020_12, False, 'fubar') uri = URI("http://example.com") @@ -183,3 +209,67 @@ def test_metaschema_isolation(): assert okay_schema.evaluate(JSON(True)).valid is True okay_schema = cached_schema(uri, {"$ref": str(metaschema_uri_2020_12)}, None) assert okay_schema.evaluate(JSON(True)).valid is True + + +def test_get_metaschema_detect_core(local_catalog): + uri = URI('https://example.com/meta_with_core') + core_vocab = local_catalog.get_vocabulary(core_vocab_uri_2020_12) + + m = local_catalog.get_metaschema(uri) + assert isinstance(m, Metaschema) + assert m['$id'].data == str(uri) + assert m.core_vocabulary is core_vocab + assert m.kwclasses == core_vocab.kwclasses + + s = local_catalog.get_schema(uri) + assert isinstance(s, JSONSchema) + assert s is not m + assert s == m + + +def test_get_metaschema_wrong_type(local_catalog): + uri = URI('https://example.com/meta_with_core') + non_meta = local_catalog.get_schema(uri) + local_catalog.add_schema(uri, non_meta, cacheid='__meta__') + with pytest.raises(CatalogError, match='not a metaschema'): + local_catalog.get_metaschema(uri) + + +def test_get_metaschema_invalid(local_catalog): + uri = URI('https://example.com/meta_invalid') + with pytest.raises(CatalogError, match='metaschema is invalid'): + local_catalog.create_metaschema(uri) + + +def test_create_metaschema_no_vocabs(local_catalog): + class ExtraKeyword(Keyword): + key = 'extra' + + uri = URI('https://example.com/meta_no_vocabs') + core_vocab = local_catalog.get_vocabulary(core_vocab_uri_2020_12) + applicator_vocab = local_catalog.get_vocabulary( + URI('https://json-schema.org/draft/2020-12/vocab/applicator') + ) + + extra_vocab = local_catalog.create_vocabulary( + URI('https://example.com/vocab/whatever'), + ExtraKeyword, + ) + + m = local_catalog.create_metaschema( + uri, + core_vocab.uri, + applicator_vocab.uri, + extra_vocab.uri, + ) + assert isinstance(m, Metaschema) + assert m['$id'].data == str(uri) + assert m.core_vocabulary is core_vocab + assert m.kwclasses.keys() == frozenset( + itertools.chain.from_iterable([ + v.kwclasses.keys() for v in + [core_vocab, applicator_vocab, extra_vocab] + ]) + ) + m1 = local_catalog.get_metaschema(uri) + assert m1 is m diff --git a/tests/test_formats.py b/tests/test_formats.py index f26317c..a68127f 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -9,7 +9,7 @@ from tests.strategies import jsonpointer -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(autouse=True) def setup_validators(catalog): catalog.enable_formats( "ipv4", diff --git a/tests/test_metaschema.py b/tests/test_metaschema.py new file mode 100644 index 0000000..824face --- /dev/null +++ b/tests/test_metaschema.py @@ -0,0 +1,68 @@ +import pytest + +from jschon import JSONSchemaError, URI, create_catalog +from jschon.vocabulary import Metaschema +from tests import ( + metaschema_uri_2020_12, + core_vocab_uri_2019_09, core_vocab_uri_2020_12, core_vocab_uri_next, +) + +@pytest.mark.parametrize('vocab_data', [ + None, + { + 'https://example.com/whatever': True, + }, + { + str(core_vocab_uri_2020_12): True, + str(core_vocab_uri_next): True, + }, +]) +def test_metaschema_no_core(vocab_data): + catalog = create_catalog('2019-09', '2020-12', 'next') + + metaschema_id = 'https://example.com/no-core' + metaschema_data = { + '$schema': str(metaschema_uri_2020_12), + '$id': metaschema_id, + } + if vocab_data: + metaschema_data['$vocabulary'] = vocab_data + for vocab in vocab_data: + catalog.create_vocabulary(URI(vocab)) + + with pytest.raises(JSONSchemaError): + Metaschema(catalog, metaschema_data, uri=URI(metaschema_id)) + +def test_detect_core(catalog): + metaschema_id = 'https://example.com/meta' + metaschema_uri = URI(metaschema_id) + metaschema_data = { + '$schema': str(metaschema_uri_2020_12), + '$id': metaschema_id, + '$vocabulary': { + str(core_vocab_uri_2020_12): True, + }, + } + m = Metaschema(catalog, metaschema_data, uri=metaschema_uri) + + m1 = catalog._schema_cache['__meta__'][metaschema_uri] + m2 = catalog.get_metaschema(metaschema_uri) + + assert m1 is m + assert m2 is m + +def test_default_core(catalog): + metaschema_id = 'https://example.com/meta' + metaschema_uri = URI(metaschema_id) + metaschema_data = { + '$schema': str(metaschema_uri_2020_12), + '$id': metaschema_id, + } + core = catalog.get_vocabulary(core_vocab_uri_2020_12) + m = Metaschema( + catalog, + metaschema_data, + core, + uri=metaschema_uri, + ) + assert m.core_vocabulary is core diff --git a/tests/test_schema.py b/tests/test_schema.py index e0fdcb9..2349196 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -4,7 +4,7 @@ from hypothesis import given from pytest import param as p -from jschon import JSON, JSONPointer, JSONSchema, URI +from jschon import JSON, JSONPointer, JSONSchema, URI, create_catalog from jschon.json import false, true from tests import example_invalid, example_schema, example_valid, metaschema_uri_2019_09, metaschema_uri_2020_12 from tests.strategies import * @@ -54,8 +54,8 @@ def assert_keyword_order(keyword_list, keyword_pairs): @given(value=interdependent_keywords) -def test_keyword_dependency_resolution_2019_09(value: list, catalog): - metaschema = catalog.get_schema(metaschema_uri_2019_09, cacheid='__meta__') +def test_keyword_dependency_resolution_2019_09(value: list): + metaschema = create_catalog('2019-09').get_metaschema(metaschema_uri_2019_09) kwclasses = { key: kwclass for key in value if (kwclass := metaschema.kwclasses.get(key)) } @@ -96,8 +96,8 @@ def test_keyword_dependency_resolution_2019_09(value: list, catalog): @given(value=interdependent_keywords) -def test_keyword_dependency_resolution_2020_12(value: list, catalog): - metaschema = catalog.get_schema(metaschema_uri_2020_12, cacheid='__meta__') +def test_keyword_dependency_resolution_2020_12(value: list): + metaschema = create_catalog('2020-12').get_metaschema(metaschema_uri_2020_12) kwclasses = { key: kwclass for key in value if (kwclass := metaschema.kwclasses.get(key)) } diff --git a/tests/test_suite.py b/tests/test_suite.py index c02ba9f..0048697 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -3,7 +3,7 @@ import pytest -from jschon import JSON, JSONSchema, LocalSource, URI, create_catalog +from jschon import JSON, JSONSchema, LocalSource, URI from jschon.utils import json_loadf from tests import metaschema_uri_2019_09, metaschema_uri_2020_12, metaschema_uri_next @@ -11,35 +11,11 @@ @pytest.fixture(autouse=True) -def catalog(): - # replaces the catalog fixture in conftest, for this test module - catalog = create_catalog( - '2019-09', - '2020-12', - 'next', - ) +def setup_remotes(catalog): catalog.add_uri_source( URI('http://localhost:1234/'), LocalSource(testsuite_dir / 'remotes'), ) - catalog.create_metaschema( - URI('http://localhost:1234/draft2019-09/metaschema-no-validation.json'), - URI('https://json-schema.org/draft/2019-09/vocab/core'), - URI('https://json-schema.org/draft/2019-09/vocab/applicator'), - metaschema_uri=metaschema_uri_2019_09, - ) - catalog.create_metaschema( - URI('http://localhost:1234/draft2020-12/metaschema-no-validation.json'), - URI('https://json-schema.org/draft/2020-12/vocab/core'), - URI('https://json-schema.org/draft/2020-12/vocab/applicator'), - metaschema_uri=metaschema_uri_2020_12, - ) - catalog.create_metaschema( - URI('http://localhost:1234/draft-next/metaschema-no-validation.json'), - URI('https://json-schema.org/draft/next/vocab/core'), - URI('https://json-schema.org/draft/next/vocab/applicator'), - metaschema_uri=metaschema_uri_2020_12, - ) def pytest_generate_tests(metafunc):