Skip to content

Commit 8ac4540

Browse files
authored
Provide line numbers in error messages (#62)
* Line number reporting WIP * Handle line number tracking $import expansion of lists and idmaps. * Break long lines. * Formatting bullets, indentation. * Add tests of validation error reporting. * type cleanups * Better error reporting on link checking. * Add missing sourceline.py * Fix tests, typing, add tests for validation errors. * Bump version. * Print error validation messages for manual audit.
1 parent 8454546 commit 8ac4540

34 files changed

+3199
-312
lines changed

schema_salad/add_dictlist.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22
from typing import Any, Dict
33

4+
45
def add_dictlist(di, key, val): # type: (Dict, Any, Any) -> None
56
if key not in di:
67
di[key] = []

schema_salad/aslist.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22
from typing import Any, List
33

4+
45
def aslist(l): # type: (Any) -> List
56
"""Convenience function to wrap single items and lists, and return lists unchanged."""
67

schema_salad/flatten.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from typing import Any, Tuple
33

44
# http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html
5+
6+
57
def flatten(l, ltypes=(list, tuple)):
68
# type: (Any, Any) -> Any
79
if l is None:

schema_salad/jsonld_context.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@
2020
import logging
2121
from .aslist import aslist
2222
from typing import Any, cast, Dict, Iterable, Tuple, Union
23-
from .ref_resolver import Loader
23+
from .ref_resolver import Loader, ContextType
2424

2525
_logger = logging.getLogger("salad")
2626

2727

28-
def pred(datatype, field, name, context, defaultBase, namespaces):
29-
# type: (Dict[str, Union[Dict, str]], Dict, str, Loader.ContextType, str, Dict[str, rdflib.namespace.Namespace]) -> Union[Dict, str]
28+
def pred(datatype, # type: Dict[str, Union[Dict, str]]
29+
field, # type: Dict
30+
name, # type: str
31+
context, # type: ContextType
32+
defaultBase, # type: str
33+
namespaces # type: Dict[str, rdflib.namespace.Namespace]
34+
):
35+
# type: (...) -> Union[Dict, str]
3036
split = urlparse.urlsplit(name)
3137

3238
vee = None # type: Union[str, unicode]
@@ -84,8 +90,14 @@ def pred(datatype, field, name, context, defaultBase, namespaces):
8490
return ret
8591

8692

87-
def process_type(t, g, context, defaultBase, namespaces, defaultPrefix):
88-
# type: (Dict[str, Any], Graph, Loader.ContextType, str, Dict[str, rdflib.namespace.Namespace], str) -> None
93+
def process_type(t, # type: Dict[str, Any]
94+
g, # type: Graph
95+
context, # type: ContextType
96+
defaultBase, # type: str
97+
namespaces, # type: Dict[str, rdflib.namespace.Namespace]
98+
defaultPrefix # type: str
99+
):
100+
# type: (...) -> None
89101
if t["type"] == "record":
90102
recordname = t["name"]
91103

@@ -154,8 +166,8 @@ def process_type(t, g, context, defaultBase, namespaces, defaultPrefix):
154166

155167

156168
def salad_to_jsonld_context(j, schema_ctx):
157-
# type: (Iterable, Dict[str, Any]) -> Tuple[Loader.ContextType, Graph]
158-
context = {} # type: Loader.ContextType
169+
# type: (Iterable, Dict[str, Any]) -> Tuple[ContextType, Graph]
170+
context = {} # type: ContextType
159171
namespaces = {}
160172
g = Graph()
161173
defaultPrefix = ""
@@ -178,8 +190,11 @@ def salad_to_jsonld_context(j, schema_ctx):
178190

179191
return (context, g)
180192

181-
def fix_jsonld_ids(obj, ids):
182-
# type: (Union[Dict[unicode, Any], List[Dict[unicode, Any]]], List[unicode]) -> None
193+
194+
def fix_jsonld_ids(obj, # type: Union[Dict[unicode, Any], List[Dict[unicode, Any]]]
195+
ids # type: List[unicode]
196+
):
197+
# type: (...) -> None
183198
if isinstance(obj, dict):
184199
for i in ids:
185200
if i in obj:
@@ -190,8 +205,13 @@ def fix_jsonld_ids(obj, ids):
190205
for entry in obj:
191206
fix_jsonld_ids(entry, ids)
192207

193-
def makerdf(workflow, wf, ctx, graph=None):
194-
# type: (Union[str, unicode], Union[List[Dict[unicode, Any]], Dict[unicode, Any]], Loader.ContextType, Graph) -> Graph
208+
209+
def makerdf(workflow, # type: Union[str, unicode]
210+
wf, # type: Union[List[Dict[unicode, Any]], Dict[unicode, Any]]
211+
ctx, # type: ContextType
212+
graph=None # type: Graph
213+
):
214+
# type: (...) -> Graph
195215
prefixes = {}
196216
idfields = []
197217
for k, v in ctx.iteritems():

schema_salad/main.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,36 @@
33
import logging
44
import sys
55
import traceback
6-
import pkg_resources # part of setuptools
7-
from . import schema
8-
from . import jsonld_context
9-
from . import makedoc
106
import json
11-
from rdflib import Graph, plugin
12-
from rdflib.serializer import Serializer
137
import os
148
import urlparse
159

16-
from .ref_resolver import Loader
17-
from . import validate
10+
import pkg_resources # part of setuptools
11+
1812
from typing import Any, Dict, List, Union
1913

14+
from rdflib import Graph, plugin
15+
from rdflib.serializer import Serializer
16+
17+
from . import schema
18+
from . import jsonld_context
19+
from . import makedoc
20+
from . import validate
21+
from .sourceline import strip_dup_lineno
22+
from .ref_resolver import Loader
23+
2024
_logger = logging.getLogger("salad")
2125

2226
from rdflib.plugin import register, Parser
2327
register('json-ld', Parser, 'rdflib_jsonld.parser', 'JsonLDParser')
2428

2529

26-
def printrdf(workflow, wf, ctx, sr):
27-
# type: (str, Union[List[Dict[unicode, Any]], Dict[unicode, Any]], Dict[unicode, Any], str) -> None
30+
def printrdf(workflow, # type: str
31+
wf, # type: Union[List[Dict[unicode, Any]], Dict[unicode, Any]]
32+
ctx, # type: Dict[unicode, Any]
33+
sr # type: str
34+
):
35+
# type: (...) -> None
2836
g = jsonld_context.makerdf(workflow, wf, ctx)
2937
print(g.serialize(format=sr))
3038

@@ -104,10 +112,14 @@ def main(argsl=None): # type: (List[str]) -> int
104112
schema_raw_doc, schema_uri)
105113
except (validate.ValidationException) as e:
106114
_logger.error("Schema `%s` failed link checking:\n%s",
107-
args.schema, e, exc_info=(True if args.debug else False))
115+
args.schema, e, exc_info=(True if args.debug else False))
108116
_logger.debug("Index is %s", metaschema_loader.idx.keys())
109117
_logger.debug("Vocabulary is %s", metaschema_loader.vocab.keys())
110118
return 1
119+
except (RuntimeError) as e:
120+
_logger.error("Schema `%s` read error:\n%s",
121+
args.schema, e, exc_info=(True if args.debug else False))
122+
return 1
111123

