Skip to content

Commit 2d6f144

Browse files
authored
Merge pull request #295 from python-jsonschema/add-remote-yaml-support
Add support for remote YAML/TOML/JSON5 schemas
2 parents 76b19fe + da3a6af commit 2d6f144

File tree

5 files changed

+34
-15
lines changed

5 files changed

+34
-15
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ Unreleased
2020
non-JSON format supported by `check-jsonschema`. The file type is inferred
2121
only from the file extension in these cases and defaults to JSON if there is
2222
no recognizable extension.
23+
- Remote schemafiles (http/s) now support YAML, TOML, and JSON5 formats, if the
24+
URL ends with the appropriate extension and the matching parser is available.
25+
Extensionless URLs are treated as JSON.
2326

2427
0.23.3
2528
------

src/check_jsonschema/checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def _fail(self, msg: str, err: Exception | None = None) -> t.NoReturn:
4848

4949
def get_validator(
5050
self, path: pathlib.Path, doc: dict[str, t.Any]
51-
) -> jsonschema.Validator:
51+
) -> jsonschema.protocols.Validator:
5252
try:
5353
return self._schema_loader.get_validator(
5454
path, doc, self._format_opts, self._fill_defaults

src/check_jsonschema/schema_loader/main.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818

1919
def _extend_with_default(
20-
validator_class: type[jsonschema.Validator],
20+
validator_class: type[jsonschema.protocols.Validator],
2121
) -> type[jsonschema.Validator]:
2222
validate_properties = validator_class.VALIDATORS["properties"]
2323

@@ -51,7 +51,7 @@ def get_validator(
5151
instance_doc: dict[str, t.Any],
5252
format_opts: FormatOptions,
5353
fill_defaults: bool,
54-
) -> jsonschema.Validator:
54+
) -> jsonschema.protocols.Validator:
5555
raise NotImplementedError
5656

5757

@@ -112,7 +112,7 @@ def get_validator(
112112
instance_doc: dict[str, t.Any],
113113
format_opts: FormatOptions,
114114
fill_defaults: bool,
115-
) -> jsonschema.Validator:
115+
) -> jsonschema.protocols.Validator:
116116
retrieval_uri = self.get_schema_retrieval_uri()
117117
schema = self.get_schema()
118118

@@ -141,7 +141,7 @@ def get_validator(
141141
registry=reference_registry,
142142
format_checker=format_checker,
143143
)
144-
return t.cast(jsonschema.Validator, validator)
144+
return t.cast(jsonschema.protocols.Validator, validator)
145145

146146

147147
class BuiltinSchemaLoader(SchemaLoader):
@@ -163,7 +163,7 @@ def get_validator(
163163
instance_doc: dict[str, t.Any],
164164
format_opts: FormatOptions,
165165
fill_defaults: bool,
166-
) -> jsonschema.Validator:
166+
) -> jsonschema.protocols.Validator:
167167
schema_validator = jsonschema.validators.validator_for(instance_doc)
168168
meta_validator_class = jsonschema.validators.validator_for(
169169
schema_validator.META_SCHEMA, default=schema_validator

src/check_jsonschema/schema_loader/readers.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
import json
3+
import io
44
import typing as t
55

66
import ruamel.yaml
@@ -48,19 +48,28 @@ def __init__(
4848
disable_cache: bool,
4949
) -> None:
5050
self.url = url
51+
self.parsers = ParserSet()
5152
self.downloader = CacheDownloader(
5253
url,
5354
cache_filename,
5455
disable_cache=disable_cache,
55-
validation_callback=json.loads,
56+
validation_callback=self._parse,
5657
)
58+
self._parsed_schema: t.Any | None = None
59+
60+
def _parse(self, schema_bytes: bytes) -> t.Any:
61+
if self._parsed_schema is None:
62+
self._parsed_schema = self.parsers.parse_data_with_path(
63+
io.BytesIO(schema_bytes), self.url, default_filetype="json"
64+
)
65+
return self._parsed_schema
5766

5867
def get_retrieval_uri(self) -> str:
5968
return self.url
6069

6170
def _read_impl(self) -> t.Any:
6271
with self.downloader.open() as fp:
63-
return json.load(fp)
72+
return self._parse(fp.read())
6473

6574
def read_schema(self) -> dict:
6675
return _run_load_callback(self.url, self._read_impl)

tests/unit/test_schema_loader.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pathlib
33

44
import pytest
5+
import responses
56

67
from check_jsonschema.schema_loader import SchemaLoader, SchemaParseError
78
from check_jsonschema.schema_loader.readers import HttpSchemaReader, LocalSchemaReader
@@ -40,12 +41,12 @@ def test_schemaloader_path_handling_relative_local_path(in_tmp_dir, filename):
4041
[
4142
"schema.yaml",
4243
"schema.yml",
44+
"https://foo.example.com/schema.yaml",
45+
"https://foo.example.com/schema.yml",
4346
],
4447
)
45-
def test_schemaloader_local_yaml_data(tmp_path, filename):
46-
f = tmp_path / filename
47-
f.write_text(
48-
"""
48+
def test_schemaloader_yaml_data(tmp_path, filename):
49+
schema_text = """
4950
---
5051
"$schema": https://json-schema.org/draft/2020-12/schema
5152
type: object
@@ -60,8 +61,14 @@ def test_schemaloader_local_yaml_data(tmp_path, filename):
6061
c:
6162
type: string
6263
"""
63-
)
64-
sl = SchemaLoader(str(f))
64+
if filename.startswith("http"):
65+
responses.add("GET", filename, body=schema_text)
66+
path = filename
67+
else:
68+
f = tmp_path / filename
69+
f.write_text(schema_text)
70+
path = str(f)
71+
sl = SchemaLoader(path)
6572
schema = sl.get_schema()
6673
assert schema == {
6774
"$schema": "https://json-schema.org/draft/2020-12/schema",

0 commit comments

Comments
 (0)