import logging import glob import sys import argparse from pathlib import Path from pprint import pprint import os from lxml import etree from xml.etree.ElementTree import ParseError from xmlschema import XMLSchema, XMLSchemaParseError def BuildSchemaDictonary (fileList): if len(fileList) == 0: logging.info("No schema files provided") return [] logging.info("Schema locations:") schemaLocations = [] for schemaFile in fileList: try: xs = XMLSchema(schemaFile, validation='skip') schemaLocations.append((xs.default_namespace, str(Path(schemaFile).resolve()))) logging.info(" [ {0} -> {1} ]".format(xs.default_namespace, schemaFile)) except ParseError as ex: logging.warning (" [ {0} failed to parse: {1} ]".format(schemaFile, ex)) return schemaLocations def BuildSchema (coreFile, fileList = None): schemaLocations = [] if fileList and len(fileList) > 0: schemaLocations = BuildSchemaDictonary(fileList) coreSchema = XMLSchema(str(Path(coreFile)), locations=schemaLocations) return coreSchema def ValidateSingleFile (schemaFile): try: xs = XMLSchema(schemaFile, validation='skip') except ParseError as ex: logging.warning (" [ {0} failed to parse: {1} ]".format(schemaFile, ex)) return ex return None def ValidateXSDFiles (fileList): if len(fileList) == 0: logging.info("No schema files provided") return {} schemaLocations = BuildSchemaDictonary(fileList) errors = {} logging.info("Schema validation:") for schemaFile in fileList: try: schema = XMLSchema(schemaFile, locations = schemaLocations) logging.info(schemaFile + ": OK") errors[schemaFile] = [] except XMLSchemaParseError as ex: if (ex.schema_url) and (ex.schema_url != ex.origin_url): logging.info(" Error {1} comes from {0}, suppressing".format(ex.schema_url, ex.message)) errors[schemaFile] = [] else: logging.warning(schemaFile + ": Failed validation ({0})".format(ex)) errors[schemaFile] = [ex.message] return errors def ValidateInstanceDocuments (coreFile, supportingSchemas, instanceDocs): if (instanceDocs is None) or len(instanceDocs) == 0: logging.warning ("No instance documents provided") return [] schema = BuildSchema(coreFile, supportingSchemas) errors = [] for instanceDoc in instanceDocs: try: schema.validate(instanceDoc) logging.info ("{0} passed validation".format(instanceDoc)) except Exception as ex: logging.error ("{0} failed validation: {1}".format(instanceDoc, ex)) return errors if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbosity", help="verbosity level", action="count", default=0) parser.add_argument("input", help="include a directory or file", action="append", nargs="+") args = parser.parse_args() logging.getLogger().setLevel(logging.WARNING) if (args.verbosity >= 1): logging.getLogger().setLevel(logging.INFO) if (args.verbosity >= 2): logging.getLogger().setLevel(logging.DEBUG) logging.debug("Very verbose selected") logging.debug(f"Path: {args.input}") includeFileList = [] for path in args.input[0]: p = Path(path) if not p.exists(): logging.error(f"Include path {path} not found") exit(1) if p.is_dir(): logging.debug(f"Expanding directory") for g in glob.glob(os.path.join(str(p), "*.xsd")): logging.info(f">Including {g}") includeFileList.append(g) else: logging.info(f"Including {p.absolute()}") includeFileList.append(p.absolute()) syntaxErrors = 0 print ("XSD syntax checks:") print ("-----------------------------") for file in includeFileList: error = ValidateSingleFile(file) if (error): print (f" {file} : Syntax error [{error}]") syntaxErrors += 1 else: print (f" {file} : OK") print ("-----------------------------") if (syntaxErrors > 0): print (f"{syntaxErrors} syntax errors detected") exit(syntaxErrors) results = ValidateXSDFiles(includeFileList) print ("XSD build checks:") print ("-----------------------------") errorCount = 0 for fileName, errors in results.items(): if len(errors) > 0: errorCount += len(errors) print (f" {fileName}: {len(errors)} errors") for error in errors: if isinstance(error, XMLSchemaParseError): print (error.msg) else: print (f" {str(error.strip())}") else: print (f" {fileName}: OK") print ("-----------------------------") print (f"{errorCount} build errors detected") exit(errorCount)