Todo perfilado para continuar con builds
This commit is contained in:
parent
247f05f851
commit
6e1ed70a78
10
README.md
10
README.md
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
warn: This is just a prototype.
|
||||
---
|
||||
|
||||
# YASD, Yet Another Schema Definition
|
||||
|
||||
YASD is a YAML format for human writable XSDs (XML Schema Definition), humans
|
||||
|
@ -22,7 +26,7 @@ flowchart LR
|
|||
sample --- |from| YASD3([YASD])
|
||||
YASD3 --> |to| XML([XML])
|
||||
document --- |from| YASD4([YASD])
|
||||
YASD4 --> |to| MD([Markdown])
|
||||
YASD4 --> |to| RST([reStructuredText])
|
||||
man --- |prints| README
|
||||
```
|
||||
|
||||
|
@ -32,7 +36,7 @@ To better understad what YASD does, see the inputs and outputs:
|
|||
|
||||
- Input: human workable schema in [YASD].
|
||||
- Output: computer processable schema in [XSD].
|
||||
- Output: human readable documentation in [Markdown].
|
||||
- Output: human readable documentation in [reStructuredText][RST].
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
@ -399,6 +403,6 @@ Mandatory.
|
|||
|
||||
[YASD]: https://gitlab.com/amlengua/apal/esquema/-/blob/main/apal.yaml
|
||||
[XSD]: https://gitlab.com/amlengua/apal/esquema/-/blob/main/apal.xsd
|
||||
[Markdown]: https://gitlab.com/amlengua/apal/esquema/-/blob/main/apal.md
|
||||
[RST]: https://gitlab.com/amlengua/apal/esquema/-/blob/main/apal.rst
|
||||
[Tutorials Point]: https://www.tutorialspoint.com/xsd/
|
||||
[W3Schools]: https://www.w3schools.com/xml/schema_intro.asp
|
||||
|
|
312
yasd.py
312
yasd.py
|
@ -13,62 +13,95 @@ from bs4.formatter import XMLFormatter
|
|||
|
||||
class YASD:
|
||||
"""
|
||||
Performs YASD actions.
|
||||
YASD actions performer.
|
||||
"""
|
||||
|
||||
def do(quiet, log, samples, action, filepath):
|
||||
yasd = YASD(quiet, log, samples, filepath)
|
||||
if action == "convert":
|
||||
yasd.convert()
|
||||
elif action == "sample":
|
||||
yasd.sample()
|
||||
elif action == "document":
|
||||
yasd.document()
|
||||
def do(action="check", indata=None, outfile=None, quiet=False, log=False):
|
||||
"""
|
||||
Performs YASD actions directly.
|
||||
|
||||
def __init__(self, quiet=False, log=False, samples=1, filepath=None):
|
||||
Intented for YASDCLI, but can also be used programmatically.
|
||||
|
||||
:param str action: YASD action to perform; 'check' by default
|
||||
:param indata: YASD input; 'None' by default
|
||||
:type indata: None or Path or dict
|
||||
:param outfile: YASD output file path; 'None' by default
|
||||
:type outfile: None or 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
|
||||
:type log: True or False
|
||||
"""
|
||||
yasd = YASD(indata, outfile, quiet, log)
|
||||
yasd.msgr.run(f"action_{action}")
|
||||
if action == "convert":
|
||||
yasd.convert(stdout=True)
|
||||
elif action == "sample":
|
||||
yasd.sample(stdout=True)
|
||||
elif action == "document":
|
||||
yasd.document(stdout=True)
|
||||
|
||||
def __init__(self, indata=None, outfile=None, quiet=False, log=False):
|
||||
"""
|
||||
Inits YASD object.
|
||||
|
||||
:param indata: YASD input; 'None' by default
|
||||
:type indata: None or Path or dict
|
||||
:param outfile: YASD output file path; 'None' by default
|
||||
:type outfile: None or Path
|
||||
:param quiet: If messages are print or not; 'False' by default
|
||||
:type quiet: False or True
|
||||
:type quiet: True or False
|
||||
:param log: If messages are write in a file or not; 'False' by default
|
||||
:type log: False or True
|
||||
:param int samples: Quantity of XML samples; '1' by default
|
||||
:param filepath: YASD file path; 'None' by default
|
||||
:type filepath: None or Path
|
||||
:type log: True or False
|
||||
"""
|
||||
self.msgr = YASDMessenger(quiet=quiet, log=log)
|
||||
valid_input = YASDCheck(self.msgr, filepath)
|
||||
self.filepath = valid_input.filepath
|
||||
self.yaml = valid_input.yaml
|
||||
self.samples = samples
|
||||
self.soups = {
|
||||
"schema": "",
|
||||
"elements": "",
|
||||
"attributes": "",
|
||||
"groups": "",
|
||||
}
|
||||
self.yaml = YASDCheck(indata, self.msgr).yaml
|
||||
self.formatter = XMLFormatter(indent=2)
|
||||
self.soups = self.__get_soups()
|
||||
self.outfile = outfile
|
||||
self.out = ""
|
||||
|
||||
def convert(self):
|
||||
def convert(self, stdout=False):
|
||||
"""
|
||||
Converts YASD to XSD.
|
||||
|
||||
:param stdout: if conversion goes to stdout or not; 'False' by default
|
||||
:type stdout: True or False
|
||||
"""
|
||||
self.__build_schema()
|
||||
self.__build_elements()
|
||||
self.__build_attributes()
|
||||
self.__write()
|
||||
self.__stringify_xsd()
|
||||
if stdout:
|
||||
self.__output()
|
||||
else:
|
||||
return self.out
|
||||
|
||||
def sample(self):
|
||||
def sample(self, stdout=False):
|
||||
"""
|
||||
Generates XML samples from YASD.
|
||||
"""
|
||||
print(f"TODO: {self.samples} samples")
|
||||
Generates XML sample from YASD.
|
||||
|
||||
def document(self):
|
||||
:param stdout: if sample goes to stdout or not; 'False' by default
|
||||
:type stdout: True or False
|
||||
"""
|
||||
Generates MD documentation
|
||||
self.out = "TODO: XML sample"
|
||||
if stdout:
|
||||
self.__output()
|
||||
else:
|
||||
return self.out
|
||||
|
||||
def document(self, stdout=False):
|
||||
"""
|
||||
print("TODO: MD document from :", self.__dict__)
|
||||
Generates RST documentation
|
||||
|
||||
:param stdout: if document goes to stdout or not; 'False' by default
|
||||
:type stdout: True or False
|
||||
"""
|
||||
self.out = f"TODO: RST document from :{self.__dict__}"
|
||||
if stdout:
|
||||
self.__output()
|
||||
else:
|
||||
return self.out
|
||||
|
||||
def __build_schema(self):
|
||||
unwanted = "version schemaLocation".split()
|
||||
|
@ -159,6 +192,17 @@ class YASD:
|
|||
del el["description"]
|
||||
return el
|
||||
|
||||
def __get_soups(self):
|
||||
"""
|
||||
Gets soups structures.
|
||||
"""
|
||||
return {
|
||||
"schema": "",
|
||||
"elements": "",
|
||||
"attributes": "",
|
||||
"groups": "",
|
||||
}
|
||||
|
||||
def __get_base(self, key):
|
||||
"""
|
||||
Gets restriction data type.
|
||||
|
@ -173,77 +217,129 @@ class YASD:
|
|||
else:
|
||||
return "xs:integer"
|
||||
|
||||
def __write(self):
|
||||
def __stringify_xsd(self):
|
||||
"""
|
||||
Writes XSD into a file.
|
||||
Converts BeautifulSoups to pretty text format.
|
||||
"""
|
||||
filename = Path(self.filepath.parent / f"{self.filepath.stem}.xsd")
|
||||
formatter = XMLFormatter(indent=2)
|
||||
xsd = self.soups["schema"]
|
||||
del self.soups["schema"]
|
||||
for key, val in self.soups.items():
|
||||
if key == "schema":
|
||||
xsd = val.schema
|
||||
else:
|
||||
xsd.append(val)
|
||||
filename.write_text(xsd.prettify(formatter=formatter))
|
||||
xsd.append(val)
|
||||
self.out = xsd.prettify(formatter=self.formatter)
|
||||
|
||||
def __output(self, extname=".xsd"):
|
||||
"""
|
||||
Prints in the terminal or writes into a file.
|
||||
"""
|
||||
if self.outfile is None:
|
||||
print(self.out)
|
||||
else:
|
||||
suffix = self.outfile.suffix
|
||||
if len(suffix) > 0 and suffix == suffix.replace(" ", ""):
|
||||
extname = suffix
|
||||
filename = f"{self.outfile.stem}{extname}"
|
||||
filename = Path(self.outfile.parent / filename)
|
||||
filename.write_text(self.out)
|
||||
|
||||
|
||||
class YASDCheck:
|
||||
"""
|
||||
Verifies YASD file.
|
||||
YASD input validator.
|
||||
"""
|
||||
|
||||
def __init__(self, messenger, filepath):
|
||||
def __init__(self, indata=None, messenger=None):
|
||||
"""
|
||||
Inits YASD validator.
|
||||
|
||||
:param YASDMessenger messenger: Object for print or save messages
|
||||
:param filepath: YASD file path
|
||||
:type filepath: None or Path
|
||||
:param indata: YASD input
|
||||
:type indata: None or Path or dict
|
||||
:param messenger: Object for print or save messages
|
||||
:type messenger: None or YASDMessenger
|
||||
"""
|
||||
self.msgr = messenger
|
||||
self.__check_file(filepath)
|
||||
self.__parse_file()
|
||||
# TODO: do extra checks
|
||||
if messenger is None:
|
||||
self.msgr = YASDMessenger()
|
||||
else:
|
||||
self.msgr = messenger
|
||||
if type(indata) is dict:
|
||||
self.yaml = indata
|
||||
else:
|
||||
self.yaml = self.parse_file(self.check_file(indata))
|
||||
self.check_structure()
|
||||
|
||||
def __check_file(self, filepath):
|
||||
def check_file(self, filepath):
|
||||
"""
|
||||
Verifies YASD file.
|
||||
|
||||
:param filepath: YASD file path
|
||||
:type filepath: None or Path
|
||||
"""
|
||||
if filepath is None:
|
||||
if type(filepath).__module__ != "pathlib":
|
||||
self.msgr.run("no_input", level="error")
|
||||
elif not filepath.exists() or not filepath.is_file():
|
||||
self.msgr.run("invalid_input", level="error", file=filepath)
|
||||
self.filepath = filepath.resolve()
|
||||
return filepath.resolve()
|
||||
|
||||
def __parse_file(self):
|
||||
def parse_file(self, filepath):
|
||||
"""
|
||||
Attempts YASD file parsing.
|
||||
|
||||
:param filepath: YASD file path
|
||||
:type filepath: Path
|
||||
"""
|
||||
raw = self.filepath.read_text(encoding="utf8")
|
||||
raw = filepath.read_text(encoding="utf8")
|
||||
try:
|
||||
self.yaml = yaml.safe_load(raw)
|
||||
return yaml.safe_load(raw)
|
||||
except yaml.YAMLError:
|
||||
# TODO: should be log class
|
||||
self.msgr.run("invalid_yaml", level="error")
|
||||
|
||||
def check_structure(self):
|
||||
"""
|
||||
Verifies YASD structure.
|
||||
|
||||
:return: YASD structure
|
||||
:rtype: dict
|
||||
"""
|
||||
# TODO: extra checks for self.yaml
|
||||
...
|
||||
|
||||
|
||||
class YASDMessenger:
|
||||
"""
|
||||
Prints or saves YASD messages.
|
||||
YASD printer or writer.
|
||||
"""
|
||||
|
||||
def keys():
|
||||
"""
|
||||
Messages keys dictionary.
|
||||
|
||||
Here multilang support could be implemented with:
|
||||
https://github.com/sectasy0/pyi18n
|
||||
"""
|
||||
return {
|
||||
"description": """
|
||||
YASD, Yet Another Schema Definition. YASD is a YAML format for
|
||||
human writable XSDs (XML Schema Definition), humans declare what is
|
||||
indispensable, leaving the machines to do the rest of the
|
||||
unreadable <syntaxis who_can_read_this="?" />.
|
||||
""",
|
||||
"epilog": """
|
||||
(c) 2023 Perro Tuerto <hi@perrotuerto.blog>. Founded by Mexican
|
||||
Academy of Language <https://academia.org.mx>. Licensed under GPLv3
|
||||
<https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
""",
|
||||
"help_action": "action to perform",
|
||||
"help_input": "input file in YAML format",
|
||||
"help_output": "output file",
|
||||
"help_quiet": "enable quiet mode",
|
||||
"help_log": "write log",
|
||||
"action_convert": "Creating XSD schema",
|
||||
"action_check": "Checking YASD",
|
||||
"action_sample": "Creating XML sample",
|
||||
"action_document": "Creating RST documentation",
|
||||
"invalid_level": "Invalid log level '@lvl'",
|
||||
"no_input": "Input file needed.",
|
||||
"invalid_input": "Invalid file '@file'",
|
||||
"invalid_yaml": "Invalid YAML structure",
|
||||
"no_input": "Input file needed.",
|
||||
}
|
||||
|
||||
def __init__(self, quiet=False, log=False):
|
||||
|
@ -278,7 +374,7 @@ class YASDMessenger:
|
|||
|
||||
:param str level: Log level
|
||||
"""
|
||||
if not level in ["trace", "debug", "info", "warn", "error", "fatal"]:
|
||||
if level not in ["trace", "debug", "info", "warn", "error", "fatal"]:
|
||||
YASDMessenger().run("invalid_level", level="warn", lvl=level)
|
||||
|
||||
def __get_msg(self, key, **kwargs):
|
||||
|
@ -300,49 +396,59 @@ class YASDMessenger:
|
|||
return key
|
||||
|
||||
|
||||
def main():
|
||||
class YASDCLI:
|
||||
"""
|
||||
Gets and parses argv, then calls YASD.
|
||||
YASD command-line interface.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="yasd",
|
||||
description="""
|
||||
YASD, Yet Another Schema Definition.
|
||||
YASD is a YAML format for human writable XSDs (XML Schema Definition),
|
||||
humans declare what is indispensable, leaving the machines to do the
|
||||
rest of the unreadable <syntaxis who_can_read_this="?" />.
|
||||
""",
|
||||
epilog="""
|
||||
(c) 2023 Perro Tuerto <hi@perrotuerto.blog>.
|
||||
Founded by Mexican Academy of Language <https://academia.org.mx>.
|
||||
Licensed under GPLv3 <https://www.gnu.org/licenses/gpl-3.0.en.html>.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"action",
|
||||
choices=["convert", "check", "sample", "document", "man"],
|
||||
help="action to perform",
|
||||
)
|
||||
parser.add_argument(
|
||||
"file",
|
||||
type=Path,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="file in YAML format",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q", "--quiet", action="store_true", help="enable quiet mode"
|
||||
)
|
||||
parser.add_argument("-l", "--log", action="store_true", help="write log")
|
||||
parser.add_argument(
|
||||
"-n", "--num", default=1, help="number of XML samples; 1 by default"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if args.action == "man":
|
||||
print("MAN")
|
||||
else:
|
||||
YASD.do(args.quiet, args.log, args.num, args.action, args.file)
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Inits YASD CLI.
|
||||
"""
|
||||
self.__init_parser()
|
||||
args = self.parser.parse_args()
|
||||
if args.action == "man":
|
||||
# TODO: print man from README
|
||||
print("TODO: MAN")
|
||||
else:
|
||||
YASD.do(args.action, args.input, args.output, args.quiet, args.log)
|
||||
|
||||
def __init_parser(self):
|
||||
"""
|
||||
Inits argument parser.
|
||||
"""
|
||||
msg = YASDMessenger.keys()
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="yasd",
|
||||
description=msg["description"],
|
||||
epilog=msg["epilog"],
|
||||
)
|
||||
self.parser.add_argument(
|
||||
"action",
|
||||
choices=["convert", "check", "sample", "document", "man"],
|
||||
help=msg["help_action"],
|
||||
)
|
||||
self.parser.add_argument(
|
||||
"input",
|
||||
type=Path,
|
||||
nargs="?",
|
||||
default=None,
|
||||
help=msg["help_input"],
|
||||
)
|
||||
self.parser.add_argument(
|
||||
"-q", "--quiet", action="store_true", help=msg["help_quiet"]
|
||||
)
|
||||
self.parser.add_argument(
|
||||
"-l", "--log", action="store_true", help=msg["help_log"]
|
||||
)
|
||||
self.parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=Path,
|
||||
default=None,
|
||||
help=msg["help_output"],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
YASDCLI()
|
||||
|
|
Loading…
Reference in New Issue