diff --git a/pyproject.toml b/pyproject.toml index eee9653..dfb92ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,8 @@ authors = [{ name = "Sigurd Spieckermann", email = "sigurd.spieckermann@gmail.co readme = "README.md" dependencies = [ "jinja2>=3.0.0", - "jsonschema>=4.0.0" + "jsonschema>=4.18.0", + "referencing>=0.28.4" ] [project.urls] diff --git a/src/jinja2_jsonschema/extension.py b/src/jinja2_jsonschema/extension.py index c2c8a32..08b5dca 100644 --- a/src/jinja2_jsonschema/extension.py +++ b/src/jinja2_jsonschema/extension.py @@ -16,13 +16,16 @@ import jsonschema from jinja2 import TemplateNotFound from jinja2.ext import Extension +from referencing import Registry +from referencing import Resource +from referencing import Specification +from referencing.exceptions import Unresolvable from .errors import JsonSchemaExtensionError from .errors import LoaderNotFoundError from .errors import SchemaFileNotFoundError if TYPE_CHECKING: - from _typeshed import Incomplete from jinja2 import Environment __all__ = ["JsonSchemaExtension"] @@ -96,26 +99,28 @@ def __call__( jsonschema.validate( data, schema, - resolver=jsonschema.RefResolver( - "", - {}, - handlers={ - "file": self._resolve_local_schema, - "http": self._resolve_remote_schema, - "https": self._resolve_remote_schema, - }, - ), + registry=Registry(retrieve=self._resolve_schema), # type: ignore[call-arg] ) - except jsonschema.RefResolutionError as exc: - if isinstance(exc.__context__, JsonSchemaExtensionError): - raise exc.__context__ from None + except Unresolvable as exc: + _exc: BaseException = exc + while _exc.__context__: + if isinstance(_exc.__context__, JsonSchemaExtensionError): + raise _exc.__context__ from None + _exc = _exc.__context__ raise except jsonschema.ValidationError as exc: return exc return "" - def _resolve_local_schema(self, uri: str) -> Incomplete: + def _resolve_schema(self, uri: str) -> Resource[Any]: + if uri.startswith(("http://", "https://")): + return self._resolve_schema_from_remote(uri) + if uri.startswith("file://"): + return self._resolve_schema_from_local(uri) + raise SchemaFileNotFoundError(uri) + + def _resolve_schema_from_local(self, uri: str) -> Resource[Any]: if not self._environment.loader: raise LoaderNotFoundError @@ -130,7 +135,7 @@ def _resolve_local_schema(self, uri: str) -> Incomplete: return self._load(raw_schema) - def _resolve_remote_schema(self, uri: str) -> Incomplete: + def _resolve_schema_from_remote(self, uri: str) -> Resource[Any]: try: with urlopen(uri) as response: # noqa: S310 raw_schema = response.read().decode("utf-8") @@ -141,7 +146,7 @@ def _resolve_remote_schema(self, uri: str) -> Incomplete: return self._load(raw_schema) @staticmethod - def _load(raw_schema: str) -> _Schema: + def _load(raw_schema: str) -> Resource[Any]: schema: _Schema try: import yaml @@ -149,4 +154,7 @@ def _load(raw_schema: str) -> _Schema: schema = json.loads(raw_schema) else: schema = yaml.safe_load(raw_schema) - return schema + return Resource.from_contents( + schema, + default_specification=Specification.OPAQUE, + ) diff --git a/uv.lock b/uv.lock index ce2651f..cf0a7a0 100644 --- a/uv.lock +++ b/uv.lock @@ -149,14 +149,14 @@ wheels = [ [[package]] name = "jinja2" -version = "3.1.4" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/0c/23cbcf515b5394e9f59a3e6629f26e1142b92d474ee0725a26aa5a3bcf76/Jinja2-3.0.0.tar.gz", hash = "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5", size = 267417 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + { url = "https://files.pythonhosted.org/packages/48/9b/dc3bbfc44d851632df958acf9d47e4de662c6bbd238e46798d555d427b27/Jinja2-3.0.0-py3-none-any.whl", hash = "sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6", size = 133357 }, ] [[package]] @@ -166,6 +166,7 @@ source = { editable = "." } dependencies = [ { name = "jinja2" }, { name = "jsonschema" }, + { name = "referencing" }, ] [package.optional-dependencies] @@ -189,8 +190,9 @@ dev = [ [package.metadata] requires-dist = [ { name = "jinja2", specifier = ">=3.0.0" }, - { name = "jsonschema", specifier = ">=4.0.0" }, + { name = "jsonschema", specifier = ">=4.18.0" }, { name = "pyyaml", marker = "extra == 'yaml'", specifier = ">=6.0.0" }, + { name = "referencing", specifier = ">=0.28.4" }, ] [package.metadata.requires-dev] @@ -208,7 +210,7 @@ dev = [ [[package]] name = "jsonschema" -version = "4.23.0" +version = "4.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -216,21 +218,21 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +sdist = { url = "https://files.pythonhosted.org/packages/79/01/ce65847f79de9594d436b7ef295e374dfcc0ff3ee884bf61cf896f970ddb/jsonschema-4.18.0.tar.gz", hash = "sha256:8caf5b57a990a98e9b39832ef3cb35c176fe331414252b6e1b26fd5866f891a4", size = 315551 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, + { url = "https://files.pythonhosted.org/packages/8a/38/2c55180702a637be0fbb8aa95358213a750d25cad3e59869726a54309996/jsonschema-4.18.0-py3-none-any.whl", hash = "sha256:b508dd6142bd03f4c3670534c80af68cd7bbff9ea830b9cf2625d4a3c49ddf60", size = 81450 }, ] [[package]] name = "jsonschema-specifications" -version = "2024.10.1" +version = "2023.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +sdist = { url = "https://files.pythonhosted.org/packages/12/ce/eb5396b34c28cbac19a6a8632f0e03d309135d77285536258b82120198d8/jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb", size = 12689 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, + { url = "https://files.pythonhosted.org/packages/1c/24/83349ac2189cc2435e84da3f69ba3c97314d3c0622628e55171c6798ed80/jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", size = 17835 }, ] [[package]] @@ -508,15 +510,15 @@ wheels = [ [[package]] name = "referencing" -version = "0.35.1" +version = "0.28.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991 } +sdist = { url = "https://files.pythonhosted.org/packages/79/c6/80e0866cbd52b4fdf1870f45b2287afd727c82d6dfc3b1566b21385380df/referencing-0.28.4.tar.gz", hash = "sha256:f2c5eeb2094fe7780000835d7aa2a0a3105ceaa8754dc69d3b2c4e19ecb9a47f", size = 32107 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684 }, + { url = "https://files.pythonhosted.org/packages/ff/d4/b63f2c2c48ec197d1fd6c467c18fea640ceb42ad60ba62ba816590403c0a/referencing-0.28.4-py3-none-any.whl", hash = "sha256:e6ac674c315502aa32c91e58d9f85caa31c48d5ae54ba34c827728ffd0b5178f", size = 22416 }, ] [[package]]