Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions schema_salad/ref_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,31 +411,29 @@ def resolve_ref(
if not base_url:
base_url = file_uri(os.getcwd()) + "/"

sl = SourceLine(None, None)
# If `ref` is a dict, look for special directives.
if isinstance(lref, CommentedMap):
obj = lref
if "$import" in obj:
sl = SourceLine(obj, "$import")
if len(obj) == 1:
lref = obj["$import"]
obj = None
else:
raise ValidationException(
f"'$import' must be the only field in {obj}", sl
f"'$import' must be the only field in {obj}",
SourceLine(obj, "$import"),
)
elif "$include" in obj:
sl = SourceLine(obj, "$include")
if len(obj) == 1:
lref = obj["$include"]
inc = True
obj = None
else:
raise ValidationException(
f"'$include' must be the only field in {obj}", sl
f"'$include' must be the only field in {obj}",
SourceLine(obj, "$include"),
)
elif "$mixin" in obj:
sl = SourceLine(obj, "$mixin")
lref = obj["$mixin"]
mixin = obj
obj = None
Expand All @@ -450,7 +448,7 @@ def resolve_ref(
"Object `{}` does not have identifier field in {}".format(
obj, self.identifiers
),
sl,
SourceLine(obj),
)

if not isinstance(lref, str):
Expand Down Expand Up @@ -514,7 +512,8 @@ def resolve_ref(
# so if we didn't find the reference earlier then it must not
# exist.
raise ValidationException(
f"Reference `#{frg}` not found in file `{doc_url}`.", sl
f"Reference `#{frg}` not found in file `{doc_url}`.",
SourceLine(self.idx[doc_url]),
)
doc = self.fetch(
doc_url, inject_ids=(not mixin), content_types=content_types
Expand Down Expand Up @@ -592,11 +591,10 @@ def _resolve_idmap(
)
v.lc.filename = document.lc.filename
else:
sl = SourceLine(document, idmapField, str)
raise ValidationException(
"mapSubject '{}' value '{}' is not a dict "
"and does not have a mapPredicate.".format(k, v),
sl,
SourceLine(document, idmapField),
)
else:
v = val
Expand Down Expand Up @@ -852,7 +850,7 @@ def resolve_all(
else:
return (document, metadata)

newctx = None # type: Optional["Loader"]
newctx: Optional["Loader"] = None
if isinstance(document, CommentedMap):
# Handle $base, $profile, $namespaces, $schemas and $graph
if "$base" in document:
Expand All @@ -867,12 +865,26 @@ def resolve_all(
if "$namespaces" in document:
if newctx is None:
newctx = SubLoader(self)
newctx.add_namespaces(document["$namespaces"])
namespaces = document["$namespaces"]
if isinstance(namespaces, dict):
newctx.add_namespaces(document["$namespaces"])
else:
raise ValidationException(
"$namespaces must be a dictionary",
SourceLine(document, "$namespaces"),
)

if "$schemas" in document:
if newctx is None:
newctx = SubLoader(self)
newctx.add_schemas(document["$schemas"], file_base)
schemas = document["$schemas"]
if isinstance(schemas, (list, str)):
newctx.add_schemas(schemas, file_base)
else:
raise ValidationException(
"$schemas must be a string or a list of string",
SourceLine(document, "$schemas"),
)

if newctx is not None:
loader = newctx
Expand Down Expand Up @@ -1118,14 +1130,13 @@ def validate_links(
iterator = enumerate(document)
elif isinstance(document, MutableMapping):
for d in self.url_fields:
sl = SourceLine(document, d, str)
try:
if d in document and d not in self.identity_links:
document[d] = self.validate_link(
d, document[d], docid, all_doc_ids
)
except SchemaSaladException as v:
v = v.with_sourceline(sl)
v = v.with_sourceline(SourceLine(document, d, str))
if d == "$schemas" or (
d in self.foreign_properties and not strict_foreign_properties
):
Expand Down
48 changes: 48 additions & 0 deletions schema_salad/tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
"""Tests of helpful error messages."""
import re

import pytest

import schema_salad
import schema_salad.main
from schema_salad.avro.schema import Names
from schema_salad.exceptions import ValidationException
from schema_salad.ref_resolver import Loader, file_uri
from schema_salad.schema import load_and_validate, load_schema
from schema_salad.sourceline import cmap

from .util import get_data

Expand Down Expand Up @@ -317,3 +322,46 @@ def test_bad_schema2() -> None:
path = get_data("tests/bad_schema2.yml")
assert path
assert 1 == schema_salad.main.main(argsl=[path])


def test_namespaces_type() -> None:
"""Confirm helpful error message when $namespaces is the wrong type."""
with pytest.raises(
ValidationException,
match=re.escape("test:1:1: $namespaces must be a dictionary"),
):
ldr, _, _, _ = schema_salad.schema.load_schema(
cmap(
{
"$base": "Y",
"name": "X",
"$namespaces": ["http://example.com/foo#"],
"$graph": [
{
"name": "ExampleType",
"type": "enum",
"symbols": ["asym", "bsym"],
}
],
}
)
)


def test_schemas_type() -> None:
"""Confirm helpful error message when $schemas is the wrong type."""
path = get_data("tests/EDAM.owl")
assert path
with pytest.raises(
ValidationException,
match=re.escape("test:1:1: $schemas must be a string or a list of string"),
):
ra, _ = Loader({}).resolve_all(
cmap(
{
"$schemas": {"wrong": file_uri(path)},
"edam:has_format": "edam:format_1915",
}
),
"",
)