El XSD ya es probado con XML de ejemplo
This commit is contained in:
parent
d7ddb5f088
commit
5ef0994a93
183
yasd.py
183
yasd.py
|
@ -25,6 +25,7 @@ class YASD:
|
||||||
action="check",
|
action="check",
|
||||||
indata=None,
|
indata=None,
|
||||||
outfile=None,
|
outfile=None,
|
||||||
|
tests=None,
|
||||||
quiet=False,
|
quiet=False,
|
||||||
log=False,
|
log=False,
|
||||||
stdout=False,
|
stdout=False,
|
||||||
|
@ -40,6 +41,8 @@ class YASD:
|
||||||
:type indata: None or Path or dict
|
:type indata: None or Path or dict
|
||||||
:param outfile: YASD output file path; 'None' by default
|
:param outfile: YASD output file path; 'None' by default
|
||||||
:type outfile: None or Path
|
:type outfile: None or Path
|
||||||
|
:param tests: XML tests; 'None' by default
|
||||||
|
:type tests: None or list<Path>
|
||||||
:param quiet: If messages are print or not; 'False' by default
|
:param quiet: If messages are print or not; 'False' by default
|
||||||
:type quiet: True or False
|
:type quiet: True or False
|
||||||
:param log: If messages are write in a file or not; 'False' by default
|
:param log: If messages are write in a file or not; 'False' by default
|
||||||
|
@ -52,7 +55,7 @@ class YASD:
|
||||||
or 'sample'; YAML dict on 'check'
|
or 'sample'; YAML dict on 'check'
|
||||||
:rtype: str or bs4.element.Tag or dict
|
:rtype: str or bs4.element.Tag or dict
|
||||||
"""
|
"""
|
||||||
yasd = YASD(indata, outfile, quiet, log, stdout, validate)
|
yasd = YASD(indata, outfile, tests, quiet, log, stdout, validate)
|
||||||
yasd.msgr.run(f"action_{action}")
|
yasd.msgr.run(f"action_{action}")
|
||||||
match action:
|
match action:
|
||||||
case "document":
|
case "document":
|
||||||
|
@ -68,6 +71,7 @@ class YASD:
|
||||||
self,
|
self,
|
||||||
indata=None,
|
indata=None,
|
||||||
outfile=None,
|
outfile=None,
|
||||||
|
tests=None,
|
||||||
quiet=False,
|
quiet=False,
|
||||||
log=False,
|
log=False,
|
||||||
stdout=False,
|
stdout=False,
|
||||||
|
@ -80,6 +84,8 @@ class YASD:
|
||||||
:type indata: None or Path or dict
|
:type indata: None or Path or dict
|
||||||
:param outfile: YASD output file path; 'None' by default
|
:param outfile: YASD output file path; 'None' by default
|
||||||
:type outfile: None or Path
|
:type outfile: None or Path
|
||||||
|
:param tests: XML tests; 'None' by default
|
||||||
|
:type tests: None or list<Path>
|
||||||
:param quiet: If messages are print or not; 'False' by default
|
:param quiet: If messages are print or not; 'False' by default
|
||||||
:type quiet: True or False
|
:type quiet: True or False
|
||||||
:param log: If messages are write in a file or not; 'False' by default
|
:param log: If messages are write in a file or not; 'False' by default
|
||||||
|
@ -94,6 +100,7 @@ class YASD:
|
||||||
self.formatter = XMLFormatter(indent=2)
|
self.formatter = XMLFormatter(indent=2)
|
||||||
self.stdout = stdout
|
self.stdout = stdout
|
||||||
self.validate = validate
|
self.validate = validate
|
||||||
|
self.tests = set(tests) if tests is not None else None
|
||||||
if outfile is None:
|
if outfile is None:
|
||||||
self.outfile = None
|
self.outfile = None
|
||||||
else:
|
else:
|
||||||
|
@ -107,11 +114,11 @@ class YASD:
|
||||||
:rtype: bs4.element.Tag
|
:rtype: bs4.element.Tag
|
||||||
"""
|
"""
|
||||||
self.xsd = YASDXSD(self.yaml, self.msgr).xsd
|
self.xsd = YASDXSD(self.yaml, self.msgr).xsd
|
||||||
out = self.__output(self.xsd)
|
out = self.__output(self.xsd, self.msgr)
|
||||||
if self.validate:
|
if self.validate:
|
||||||
return YASDCheck.xsd(out, self.msgr)
|
return YASDCheck.xsd(out, self.tests, self.msgr)
|
||||||
else:
|
else:
|
||||||
return out
|
return {"xsd": out, "tests": []}
|
||||||
|
|
||||||
def sample(self):
|
def sample(self):
|
||||||
"""
|
"""
|
||||||
|
@ -121,7 +128,7 @@ class YASD:
|
||||||
:rtype: bs4.element.Tag
|
:rtype: bs4.element.Tag
|
||||||
"""
|
"""
|
||||||
self.xml = YASDXML(self.yaml, self.msgr).xml
|
self.xml = YASDXML(self.yaml, self.msgr).xml
|
||||||
return self.__output(self.xml, extname=".xml")
|
return self.__output(self.xml, self.msgr, extname=".xml")
|
||||||
|
|
||||||
def document(self):
|
def document(self):
|
||||||
"""
|
"""
|
||||||
|
@ -131,21 +138,26 @@ class YASD:
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
self.rst = YASDRST(self.yaml, self.msgr).rst
|
self.rst = YASDRST(self.yaml, self.msgr).rst
|
||||||
return self.__output(self.rst, extname=".rst")
|
return self.__output(self.rst, self.msgr, extname=".rst")
|
||||||
|
|
||||||
def __output(self, outdata, extname=".xsd"):
|
def __output(self, outdata="", msgr=None, extname=".xsd"):
|
||||||
"""
|
"""
|
||||||
Prints in the terminal or writes into a file.
|
Prints in the terminal or writes into a file.
|
||||||
|
|
||||||
|
:param outdata: Data for output
|
||||||
|
:type outdata: bs4.BeautifulSoup or str
|
||||||
|
:param messenger: Messenger object
|
||||||
|
:type messenger: None or YASDMessenger
|
||||||
|
:param str extname: Extension name for file output
|
||||||
:return: Output data
|
:return: Output data
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
if type(outdata) is not str:
|
if not isinstance(outdata, str):
|
||||||
outdata = outdata.prettify(formatter=self.formatter)
|
outdata = outdata.prettify(formatter=self.formatter)
|
||||||
outdata = f'<?xml version="1.0"?>\n{outdata}'
|
outdata = f'<?xml version="1.0"?>\n{outdata}'
|
||||||
if self.stdout:
|
if self.stdout:
|
||||||
if self.outfile is None:
|
if self.outfile is None:
|
||||||
print(outdata)
|
msgr.run(outdata)
|
||||||
else:
|
else:
|
||||||
suffix = self.outfile.suffix
|
suffix = self.outfile.suffix
|
||||||
if len(suffix) > 0 and suffix == suffix.replace(" ", ""):
|
if len(suffix) > 0 and suffix == suffix.replace(" ", ""):
|
||||||
|
@ -237,6 +249,8 @@ class YASDXSD:
|
||||||
del el["fixed"]
|
del el["fixed"]
|
||||||
if "restriction" in el.keys() and "datatype" in el.keys():
|
if "restriction" in el.keys() and "datatype" in el.keys():
|
||||||
del el["datatype"]
|
del el["datatype"]
|
||||||
|
if "type" in el.keys() and tag == "element":
|
||||||
|
del el["type"]
|
||||||
for key, val in el.items():
|
for key, val in el.items():
|
||||||
if key == "datatype":
|
if key == "datatype":
|
||||||
element["type"] = f"xs:{val}"
|
element["type"] = f"xs:{val}"
|
||||||
|
@ -518,22 +532,134 @@ class YASDCheck:
|
||||||
except Exception:
|
except Exception:
|
||||||
msgr.run("no_url", level="error", url=url)
|
msgr.run("no_url", level="error", url=url)
|
||||||
|
|
||||||
def xsd(xsd, msgr=None):
|
def xsd(xsd, tests=None, msgr=None):
|
||||||
"""
|
"""
|
||||||
Validates XSD.
|
Validates XSD.
|
||||||
|
|
||||||
:param str xsd: XSD as XML string
|
:param str xsd: XSD as XML string
|
||||||
|
:param tests: XML file paths
|
||||||
|
:type tests: None or set
|
||||||
:param msgr: Messenger object
|
:param msgr: Messenger object
|
||||||
:type msgr: None or YASDMessenger
|
:type msgr: None or YASDMessenger
|
||||||
|
:return: XSD as string and results as list
|
||||||
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
msgr = YASDCheck.messenger(msgr)
|
msgr = YASDCheck.messenger(msgr)
|
||||||
msgr.run("validating")
|
msgr.run("validating")
|
||||||
try:
|
try:
|
||||||
xmlschema.XMLSchema(xsd)
|
valid_xsd = xmlschema.XMLSchema(xsd)
|
||||||
|
tests = YASDCheck.xsd_test(valid_xsd, tests, msgr)
|
||||||
|
return {"xsd": xsd, "tests": tests}
|
||||||
except xmlschema.validators.exceptions.XMLSchemaParseError as error:
|
except xmlschema.validators.exceptions.XMLSchemaParseError as error:
|
||||||
error = str(error).replace("\n", "\n ")
|
error = str(error).replace("\n", "\n ")
|
||||||
msgr.run("no_valid", error=error, level="error")
|
msgr.run("no_valid", error=error, level="error")
|
||||||
|
|
||||||
|
def xsd_test(xsd, tests=None, msgr=None):
|
||||||
|
"""
|
||||||
|
Test XSD against XML files.
|
||||||
|
|
||||||
|
:param xsd: XSD for testing
|
||||||
|
:type xsd: XMLSchema10 or str
|
||||||
|
:param tests: XML file paths
|
||||||
|
:type tests: None or set
|
||||||
|
:param msgr: Messenger object
|
||||||
|
:type msgr: None or YASDMessenger
|
||||||
|
:return: Tests results
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
results = []
|
||||||
|
if isinstance(xsd, str):
|
||||||
|
xsd = xmlschema.XMLSchema(xsd)
|
||||||
|
if isinstance(tests, set):
|
||||||
|
for test in tests:
|
||||||
|
result = YASDCheck.__valid_test_path(test)
|
||||||
|
if "error" not in result.keys():
|
||||||
|
result = YASDCheck.__valid_test_xml(xsd, result)
|
||||||
|
results.append(result)
|
||||||
|
YASDCheck.__print_tests(results, msgr)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def __valid_test_path(filepath=""):
|
||||||
|
"""
|
||||||
|
Validates test path.
|
||||||
|
|
||||||
|
:param filepath: Test file path
|
||||||
|
:type filepath: Path or str
|
||||||
|
"""
|
||||||
|
result = {"path": filepath}
|
||||||
|
if isinstance(filepath, str):
|
||||||
|
filepath = Path(filepath)
|
||||||
|
if not filepath.exists() or not filepath.is_file():
|
||||||
|
if not filepath.exists():
|
||||||
|
error = YASDMessenger.keys()["no_exists"]
|
||||||
|
else:
|
||||||
|
error = YASDMessenger.keys()["no_file"]
|
||||||
|
result.setdefault("msg", error)
|
||||||
|
result.setdefault("error", True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __valid_test_xml(xsd, result):
|
||||||
|
"""
|
||||||
|
Test XSD against XML file.
|
||||||
|
|
||||||
|
The result is a temporary dict with only 'path' as key.
|
||||||
|
|
||||||
|
:param XMLSchema10 xsd: XSD for testing
|
||||||
|
:param dict result: Temporary test result
|
||||||
|
:return: Test result
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
xsd.validate(result["path"])
|
||||||
|
result.setdefault("msg", YASDMessenger.keys()["passed"])
|
||||||
|
result.setdefault("error", False)
|
||||||
|
except Exception as error:
|
||||||
|
result.setdefault("msg", error.message)
|
||||||
|
result.setdefault("error", True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __print_tests(results=[], msgr=None):
|
||||||
|
"""
|
||||||
|
Prints XSD test results.
|
||||||
|
|
||||||
|
:param list results: Tests results
|
||||||
|
:param msgr: Messenger object
|
||||||
|
:type msgr: None or YASDMessenger
|
||||||
|
"""
|
||||||
|
for result in results:
|
||||||
|
level = "warn" if result["error"] else "info"
|
||||||
|
res = "FAILED" if result["error"] else "PASSED"
|
||||||
|
msg = (" " * 10) + result["msg"]
|
||||||
|
expectation = YASDCheck.__get_expectation(result["path"])
|
||||||
|
msgr.run("testing", path=result["path"])
|
||||||
|
msgr.run(
|
||||||
|
"test_result",
|
||||||
|
level=level,
|
||||||
|
path=result["path"],
|
||||||
|
expectation=expectation,
|
||||||
|
res=res,
|
||||||
|
msg=msg,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __get_expectation(filepath=""):
|
||||||
|
"""
|
||||||
|
Gets XML file expected result.
|
||||||
|
|
||||||
|
:param filepath: Test file path
|
||||||
|
:type filepath: Path or str
|
||||||
|
:return: 'PASSED' or 'FAILED' or '???'
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if isinstance(filepath, str):
|
||||||
|
filepath = Path(filepath)
|
||||||
|
name = filepath.stem
|
||||||
|
if name.find("pass") >= 0:
|
||||||
|
return "PASSED"
|
||||||
|
elif name.find("fail") >= 0:
|
||||||
|
return "FAILED"
|
||||||
|
else:
|
||||||
|
return "???"
|
||||||
|
|
||||||
def __init__(self, indata=None, messenger=None):
|
def __init__(self, indata=None, messenger=None):
|
||||||
"""
|
"""
|
||||||
Inits YASD validator.
|
Inits YASD validator.
|
||||||
|
@ -597,11 +723,22 @@ class YASDMessenger:
|
||||||
Academy of Language <https://academia.org.mx>. Licensed under GPLv3
|
Academy of Language <https://academia.org.mx>. Licensed under GPLv3
|
||||||
<https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
<https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||||
""",
|
""",
|
||||||
"readme": "https://gitlab.com/perrotuerto_personal/codigo/yasd/-/raw/no-masters/README.md",
|
"readme": "".join(
|
||||||
|
[
|
||||||
|
"https://gitlab.com/perrotuerto_personal/codigo/yasd/",
|
||||||
|
"-/raw/no-masters/README.md",
|
||||||
|
]
|
||||||
|
),
|
||||||
"w3": "https://www.w3.org/2001/XMLSchema.xsd",
|
"w3": "https://www.w3.org/2001/XMLSchema.xsd",
|
||||||
"help_action": "action to perform",
|
"help_action": "action to perform",
|
||||||
"help_input": "input file in YAML format",
|
"help_input": "input file in YAML format",
|
||||||
"help_output": "output file",
|
"help_output": "output file",
|
||||||
|
"help_tests": " ".join(
|
||||||
|
[
|
||||||
|
"one or more XML test files;",
|
||||||
|
"use 'pass' or 'fail' as file name prefix for expectation",
|
||||||
|
]
|
||||||
|
),
|
||||||
"help_quiet": "enable quiet mode",
|
"help_quiet": "enable quiet mode",
|
||||||
"help_log": "write log",
|
"help_log": "write log",
|
||||||
"action_convert": "creating XSD schema",
|
"action_convert": "creating XSD schema",
|
||||||
|
@ -615,8 +752,20 @@ class YASDMessenger:
|
||||||
"no_input": "input file needed",
|
"no_input": "input file needed",
|
||||||
"no_yaml": "YAML dict needed",
|
"no_yaml": "YAML dict needed",
|
||||||
"no_valid": "XSD schema has the following error:\n @error",
|
"no_valid": "XSD schema has the following error:\n @error",
|
||||||
|
"no_exists": "File doesn't exists",
|
||||||
|
"no_file": "Path isn't a file",
|
||||||
"fetching": "fetching '@url'",
|
"fetching": "fetching '@url'",
|
||||||
"validating": "validating XSD schema",
|
"validating": "validating XSD schema",
|
||||||
|
"testing": "testing XSD against '@path'",
|
||||||
|
"test_result": "\n".join(
|
||||||
|
[
|
||||||
|
"test output:",
|
||||||
|
" File: @path",
|
||||||
|
" Expectation: @expectation",
|
||||||
|
" Result: @res\n@msg",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"passed": "Test passed!",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, quiet=False, log=False):
|
def __init__(self, quiet=False, log=False):
|
||||||
|
@ -703,6 +852,7 @@ class YASDCLI:
|
||||||
args.action,
|
args.action,
|
||||||
args.input,
|
args.input,
|
||||||
args.output,
|
args.output,
|
||||||
|
args.tests,
|
||||||
args.quiet,
|
args.quiet,
|
||||||
args.log,
|
args.log,
|
||||||
stdout=True,
|
stdout=True,
|
||||||
|
@ -744,6 +894,15 @@ class YASDCLI:
|
||||||
default=None,
|
default=None,
|
||||||
help=msg["help_output"],
|
help=msg["help_output"],
|
||||||
)
|
)
|
||||||
|
self.parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--tests",
|
||||||
|
type=Path,
|
||||||
|
default=None,
|
||||||
|
help=msg["help_tests"],
|
||||||
|
action="extend",
|
||||||
|
nargs="+",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue