Previous changeset 91:7176af503cdd (2020-11-19) Next changeset 93:df1205dcf676 (2020-11-20) |
Commit message:
Uploaded |
added:
toolfactory/galaxyxml/__init__.py toolfactory/galaxyxml/tool/__init__.py toolfactory/galaxyxml/tool/__pycache__/__init__.cpython-36.pyc toolfactory/galaxyxml/tool/__pycache__/import_xml.cpython-36.pyc toolfactory/galaxyxml/tool/import_xml.py toolfactory/galaxyxml/tool/parameters/__init__.py toolfactory/galaxyxml/tool/parameters/__pycache__/__init__.cpython-36.pyc |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/galaxyxml/__init__.py Thu Nov 19 23:59:50 2020 +0000 |
[ |
@@ -0,0 +1,64 @@ +from builtins import object +from builtins import str + +from lxml import etree + + +class GalaxyXML(object): + def __init__(self): + self.root = etree.Element("root") + + def export(self): + return etree.tostring(self.root, pretty_print=True, encoding="unicode") + + +class Util(object): + @classmethod + def coerce(cls, data, kill_lists=False): + """Recursive data sanitisation + """ + if isinstance(data, dict): + return {k: cls.coerce(v, kill_lists=kill_lists) for k, v in list(data.items()) if v is not None} + elif isinstance(data, list): + if kill_lists: + return cls.coerce(data[0]) + else: + return [cls.coerce(v, kill_lists=kill_lists) for v in data] + else: + return cls.coerce_value(data) + + @classmethod + def coerce_value(cls, obj): + """Make everything a string! + """ + if isinstance(obj, bool): + if obj: + return "true" + else: + return "false" + elif isinstance(obj, str): + return obj + else: + return str(obj) + + @classmethod + def clean_kwargs(cls, params, final=False): + if "kwargs" in params: + kwargs = params["kwargs"] + for k in kwargs: + params[k] = kwargs[k] + del params["kwargs"] + if "self" in params: + del params["self"] + + if "__class__" in params: + del params["__class__"] + + # There will be more params, it would be NICE to use a whitelist + # instead of a blacklist, but until we have more data let's just + # blacklist stuff we see commonly. + if final: + for blacklist in ("positional",): + if blacklist in params: + del params[blacklist] + return params |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/galaxyxml/tool/__init__.py Thu Nov 19 23:59:50 2020 +0000 |
[ |
@@ -0,0 +1,184 @@ +import copy +import logging + +from galaxyxml import GalaxyXML, Util +from galaxyxml.tool.parameters import XMLParam + +from lxml import etree + +VALID_TOOL_TYPES = ("data_source", "data_source_async") +VALID_URL_METHODS = ("get", "post") + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class Tool(GalaxyXML): + + def __init__( + self, + name, + id, + version, + description, + executable, + hidden=False, + tool_type=None, + URL_method=None, + workflow_compatible=True, + interpreter=None, + version_command="interpreter filename.exe --version", + command_override=None, + ): + + self.executable = executable + self.interpreter = interpreter + self.command_override = command_override + kwargs = { + "name": name, + "id": id, + "version": version, + "hidden": hidden, + "workflow_compatible": workflow_compatible, + } + self.version_command = version_command + + # Remove some of the default values to make tools look a bit nicer + if not hidden: + del kwargs["hidden"] + if workflow_compatible: + del kwargs["workflow_compatible"] + + kwargs = Util.coerce(kwargs) + self.root = etree.Element("tool", **kwargs) + + if tool_type is not None: + if tool_type not in VALID_TOOL_TYPES: + raise Exception("Tool type must be one of %s" % ",".join(VALID_TOOL_TYPES)) + else: + kwargs["tool_type"] = tool_type + + if URL_method is not None: + if URL_method in VALID_URL_METHODS: + kwargs["URL_method"] = URL_method + else: + raise Exception("URL_method must be one of %s" % ",".join(VALID_URL_METHODS)) + + description_node = etree.SubElement(self.root, "description") + description_node.text = description + + def add_comment(self, comment_txt): + comment = etree.Comment(comment_txt) + self.root.insert(0, comment) + + def append_version_command(self): + version_command = etree.SubElement(self.root, "version_command") + try: + version_command.text = etree.CDATA(self.version_command) + except Exception: + pass + + def append(self, sub_node): + if issubclass(type(sub_node), XMLParam): + self.root.append(sub_node.node) + else: + self.root.append(sub_node) + + def clean_command_string(self, command_line): + clean = [] + for x in command_line: + if x is not [] and x is not [""]: + clean.append(x) + + return "\n".join(clean) + + def export(self, keep_old_command=False): # noqa + + export_xml = copy.deepcopy(self) + + try: + export_xml.append(export_xml.edam_operations) + except Exception: + pass + + try: + export_xml.append(export_xml.edam_topics) + except Exception: + pass + + try: + export_xml.append(export_xml.requirements) + except Exception: + pass + + try: + export_xml.append(export_xml.configfiles) + except Exception: + pass + + if self.command_override: + command_line = self.command_override + else: + command_line = [] + try: + command_line.append(export_xml.inputs.cli()) + except Exception as e: + logger.warning(str(e)) + + try: + command_line.append(export_xml.outputs.cli()) + except Exception: + pass + + # Add stdio section + stdio = etree.SubElement(export_xml.root, "stdio") + etree.SubElement(stdio, "exit_code", range="1:", level="fatal") + + # Append version command + export_xml.append_version_command() + + # Steal interpreter from kwargs + command_kwargs = {} + if export_xml.interpreter is not None: + command_kwargs["interpreter"] = export_xml.interpreter + + # Add command section + command_node = etree.SubElement(export_xml.root, "command", **command_kwargs) + + if keep_old_command: + if getattr(self, "command", None): + command_node.text = etree.CDATA(export_xml.command) + else: + logger.warning("The tool does not have any old command stored. " + "Only the command line is written.") + command_node.text = export_xml.executable + else: + if self.command_override: + actual_cli = export_xml.clean_command_string(command_line) + else: + actual_cli = "%s %s" % (export_xml.executable, export_xml.clean_command_string(command_line)) + command_node.text = etree.CDATA(actual_cli.strip()) + + try: + export_xml.append(export_xml.inputs) + except Exception: + pass + + try: + export_xml.append(export_xml.outputs) + except Exception: + pass + + try: + export_xml.append(export_xml.tests) + except Exception: + pass + + help_element = etree.SubElement(export_xml.root, "help") + help_element.text = etree.CDATA(export_xml.help) + + try: + export_xml.append(export_xml.citations) + except Exception: + pass + + return super(Tool, export_xml).export() |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/__pycache__/__init__.cpython-36.pyc |
b |
Binary file toolfactory/galaxyxml/tool/__pycache__/__init__.cpython-36.pyc has changed |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/__pycache__/import_xml.cpython-36.pyc |
b |
Binary file toolfactory/galaxyxml/tool/__pycache__/import_xml.cpython-36.pyc has changed |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/import_xml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/galaxyxml/tool/import_xml.py Thu Nov 19 23:59:50 2020 +0000 |
[ |
b'@@ -0,0 +1,713 @@\n+import logging\n+import xml.etree.ElementTree as ET\n+\n+import galaxyxml.tool as gxt\n+import galaxyxml.tool.parameters as gxtp\n+\n+logging.basicConfig(level=logging.INFO)\n+logger = logging.getLogger(__name__)\n+\n+\n+class GalaxyXmlParser(object):\n+ """\n+ Class to import content from an existing Galaxy XML wrapper.\n+ """\n+\n+ def _init_tool(self, xml_root):\n+ """\n+ Init tool from existing xml tool.\n+\n+ :param xml_root: root of the galaxy xml file.\n+ :type xml_root: :class:`xml.etree._Element`\n+ """\n+ version_cmd = None\n+ description = None\n+ for child in xml_root:\n+ if child.tag == "description":\n+ description = child.text\n+ elif child.tag == "command":\n+ executable = child.text.split()[0]\n+ command = child.text\n+ elif child.tag == "version_command":\n+ version_cmd = child.text\n+\n+ tool = gxt.Tool(\n+ xml_root.attrib["name"],\n+ xml_root.attrib["id"],\n+ xml_root.attrib.get("version", None),\n+ description,\n+ executable,\n+ hidden=xml_root.attrib.get("hidden", False),\n+ tool_type=xml_root.attrib.get("tool_type", None),\n+ URL_method=xml_root.attrib.get("URL_method", None),\n+ workflow_compatible=xml_root.attrib.get("workflow_compatible", True),\n+ version_command=version_cmd,\n+ )\n+ tool.command = command\n+ return tool\n+\n+ def _load_description(self, tool, desc_root):\n+ """\n+ <description> is already loaded during initiation.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param desc_root: root of <description> tag.\n+ :type desc_root: :class:`xml.etree._Element`\n+ """\n+ logger.info("<description> is loaded during initiation of the object.")\n+\n+ def _load_version_command(self, tool, vers_root):\n+ """\n+ <version_command> is already loaded during initiation.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param vers_root: root of <version_command> tag.\n+ :type vers_root: :class:`xml.etree._Element`\n+ """\n+ logger.info("<version_command> is loaded during initiation of the object.")\n+\n+ def _load_stdio(self, tool, stdio_root):\n+ """\n+ So far, <stdio> is automatically generated by galaxyxml.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param desc_root: root of <stdio> tag.\n+ :type desc_root: :class:`xml.etree._Element`\n+ """\n+ logger.info("<stdio> is not loaded but automatically generated by galaxyxml.")\n+\n+ def _load_command(self, tool, desc_root):\n+ """\n+ <command> is already loaded during initiation.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param desc_root: root of <command> tag.\n+ :type desc_root: :class:`xml.etree._Element`\n+ """\n+ logger.info("<command> is loaded during initiation of the object.")\n+\n+ def _load_help(self, tool, help_root):\n+ """\n+ Load the content of the <help> into the tool.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param requirements_root: root of <help> tag.\n+ :type requirements_root: :class:`xml.etree._Element`\n+ """\n+ tool.help = help_root.text\n+\n+ def _load_requirements(self, tool, requirements_root):\n+ """\n+ Add <requirements> to the tool.\n+\n+ :param tool: Tool object from galaxyxml.\n+ :type tool: :class:`galaxyxml.tool.Tool`\n+ :param requirements_root: root of <requirements> tag.\n+ :type requirements_root: :class:`xml.etree._Element`\n+ """'..b')\n+ outputs_root.append(collection)\n+\n+ def _load_discover_datasets(self, root, disc_root):\n+ """\n+ Add <discover_datasets> to root (<collection>).\n+\n+ :param root: root to append <collection> to.\n+ :param disc_root: root of <discover_datasets> tag.\n+ :param disc_root: :class:`xml.etree._Element`\n+ """\n+ root.append(\n+ gxtp.DiscoverDatasets(\n+ disc_root.attrib["pattern"],\n+ directory=disc_root.attrib.get("directory", None),\n+ format=disc_root.attrib.get("format", None),\n+ ext=disc_root.attrib.get("ext", None),\n+ visible=disc_root.attrib.get("visible", None),\n+ )\n+ )\n+\n+ def _load_filter(self, root, filter_root):\n+ """\n+ Add <filter> to root (<collection> or <data>).\n+\n+ :param root: root to append <collection> to.\n+ :param coll_root: root of <filter> tag.\n+ :param coll_root: :class:`xml.etree._Element`\n+ """\n+ root.append(gxtp.OutputFilter(filter_root.text))\n+\n+ def load_outputs(self, root, outputs_root):\n+ """\n+ Add <outputs> to the root.\n+\n+ :param root: root to attach <outputs> to (<tool>).\n+ :param tests_root: root of <outputs> tag.\n+ :type tests_root: :class:`xml.etree._Element`\n+ """\n+ for out_child in outputs_root:\n+ try:\n+ getattr(self, "_load_{}".format(out_child.tag))(root, out_child)\n+ except AttributeError:\n+ logger.warning(out_child.tag + " tag is not processed for <outputs>.")\n+\n+\n+class TestsParser(object):\n+ """\n+ Class to parse content of the <tests> tag from a Galaxy XML wrapper.\n+ """\n+\n+ def _load_param(self, test_root, param_root):\n+ """\n+ Add <param> to the <test>.\n+\n+ :param root: <test> root to append <param> to.\n+ :param repeat_root: root of <param> tag.\n+ :param repeat_root: :class:`xml.etree._Element`\n+ """\n+ test_root.append(\n+ gxtp.TestParam(\n+ param_root.attrib["name"],\n+ value=param_root.attrib.get("value", None),\n+ ftype=param_root.attrib.get("ftype", None),\n+ dbkey=param_root.attrib.get("dbkey", None),\n+ )\n+ )\n+\n+ def _load_output(self, test_root, output_root):\n+ """\n+ Add <output> to the <test>.\n+\n+ :param root: <test> root to append <output> to.\n+ :param repeat_root: root of <output> tag.\n+ :param repeat_root: :class:`xml.etree._Element`\n+ """\n+ test_root.append(\n+ gxtp.TestOutput(\n+ name=output_root.attrib.get("name", None),\n+ file=output_root.attrib.get("file", None),\n+ ftype=output_root.attrib.get("ftype", None),\n+ sort=output_root.attrib.get("sort", None),\n+ value=output_root.attrib.get("value", None),\n+ md5=output_root.attrib.get("md5", None),\n+ checksum=output_root.attrib.get("checksum", None),\n+ compare=output_root.attrib.get("compare", None),\n+ lines_diff=output_root.attrib.get("lines_diff", None),\n+ delta=output_root.attrib.get("delta", None),\n+ )\n+ )\n+\n+ def load_tests(self, root, tests_root):\n+ """\n+ Add <tests> to the root.\n+\n+ :param root: root to attach <tests> to (<tool>).\n+ :param tests_root: root of <tests> tag.\n+ :type tests_root: :class:`xml.etree._Element`\n+ """\n+ for test_root in tests_root:\n+ test = gxtp.Test()\n+ for test_child in test_root:\n+ try:\n+ getattr(self, "_load_{}".format(test_child.tag))(test, test_child)\n+ except AttributeError:\n+ logger.warning(test_child.tag + " tag is not processed within <test>.")\n+ root.append(test)\n' |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/parameters/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/galaxyxml/tool/parameters/__init__.py Thu Nov 19 23:59:50 2020 +0000 |
[ |
b'@@ -0,0 +1,739 @@\n+from builtins import object\n+from builtins import str\n+\n+from galaxyxml import Util\n+\n+from lxml import etree\n+\n+\n+\n+class XMLParam(object):\n+ name = "node"\n+\n+ def __init__(self, *args, **kwargs):\n+ # http://stackoverflow.com/a/12118700\n+ self.children = []\n+ kwargs = {k: v for k, v in list(kwargs.items()) if v is not None}\n+ kwargs = Util.coerce(kwargs, kill_lists=True)\n+ kwargs = Util.clean_kwargs(kwargs, final=True)\n+ self.node = etree.Element(self.name, **kwargs)\n+\n+ def append(self, sub_node):\n+ if self.acceptable_child(sub_node):\n+ # If one of ours, they aren\'t etree nodes, they\'re custom objects\n+ if issubclass(type(sub_node), XMLParam):\n+ self.node.append(sub_node.node)\n+ self.children.append(sub_node)\n+ else:\n+ raise Exception(\n+ "Child was unacceptable to parent (%s is not appropriate for %s)" % (type(self), type(sub_node))\n+ )\n+ else:\n+ raise Exception(\n+ "Child was unacceptable to parent (%s is not appropriate for %s)" % (type(self), type(sub_node))\n+ )\n+\n+ def validate(self):\n+ # Very few need validation, but some nodes we may want to have\n+ # validation routines on. Should only be called when DONE.\n+ for child in self.children:\n+ # If any child fails to validate return false.\n+ if not child.validate():\n+ return False\n+ return True\n+\n+ def cli(self):\n+ lines = []\n+ for child in self.children:\n+ lines.append(child.command_line())\n+ # lines += child.command_line()\n+ return "\\n".join(lines)\n+\n+ def command_line(self):\n+ return None\n+\n+\n+class RequestParamTranslation(XMLParam):\n+ name = "request_param_translation"\n+\n+ def __init__(self, **kwargs):\n+ self.node = etree.Element(self.name)\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, RequestParamTranslation)\n+\n+\n+class RequestParam(XMLParam):\n+ name = "request_param"\n+\n+ def __init__(self, galaxy_name, remote_name, missing, **kwargs):\n+ # TODO: bulk copy locals into self.attr?\n+ self.galaxy_name = galaxy_name\n+ # http://stackoverflow.com/a/1408860\n+ params = Util.clean_kwargs(locals().copy())\n+ super(RequestParam, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, AppendParam) and self.galaxy_name == "URL"\n+\n+\n+class AppendParam(XMLParam):\n+ name = "append_param"\n+\n+ def __init__(self, separator="&", first_separator="?", join="=", **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(AppendParam, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, AppendParamValue)\n+\n+\n+class AppendParamValue(XMLParam):\n+ name = "value"\n+\n+ def __init__(self, name="_export", missing="1", **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(AppendParamValue, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return False\n+\n+\n+class EdamOperations(XMLParam):\n+ name = "edam_operations"\n+\n+ def acceptable_child(self, child):\n+ return issubclass(type(child), EdamOperation)\n+\n+ def has_operation(self, edam_operation):\n+ """\n+ Check the presence of a given edam_operation.\n+\n+ :type edam_operation: STRING\n+ """\n+ for operation in self.children:\n+ if operation.node.text == edam_operation:\n+ return True\n+ return False\n+\n+\n+class EdamOperation(XMLParam):\n+ name = "edam_operation"\n+\n+ def __init__(self, value):\n+ super(EdamOperation, self).__init__()\n+ self.node.text = str(value)\n+\n+\n+class EdamTopics(XMLParam):\n+ name = "edam_topics"\n+\n+ def acceptable_child(self, chil'..b'e"):\n+ return self.command_line_override\n+ else:\n+ return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name())\n+\n+ def mako_name(self):\n+ return "$" + self.mako_identifier\n+\n+ def flag(self):\n+ flag = "-" * self.num_dashes\n+ return flag + self.mako_identifier\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, OutputFilter) or isinstance(child, ChangeFormat) or isinstance(child, DiscoverDatasets)\n+\n+\n+class OutputFilter(XMLParam):\n+ name = "filter"\n+\n+ def __init__(self, text, **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ del params["text"]\n+ super(OutputFilter, self).__init__(**params)\n+ self.node.text = text\n+\n+ def acceptable_child(self, child):\n+ return False\n+\n+\n+class ChangeFormat(XMLParam):\n+ name = "change_format"\n+\n+ def __init__(self, **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(ChangeFormat, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, ChangeFormatWhen)\n+\n+\n+class ChangeFormatWhen(XMLParam):\n+ name = "when"\n+\n+ def __init__(self, input, format, value, **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(ChangeFormatWhen, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return False\n+\n+\n+class OutputCollection(XMLParam):\n+ name = "collection"\n+\n+ def __init__(\n+ self,\n+ name,\n+ type=None,\n+ label=None,\n+ format_source=None,\n+ type_source=None,\n+ structured_like=None,\n+ inherit_format=None,\n+ **kwargs\n+ ):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(OutputCollection, self).__init__(**params)\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, OutputData) or isinstance(child, OutputFilter) or isinstance(child, DiscoverDatasets)\n+\n+\n+class DiscoverDatasets(XMLParam):\n+ name = "discover_datasets"\n+\n+ def __init__(self, pattern, directory=None, format=None, ext=None, visible=None, **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(DiscoverDatasets, self).__init__(**params)\n+\n+\n+class Tests(XMLParam):\n+ name = "tests"\n+\n+ def acceptable_child(self, child):\n+ return issubclass(type(child), Test)\n+\n+\n+class Test(XMLParam):\n+ name = "test"\n+\n+ def acceptable_child(self, child):\n+ return isinstance(child, TestParam) or isinstance(child, TestOutput)\n+\n+\n+class TestParam(XMLParam):\n+ name = "param"\n+\n+ def __init__(self, name, value=None, ftype=None, dbkey=None, **kwargs):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(TestParam, self).__init__(**params)\n+\n+\n+class TestOutput(XMLParam):\n+ name = "output"\n+\n+ def __init__(\n+ self,\n+ name=None,\n+ file=None,\n+ ftype=None,\n+ sort=None,\n+ value=None,\n+ md5=None,\n+ checksum=None,\n+ compare=None,\n+ lines_diff=None,\n+ delta=None,\n+ **kwargs\n+ ):\n+ params = Util.clean_kwargs(locals().copy())\n+ super(TestOutput, self).__init__(**params)\n+\n+\n+class Citations(XMLParam):\n+ name = "citations"\n+\n+ def acceptable_child(self, child):\n+ return issubclass(type(child), Citation)\n+\n+ def has_citation(self, type, value):\n+ """\n+ Check the presence of a given citation.\n+\n+ :type type: STRING\n+ :type value: STRING\n+ """\n+ for citation in self.children:\n+ if citation.node.attrib["type"] == type and citation.node.text == value:\n+ return True\n+ return False\n+\n+\n+class Citation(XMLParam):\n+ name = "citation"\n+\n+ def __init__(self, type, value):\n+ passed_kwargs = {}\n+ passed_kwargs["type"] = type\n+ super(Citation, self).__init__(**passed_kwargs)\n+ self.node.text = str(value)\n' |
b |
diff -r 7176af503cdd -r 6ce360759c28 toolfactory/galaxyxml/tool/parameters/__pycache__/__init__.cpython-36.pyc |
b |
Binary file toolfactory/galaxyxml/tool/parameters/__pycache__/__init__.cpython-36.pyc has changed |