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