El XSD ya es probado con XML de ejemplo

This commit is contained in:
perro tuerto 2023-02-28 19:17:25 -08:00
parent d7ddb5f088
commit 5ef0994a93
1 changed files with 171 additions and 12 deletions

183
yasd.py
View File

@ -25,6 +25,7 @@ class YASD:
action="check",
indata=None,
outfile=None,
tests=None,
quiet=False,
log=False,
stdout=False,
@ -40,6 +41,8 @@ class YASD:
:type indata: None or Path or dict
:param outfile: YASD output file path; 'None' by default
: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
:type quiet: True or False
: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'
: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}")
match action:
case "document":
@ -68,6 +71,7 @@ class YASD:
self,
indata=None,
outfile=None,
tests=None,
quiet=False,
log=False,
stdout=False,
@ -80,6 +84,8 @@ class YASD:
:type indata: None or Path or dict
:param outfile: YASD output file path; 'None' by default
: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
:type quiet: True or False
: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.stdout = stdout
self.validate = validate
self.tests = set(tests) if tests is not None else None
if outfile is None:
self.outfile = None
else:
@ -107,11 +114,11 @@ class YASD:
:rtype: bs4.element.Tag
"""
self.xsd = YASDXSD(self.yaml, self.msgr).xsd
out = self.__output(self.xsd)
out = self.__output(self.xsd, self.msgr)
if self.validate:
return YASDCheck.xsd(out, self.msgr)
return YASDCheck.xsd(out, self.tests, self.msgr)
else:
return out
return {"xsd": out, "tests": []}
def sample(self):
"""
@ -121,7 +128,7 @@ class YASD:
:rtype: bs4.element.Tag
"""
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):
"""
@ -131,21 +138,26 @@ class YASD:
:rtype: str
"""
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.
: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
:rtype: str
"""
if type(outdata) is not str:
if not isinstance(outdata, str):
outdata = outdata.prettify(formatter=self.formatter)
outdata = f'<?xml version="1.0"?>\n{outdata}'
if self.stdout:
if self.outfile is None:
print(outdata)
msgr.run(outdata)
else:
suffix = self.outfile.suffix
if len(suffix) > 0 and suffix == suffix.replace(" ", ""):
@ -237,6 +249,8 @@ class YASDXSD:
del el["fixed"]
if "restriction" in el.keys() and "datatype" in el.keys():
del el["datatype"]
if "type" in el.keys() and tag == "element":
del el["type"]
for key, val in el.items():
if key == "datatype":
element["type"] = f"xs:{val}"
@ -518,22 +532,134 @@ class YASDCheck:
except Exception:
msgr.run("no_url", level="error", url=url)
def xsd(xsd, msgr=None):
def xsd(xsd, tests=None, msgr=None):
"""
Validates XSD.
:param str xsd: XSD as XML string
:param tests: XML file paths
:type tests: None or set
:param msgr: Messenger object
:type msgr: None or YASDMessenger
:return: XSD as string and results as list
:rtype: dict
"""
msgr = YASDCheck.messenger(msgr)
msgr.run("validating")
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:
error = str(error).replace("\n", "\n ")
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):
"""
Inits YASD validator.
@ -597,11 +723,22 @@ class YASDMessenger:
Academy of Language <https://academia.org.mx>. Licensed under GPLv3
<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",
"help_action": "action to perform",
"help_input": "input file in YAML format",
"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_log": "write log",
"action_convert": "creating XSD schema",
@ -615,8 +752,20 @@ class YASDMessenger:
"no_input": "input file needed",
"no_yaml": "YAML dict needed",
"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'",
"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):
@ -703,6 +852,7 @@ class YASDCLI:
args.action,
args.input,
args.output,
args.tests,
args.quiet,
args.log,
stdout=True,
@ -744,6 +894,15 @@ class YASDCLI:
default=None,
help=msg["help_output"],
)
self.parser.add_argument(
"-t",
"--tests",
type=Path,
default=None,
help=msg["help_tests"],
action="extend",
nargs="+",
)
if __name__ == "__main__":