112124
# Optionally print the schema after ref resolution
113125
if not args.document and args.print_pre:
@@ -121,7 +133,8 @@ def main(argsl=None): # type: (List[str]) -> int
121133
# Validate the schema document against the metaschema
122134
try:
123135
schema.validate_doc(metaschema_names, schema_doc,
124-
metaschema_loader, args.strict)
136+
metaschema_loader, args.strict,
137+
source=schema_metadata["name"])
125138
except validate.ValidationException as e:
126139
_logger.error("While validating schema `%s`:\n%s" %
127140
(args.schema, str(e)))
@@ -149,8 +162,8 @@ def main(argsl=None): # type: (List[str]) -> int
149162

150163
if isinstance(avsc_names, Exception):
151164
_logger.error("Schema `%s` error:\n%s", args.schema,
152-
avsc_names, exc_info=((type(avsc_names), avsc_names,
153-
None) if args.debug else None))
165+
avsc_names, exc_info=((type(avsc_names), avsc_names,
166+
None) if args.debug else None))
154167
if args.print_avro:
155168
print(json.dumps(avsc_obj, indent=4))
156169
return 1
@@ -188,7 +201,7 @@ def main(argsl=None): # type: (List[str]) -> int
188201
document, doc_metadata = document_loader.resolve_ref(uri)
189202
except (validate.ValidationException, RuntimeError) as e:
190203
_logger.error("Document `%s` failed validation:\n%s",
191-
args.document, e, exc_info=args.debug)
204+
args.document, strip_dup_lineno(unicode(e)), exc_info=args.debug)
192205
return 1
193206

194207
# Optionally print the document after ref resolution

schema_salad/makedoc.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def contents(self, idn): # type: (str) -> str
110110
c += """</nav>"""
111111
return c
112112

113+
113114
basicTypes = ("https://w3id.org/cwl/salad#null",
114115
"http://www.w3.org/2001/XMLSchema#boolean",
115116
"http://www.w3.org/2001/XMLSchema#int",
@@ -219,8 +220,13 @@ def __init__(self, toc, j, renderlist, redirects):
219220
("docAfter" not in f))):
220221
self.render_type(f, 1)
221222

222-
def typefmt(self, tp, redirects, nbsp=False, jsonldPredicate=None):
223-
# type: (Any, Dict[str, str], bool, Dict[str, str]) -> Union[str, unicode]
223+
def typefmt(self,
224+
tp, # type: Any
225+
redirects, # type: Dict[str, str]
226+
nbsp=False, # type: bool
227+
jsonldPredicate=None # type: Dict[str, str]
228+
):
229+
# type: (...) -> Union[str, unicode]
224230
global primitiveType
225231
if isinstance(tp, list):
226232
if nbsp and len(tp) <= 3:
@@ -229,16 +235,20 @@ def typefmt(self, tp, redirects, nbsp=False, jsonldPredicate=None):
229235
return " | ".join([self.typefmt(n, redirects) for n in tp])
230236
if isinstance(tp, dict):
231237
if tp["type"] == "https://w3id.org/cwl/salad#array":
232-
ar = "array&lt;%s&gt;" % (self.typefmt(tp["items"], redirects, nbsp=True))
238+
ar = "array&lt;%s&gt;" % (self.typefmt(
239+
tp["items"], redirects, nbsp=True))
233240
if jsonldPredicate and "mapSubject" in jsonldPredicate:
234241
if "mapPredicate" in jsonldPredicate:
235242
ar += " | map&lt;%s.%s,&nbsp;%s.%s&gt" % (self.typefmt(tp["items"], redirects),
236-
jsonldPredicate["mapSubject"],
237-
self.typefmt(tp["items"], redirects),
238-
jsonldPredicate["mapPredicate"])
243+
jsonldPredicate[
244+
"mapSubject"],
245+
self.typefmt(
246+
tp["items"], redirects),
247+
jsonldPredicate["mapPredicate"])
239248
ar += " | map&lt;%s.%s,&nbsp;%s&gt" % (self.typefmt(tp["items"], redirects),
240-
jsonldPredicate["mapSubject"],
241-
self.typefmt(tp["items"], redirects))
249+
jsonldPredicate[
250+
"mapSubject"],
251+
self.typefmt(tp["items"], redirects))
242252
return ar
243253
if tp["type"] in ("https://w3id.org/cwl/salad#record", "https://w3id.org/cwl/salad#enum"):
244254
frg = schema.avro_name(tp["name"])
@@ -481,6 +491,7 @@ def avrold_doc(j, outdoc, renderlist, redirects, brand, brandlink):
481491
</body>
482492
</html>""")
483493

494+
484495
if __name__ == "__main__":
485496

486497
parser = argparse.ArgumentParser()

0 commit comments

Comments
 (0)