Skip to content

Commit

Permalink
refactor: resolve $refs via the referencing library
Browse files Browse the repository at this point in the history
  • Loading branch information
sisp committed Oct 16, 2024
1 parent 869a735 commit 2362306
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 31 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
42 changes: 25 additions & 17 deletions src/jinja2_jsonschema/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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

Expand All @@ -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")
Expand All @@ -141,12 +146,15 @@ 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
except ImportError:
schema = json.loads(raw_schema)
else:
schema = yaml.safe_load(raw_schema)
return schema
return Resource.from_contents(
schema,
default_specification=Specification.OPAQUE,
)
28 changes: 15 additions & 13 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2362306

Please sign in to comment.