check_xsd.py 5.02 KB
Newer Older
canterburym's avatar
canterburym committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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)