comparison env/lib/python3.7/site-packages/schema_salad/main.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 """Command line interface to schema-salad."""
2 from __future__ import absolute_import, print_function
3
4 import argparse
5 import logging
6 import os
7 import sys
8 from typing import Any, Dict, List, Mapping, MutableSequence, Optional, Union, cast
9
10 import pkg_resources # part of setuptools
11 import six
12 from rdflib.parser import Parser
13 from rdflib.plugin import register
14 from six.moves import urllib
15 from typing_extensions import Text # pylint: disable=unused-import
16
17 from ruamel.yaml.comments import CommentedSeq
18
19 from . import codegen, jsonld_context, schema
20 from .avro.schema import SchemaParseException
21 from .exceptions import ValidationException, to_one_line_messages
22 from .makedoc import makedoc
23 from .ref_resolver import Loader, file_uri
24 from .utils import json_dumps
25
26 # move to a regular typing import when Python 3.3-3.6 is no longer supported
27
28
29 register("json-ld", Parser, "rdflib_jsonld.parser", "JsonLDParser")
30 _logger = logging.getLogger("salad")
31
32
33 def printrdf(
34 workflow, # type: str
35 wf, # type: Union[List[Dict[Text, Any]], Dict[Text, Any]]
36 ctx, # type: Dict[Text, Any]
37 sr, # type: str
38 ):
39 # type: (...) -> None
40 g = jsonld_context.makerdf(workflow, wf, ctx)
41 print(g.serialize(format=sr, encoding="utf-8").decode("utf-8"))
42
43
44 def main(argsl=None): # type: (Optional[List[str]]) -> int
45 if argsl is None:
46 argsl = sys.argv[1:]
47
48 parser = argparse.ArgumentParser()
49 parser.add_argument(
50 "--rdf-serializer",
51 help="Output RDF serialization format used by --print-rdf"
52 "(one of turtle (default), n3, nt, xml)",
53 default="turtle",
54 )
55
56 parser.add_argument(
57 "--skip-schemas",
58 action="store_true",
59 default=False,
60 help="If specified, ignore $schemas sections.",
61 )
62 parser.add_argument(
63 "--strict-foreign-properties",
64 action="store_true",
65 help="Strict checking of foreign properties",
66 default=False,
67 )
68
69 exgroup = parser.add_mutually_exclusive_group()
70 exgroup.add_argument(
71 "--print-jsonld-context",
72 action="store_true",
73 help="Print JSON-LD context for schema",
74 )
75 exgroup.add_argument("--print-rdfs", action="store_true", help="Print RDF schema")
76 exgroup.add_argument("--print-avro", action="store_true", help="Print Avro schema")
77
78 exgroup.add_argument(
79 "--print-rdf",
80 action="store_true",
81 help="Print corresponding RDF graph for document",
82 )
83 exgroup.add_argument(
84 "--print-pre", action="store_true", help="Print document after preprocessing"
85 )
86 exgroup.add_argument("--print-index", action="store_true", help="Print node index")
87 exgroup.add_argument(
88 "--print-metadata", action="store_true", help="Print document metadata"
89 )
90 exgroup.add_argument(
91 "--print-inheritance-dot",
92 action="store_true",
93 help="Print graphviz file of inheritance",
94 )
95 exgroup.add_argument(
96 "--print-fieldrefs-dot",
97 action="store_true",
98 help="Print graphviz file of field refs",
99 )
100
101 exgroup.add_argument(
102 "--codegen",
103 type=str,
104 metavar="language",
105 help="Generate classes in target language, currently supported: python, java",
106 )
107
108 parser.add_argument(
109 "--codegen-target",
110 type=str,
111 default=None,
112 help="Defaults to sys.stdout for python and ./ for Java",
113 )
114
115 parser.add_argument(
116 "--codegen-examples",
117 type=str,
118 metavar="directory",
119 default=None,
120 help="Directory of example documents for test case generation (Java only).",
121 )
122
123 exgroup.add_argument(
124 "--print-oneline",
125 action="store_true",
126 help="Print each error message in oneline",
127 )
128
129 exgroup.add_argument(
130 "--print-doc", action="store_true", help="Print HTML schema documentation page"
131 )
132
133 exgroup = parser.add_mutually_exclusive_group()
134 exgroup.add_argument(
135 "--strict",
136 action="store_true",
137 help="Strict validation (unrecognized or out of place fields are error)",
138 default=True,
139 dest="strict",
140 )
141 exgroup.add_argument(
142 "--non-strict",
143 action="store_false",
144 help="Lenient validation (ignore unrecognized fields)",
145 default=True,
146 dest="strict",
147 )
148
149 exgroup = parser.add_mutually_exclusive_group()
150 exgroup.add_argument("--verbose", action="store_true", help="Default logging")
151 exgroup.add_argument(
152 "--quiet", action="store_true", help="Only print warnings and errors."
153 )
154 exgroup.add_argument("--debug", action="store_true", help="Print even more logging")
155
156 parser.add_argument(
157 "--only",
158 action="append",
159 help="Use with --print-doc, document only listed types",
160 )
161 parser.add_argument(
162 "--redirect",
163 action="append",
164 help="Use with --print-doc, override default link for type",
165 )
166 parser.add_argument(
167 "--brand", help="Use with --print-doc, set the 'brand' text in nav bar"
168 )
169 parser.add_argument(
170 "--brandlink", help="Use with --print-doc, set the link for 'brand' in nav bar"
171 )
172 parser.add_argument(
173 "--primtype",
174 default="#PrimitiveType",
175 help="Use with --print-doc, link to use for primitive types (string, int etc)",
176 )
177
178 parser.add_argument("schema", type=str, nargs="?", default=None)
179 parser.add_argument("document", type=str, nargs="?", default=None)
180 parser.add_argument(
181 "--version", "-v", action="store_true", help="Print version", default=None
182 )
183
184 args = parser.parse_args(argsl)
185
186 if args.version is None and args.schema is None:
187 print("{}: error: too few arguments".format(sys.argv[0]))
188 return 1
189
190 if args.quiet:
191 _logger.setLevel(logging.WARN)
192 if args.debug:
193 _logger.setLevel(logging.DEBUG)
194
195 pkg = pkg_resources.require("schema_salad")
196 if pkg:
197 if args.version:
198 print("{} Current version: {}".format(sys.argv[0], pkg[0].version))
199 return 0
200 else:
201 _logger.info("%s Current version: %s", sys.argv[0], pkg[0].version)
202
203 # Get the metaschema to validate the schema
204 metaschema_names, metaschema_doc, metaschema_loader = schema.get_metaschema()
205
206 # Load schema document and resolve refs
207
208 schema_uri = args.schema
209 if not (
210 urllib.parse.urlparse(schema_uri)[0]
211 and urllib.parse.urlparse(schema_uri)[0] in [u"http", u"https", u"file"]
212 ):
213 schema_uri = file_uri(os.path.abspath(schema_uri))
214 schema_raw_doc = metaschema_loader.fetch(schema_uri)
215
216 try:
217 schema_doc, schema_metadata = metaschema_loader.resolve_all(
218 schema_raw_doc, schema_uri
219 )
220 except ValidationException as e:
221 _logger.error(
222 "Schema `%s` failed link checking:\n%s",
223 args.schema,
224 Text(e),
225 exc_info=(True if args.debug else False),
226 )
227 _logger.debug("Index is %s", list(metaschema_loader.idx.keys()))
228 _logger.debug("Vocabulary is %s", list(metaschema_loader.vocab.keys()))
229 return 1
230 except (RuntimeError) as e:
231 _logger.error(
232 "Schema `%s` read error:\n%s",
233 args.schema,
234 Text(e),
235 exc_info=(True if args.debug else False),
236 )
237 return 1
238
239 if args.print_doc:
240 makedoc(args)
241 return 0
242
243 # Optionally print the schema after ref resolution
244 if not args.document and args.print_pre:
245 print(json_dumps(schema_doc, indent=4))
246 return 0
247
248 if not args.document and args.print_index:
249 print(json_dumps(list(metaschema_loader.idx.keys()), indent=4))
250 return 0
251
252 # Validate the schema document against the metaschema
253 try:
254 schema.validate_doc(
255 metaschema_names, schema_doc, metaschema_loader, args.strict
256 )
257 except ValidationException as e:
258 _logger.error("While validating schema `%s`:\n%s", args.schema, Text(e))
259 return 1
260
261 # Get the json-ld context and RDFS representation from the schema
262 metactx = schema.collect_namespaces(schema_metadata)
263 if "$base" in schema_metadata:
264 metactx["@base"] = schema_metadata["$base"]
265 if isinstance(schema_doc, CommentedSeq):
266 (schema_ctx, rdfs) = jsonld_context.salad_to_jsonld_context(schema_doc, metactx)
267 else:
268 raise ValidationException(
269 "Expected a CommentedSeq, got {}: {}.".format(type(schema_doc), schema_doc)
270 )
271
272 # Create the loader that will be used to load the target document.
273 document_loader = Loader(schema_ctx, skip_schemas=args.skip_schemas)
274
275 if args.codegen:
276 codegen.codegen(
277 args.codegen,
278 cast(List[Dict[Text, Any]], schema_doc),
279 schema_metadata,
280 document_loader,
281 target=args.codegen_target,
282 examples=args.codegen_examples,
283 )
284 return 0
285
286 # Make the Avro validation that will be used to validate the target
287 # document
288 if isinstance(schema_doc, MutableSequence):
289 avsc_obj = schema.make_avro(schema_doc, document_loader)
290 try:
291 avsc_names = schema.make_avro_schema_from_avro(avsc_obj)
292 except SchemaParseException as err:
293 _logger.error(
294 "Schema `%s` error:\n%s",
295 args.schema,
296 Text(err),
297 exc_info=((type(err), err, None) if args.debug else None),
298 )
299 if args.print_avro:
300 print(json_dumps(avsc_obj, indent=4))
301 return 1
302 else:
303 _logger.error("Schema `%s` must be a list.", args.schema)
304 return 1
305
306 # Optionally print Avro-compatible schema from schema
307 if args.print_avro:
308 print(json_dumps(avsc_obj, indent=4))
309 return 0
310
311 # Optionally print the json-ld context from the schema
312 if args.print_jsonld_context:
313 j = {"@context": schema_ctx}
314 print(json_dumps(j, indent=4, sort_keys=True))
315 return 0
316
317 # Optionally print the RDFS graph from the schema
318 if args.print_rdfs:
319 print(rdfs.serialize(format=args.rdf_serializer).decode("utf-8"))
320 return 0
321
322 if args.print_metadata and not args.document:
323 print(json_dumps(schema_metadata, indent=4))
324 return 0
325
326 if args.print_inheritance_dot:
327 schema.print_inheritance(schema_doc, sys.stdout)
328 return 0
329
330 if args.print_fieldrefs_dot:
331 schema.print_fieldrefs(schema_doc, document_loader, sys.stdout)
332 return 0
333
334 # If no document specified, all done.
335 if not args.document:
336 print("Schema `{}` is valid".format(args.schema))
337 return 0
338
339 # Load target document and resolve refs
340 try:
341 uri = args.document
342 document, doc_metadata = document_loader.resolve_ref(
343 uri, strict_foreign_properties=args.strict_foreign_properties
344 )
345 except ValidationException as e:
346 msg = to_one_line_messages(e) if args.print_oneline else six.text_type(e)
347 _logger.error(
348 "Document `%s` failed validation:\n%s",
349 args.document,
350 msg,
351 exc_info=args.debug,
352 )
353 return 1
354
355 # Optionally print the document after ref resolution
356 if args.print_pre:
357 print(json_dumps(document, indent=4))
358 return 0
359
360 if args.print_index:
361 print(json_dumps(list(document_loader.idx.keys()), indent=4))
362 return 0
363
364 # Validate the user document against the schema
365 try:
366 schema.validate_doc(
367 avsc_names,
368 document,
369 document_loader,
370 args.strict,
371 strict_foreign_properties=args.strict_foreign_properties,
372 )
373 except ValidationException as e:
374 msg = to_one_line_messages(e) if args.print_oneline else six.text_type(e)
375 _logger.error("While validating document `%s`:\n%s" % (args.document, msg))
376 return 1
377
378 # Optionally convert the document to RDF
379 if args.print_rdf:
380 if isinstance(document, (Mapping, MutableSequence)):
381 printrdf(args.document, document, schema_ctx, args.rdf_serializer)
382 return 0
383 else:
384 print("Document must be a dictionary or list.")
385 return 1
386
387 if args.print_metadata:
388 print(json_dumps(doc_metadata, indent=4))
389 return 0
390
391 print("Document `{}` is valid".format(args.document))
392
393 return 0
394
395
396 if __name__ == "__main__":
397 sys.exit(main(sys.argv[1:]))