From fdd44d72ffdcd54bd8f4213642e147737a18a9ab Mon Sep 17 00:00:00 2001 From: perro Date: Fri, 27 Jan 2023 12:19:39 -0800 Subject: [PATCH] =?UTF-8?q?Fin=20de=20refactorizaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yasd.py | 263 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 174 insertions(+), 89 deletions(-) diff --git a/yasd.py b/yasd.py index 7ca8390..ae14e24 100755 --- a/yasd.py +++ b/yasd.py @@ -16,7 +16,14 @@ class YASD: YASD actions performer. """ - def do(action="check", indata=None, outfile=None, quiet=False, log=False): + def do( + action="check", + indata=None, + outfile=None, + quiet=False, + log=False, + stdout=False, + ): """ Performs YASD actions directly. @@ -31,17 +38,27 @@ class YASD: :type quiet: True or False :param log: If messages are write in a file or not; 'False' by default :type log: True or False + :param stdout: if conversion goes to stdout or not; 'False' by default + :type stdout: True or False + :return: Output data; str on 'document'; bs4.element.Tag on 'convert' + or 'sample'; YAML dict on 'check' + :rtype: str or bs4.element.Tag or dict """ - yasd = YASD(indata, outfile, quiet, log) + yasd = YASD(indata, outfile, quiet, log, stdout) 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) + match action: + case "document": + return yasd.document() + case "convert": + return yasd.convert() + case "sample": + return yasd.sample() + case "check": + return yasd.yaml - def __init__(self, indata=None, outfile=None, quiet=False, log=False): + def __init__( + self, indata=None, outfile=None, quiet=False, log=False, stdout=False + ): """ Inits YASD object. @@ -55,56 +72,83 @@ class YASD: :type log: True or False """ self.msgr = YASDMessenger(quiet=quiet, log=log) + self.outfile = YASDCheck.file(outfile, self.msgr) self.yaml = YASDCheck(indata, self.msgr).yaml self.formatter = XMLFormatter(indent=2) - self.outfile = outfile - self.xsd = None - self.out = "" + self.stdout = stdout - def convert(self, stdout=False): + def convert(self): """ Converts YASD to XSD. - :param stdout: if conversion goes to stdout or not; 'False' by default - :type stdout: True or False + :return: XSD element + :rtype: bs4.element.Tag """ + self.xsd = YASDXSD(self.yaml, self.msgr).xsd + return self.__output(self.xsd) + + def sample(self): + """ + Generates XML sample from YASD. + + :return: XML element + :rtype: bs4.element.Tag + """ + self.xml = YASDXML(self.yaml, self.msgr).xml + return self.__output(self.xml, extname=".xml") + + def document(self): + """ + Generates RST documentation. + + :return: RST document + :rtype: str + """ + self.rst = YASDRST(self.yaml, self.msgr).rst + return self.__output(self.rst, extname=".rst") + + def __output(self, outdata, extname=".xsd"): + """ + Prints in the terminal or writes into a file. + + :return: Output data; str if outdata comes from YASDRST; otherwise + bs4.element.Tag + :rtype: str or bs4.element.Tag + """ + if self.stdout: + if type(outdata) is not str: + outdata = outdata.prettify(formatter=self.formatter) + if self.outfile is None: + print(outdata) + 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(outdata) + return outdata + + +class YASDXSD: + """ + YASD convertor to XSD. + """ + + # TODO refactor so it fits XSD grammar, cfr. reference: + # https://www.w3schools.com/xml/schema_elements_ref.asp + + def __init__(self, yml=None, messenger=None): + """ + Inits YASD convertor. + """ + self.msgr = YASDCheck.messenger(messenger) + self.yaml = YASDCheck.yaml(yml, self.msgr) + self.xsd = BeautifulSoup(parser="xml") self.__build_schema() self.__build_elements() self.__build_attributes() self.__build_groups() - self.__stringify_xsd() - if stdout: - self.__output() - else: - return self.out - - def sample(self, stdout=False): - """ - Generates XML sample from YASD. - - :param stdout: if sample goes to stdout or not; 'False' by default - :type stdout: True or False - """ - # TODO XML sample - self.out = "XML sample" - if stdout: - self.__output() - else: - return self.out - - def document(self, stdout=False): - """ - Generates RST documentation. - - :param stdout: if document goes to stdout or not; 'False' by default - :type stdout: True or False - """ - # TODO RST document - self.out = f"RST document from :{self.__dict__}" - if stdout: - self.__output() - else: - return self.out def __build_schema(self): """ @@ -112,7 +156,6 @@ class YASD: """ for key in ["version", "schemaLocation"]: del self.yaml["schema"][key] - self.xsd = BeautifulSoup(parser="xml") schema = self.xsd.new_tag("schema", nsprefix="xs") schema["xmlns:xs"] = "http://www.w3.org/2001/XMLSchema" for key, val in self.yaml["schema"].items(): @@ -152,8 +195,6 @@ class YASD: :param dict el: YASD element :param str tag: tag name for XSD node """ - # TODO fix according to 'attribute', 'element' and 'simpleType' refs - # https://www.w3schools.com/xml/schema_elements_ref.asp element = self.xsd.new_tag(tag, nsprefix="xs") if "default" in el.keys() and "fixed" in el.keys(): del el["fixed"] @@ -172,8 +213,6 @@ class YASD: :param dict el: YASD element """ - # TODO fix according to 'element' and 'complexType' refs - # https://www.w3schools.com/xml/schema_elements_ref.asp element = self.__build_complex_root(el) complex_type = self.__build_complex_type(el) self.__add_references(complex_type, el, is_attr=True) @@ -313,32 +352,86 @@ class YASD: del el["description"] return el - def __stringify_xsd(self): - """ - Converts BeautifulSoups to pretty text format. - """ - self.out = self.xsd.prettify(formatter=self.formatter) - def __output(self, extname=".xsd"): +class YASDXML: + """ + YASD sampler to XML. + """ + + # TODO XML Sample + + def __init__(self, yml=None, messenger=None): """ - Prints in the terminal or writes into a file. + Inits YASD sampler. """ - 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) + self.msgr = YASDCheck.messenger(messenger) + self.yaml = YASDCheck.yaml(yml, self.msgr) + self.xml = "XML sample" + + +class YASDRST: + """ + YASD document generator to RST. + """ + + # TODO RST document + + def __init__(self, yml=None, messenger=None): + """ + Inits YASD document generator. + """ + self.msgr = YASDCheck.messenger(messenger) + self.yaml = YASDCheck.yaml(yml, self.msgr) + self.rst = "RST document" class YASDCheck: """ - YASD input validator. + YASD validator. + + Validates everything related to YASD classes. """ + def messenger(msgr=None): + """ + Verifies if messenger was initialize. + + :param messenger: Messenger object + :type messenger: None or YASDMessenger + """ + if msgr is None: + return YASDMessenger() + else: + return msgr + + def yaml(yml=None, msgr=None): + """ + Verifies if yaml exists. + + :param dict yml: YAML object + :param msgr: Messenger object + :type msgr: None or YASDMessenger + """ + msgr = YASDCheck.messenger(msgr) + if yml is None or type(yml) is not dict: + msgr.run("no_yaml", level="error") + else: + return yml + + def file(filepath=None, msgr=None): + """ + Verififes if file exists. + + :param filepath: File path + :type filepath: None or Path + """ + msgr = YASDCheck.messenger(msgr) + if type(filepath).__module__ != "pathlib": + msgr.run("no_input", level="error") + elif not filepath.exists() or not filepath.is_file(): + msgr.run("invalid_input", level="error", file=filepath) + return filepath.resolve() + def __init__(self, indata=None, messenger=None): """ Inits YASD validator. @@ -348,29 +441,13 @@ class YASDCheck: :param messenger: Object for print or save messages :type messenger: None or YASDMessenger """ - if messenger is None: - self.msgr = YASDMessenger() - else: - self.msgr = messenger + self.msgr = YASDCheck.messenger(messenger) if type(indata) is dict: self.yaml = indata else: - self.yaml = self.parse_file(self.check_file(indata)) + self.yaml = self.parse_file(YASDCheck.file(indata, self.msgr)) self.check_structure() - def check_file(self, filepath): - """ - Verifies YASD file. - - :param filepath: YASD file path - :type filepath: None or Path - """ - 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) - return filepath.resolve() - def parse_file(self, filepath): """ Attempts YASD file parsing. @@ -429,6 +506,7 @@ class YASDMessenger: "invalid_level": "Invalid log level '@lvl'", "invalid_input": "Invalid file '@file'", "invalid_yaml": "Invalid YAML structure", + "no_yaml": "YAML dict needed", "no_input": "Input file needed.", } @@ -501,7 +579,14 @@ class YASDCLI: # TODO print man from README print("Manual") else: - YASD.do(args.action, args.input, args.output, args.quiet, args.log) + YASD.do( + args.action, + args.input, + args.output, + args.quiet, + args.log, + True, + ) def __init_parser(self): """