Skip to content

Commit a3aaa0a

Browse files
committed
better errors for bad $schemas, $namespaces
1 parent 61e8f0b commit a3aaa0a

File tree

2 files changed

+74
-15
lines changed

2 files changed

+74
-15
lines changed

schema_salad/ref_resolver.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -411,31 +411,29 @@ def resolve_ref(
411411
if not base_url:
412412
base_url = file_uri(os.getcwd()) + "/"
413413

414-
sl = SourceLine(None, None)
415414
# If `ref` is a dict, look for special directives.
416415
if isinstance(lref, CommentedMap):
417416
obj = lref
418417
if "$import" in obj:
419-
sl = SourceLine(obj, "$import")
420418
if len(obj) == 1:
421419
lref = obj["$import"]
422420
obj = None
423421
else:
424422
raise ValidationException(
425-
f"'$import' must be the only field in {obj}", sl
423+
f"'$import' must be the only field in {obj}",
424+
SourceLine(obj, "$import"),
426425
)
427426
elif "$include" in obj:
428-
sl = SourceLine(obj, "$include")
429427
if len(obj) == 1:
430428
lref = obj["$include"]
431429
inc = True
432430
obj = None
433431
else:
434432
raise ValidationException(
435-
f"'$include' must be the only field in {obj}", sl
433+
f"'$include' must be the only field in {obj}",
434+
SourceLine(obj, "$include"),
436435
)
437436
elif "$mixin" in obj:
438-
sl = SourceLine(obj, "$mixin")
439437
lref = obj["$mixin"]
440438
mixin = obj
441439
obj = None
@@ -450,7 +448,7 @@ def resolve_ref(
450448
"Object `{}` does not have identifier field in {}".format(
451449
obj, self.identifiers
452450
),
453-
sl,
451+
SourceLine(obj),
454452
)
455453

456454
if not isinstance(lref, str):
@@ -514,7 +512,8 @@ def resolve_ref(
514512
# so if we didn't find the reference earlier then it must not
515513
# exist.
516514
raise ValidationException(
517-
f"Reference `#{frg}` not found in file `{doc_url}`.", sl
515+
f"Reference `#{frg}` not found in file `{doc_url}`.",
516+
SourceLine(self.idx[doc_url]),
518517
)
519518
doc = self.fetch(
520519
doc_url, inject_ids=(not mixin), content_types=content_types
@@ -592,11 +591,10 @@ def _resolve_idmap(
592591
)
593592
v.lc.filename = document.lc.filename
594593
else:
595-
sl = SourceLine(document, idmapField, str)
596594
raise ValidationException(
597595
"mapSubject '{}' value '{}' is not a dict "
598596
"and does not have a mapPredicate.".format(k, v),
599-
sl,
597+
SourceLine(document, idmapField),
600598
)
601599
else:
602600
v = val
@@ -852,7 +850,7 @@ def resolve_all(
852850
else:
853851
return (document, metadata)
854852

855-
newctx = None # type: Optional["Loader"]
853+
newctx: Optional["Loader"] = None
856854
if isinstance(document, CommentedMap):
857855
# Handle $base, $profile, $namespaces, $schemas and $graph
858856
if "$base" in document:
@@ -867,12 +865,26 @@ def resolve_all(
867865
if "$namespaces" in document:
868866
if newctx is None:
869867
newctx = SubLoader(self)
870-
newctx.add_namespaces(document["$namespaces"])
868+
namespaces = document["$namespaces"]
869+
if isinstance(namespaces, dict):
870+
newctx.add_namespaces(document["$namespaces"])
871+
else:
872+
raise ValidationException(
873+
"$namespaces must be a dictionary",
874+
SourceLine(document, "$namespaces"),
875+
)
871876

872877
if "$schemas" in document:
873878
if newctx is None:
874879
newctx = SubLoader(self)
875-
newctx.add_schemas(document["$schemas"], file_base)
880+
schemas = document["$schemas"]
881+
if isinstance(schemas, (list, str)):
882+
newctx.add_schemas(schemas, file_base)
883+
else:
884+
raise ValidationException(
885+
"$schemas must be a string or a list of string",
886+
SourceLine(document, "$schemas"),
887+
)
876888

877889
if newctx is not None:
878890
loader = newctx
@@ -1118,14 +1130,13 @@ def validate_links(
11181130
iterator = enumerate(document)
11191131
elif isinstance(document, MutableMapping):
11201132
for d in self.url_fields:
1121-
sl = SourceLine(document, d, str)
11221133
try:
11231134
if d in document and d not in self.identity_links:
11241135
document[d] = self.validate_link(
11251136
d, document[d], docid, all_doc_ids
11261137
)
11271138
except SchemaSaladException as v:
1128-
v = v.with_sourceline(sl)
1139+
v = v.with_sourceline(SourceLine(document, d, str))
11291140
if d == "$schemas" or (
11301141
d in self.foreign_properties and not strict_foreign_properties
11311142
):

schema_salad/tests/test_errors.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
"""Tests of helpful error messages."""
2+
import re
3+
14
import pytest
25

36
import schema_salad
47
import schema_salad.main
58
from schema_salad.avro.schema import Names
69
from schema_salad.exceptions import ValidationException
10+
from schema_salad.ref_resolver import Loader, file_uri
711
from schema_salad.schema import load_and_validate, load_schema
12+
from schema_salad.sourceline import cmap
813

914
from .util import get_data
1015

@@ -317,3 +322,46 @@ def test_bad_schema2() -> None:
317322
path = get_data("tests/bad_schema2.yml")
318323
assert path
319324
assert 1 == schema_salad.main.main(argsl=[path])
325+
326+
327+
def test_namespaces_type() -> None:
328+
"""Confirm helpful error message when $namespaces is the wrong type."""
329+
with pytest.raises(
330+
ValidationException,
331+
match=re.escape("test:1:1: $namespaces must be a dictionary"),
332+
):
333+
ldr, _, _, _ = schema_salad.schema.load_schema(
334+
cmap(
335+
{
336+
"$base": "Y",
337+
"name": "X",
338+
"$namespaces": ["http://example.com/foo#"],
339+
"$graph": [
340+
{
341+
"name": "ExampleType",
342+
"type": "enum",
343+
"symbols": ["asym", "bsym"],
344+
}
345+
],
346+
}
347+
)
348+
)
349+
350+
351+
def test_schemas_type() -> None:
352+
"""Confirm helpful error message when $schemas is the wrong type."""
353+
path = get_data("tests/EDAM.owl")
354+
assert path
355+
with pytest.raises(
356+
ValidationException,
357+
match=re.escape("test:1:1: $schemas must be a string or a list of string"),
358+
):
359+
ra, _ = Loader({}).resolve_all(
360+
cmap(
361+
{
362+
"$schemas": {"wrong": file_uri(path)},
363+
"edam:has_format": "edam:format_1915",
364+
}
365+
),
366+
"",
367+
)

0 commit comments

Comments
 (0)