Mercurial > repos > fubar > tool_factory_2
changeset 99:d4d88d393285 draft
Uploaded
author | fubar |
---|---|
date | Mon, 23 Nov 2020 02:22:01 +0000 |
parents | 67628c7dc9f3 |
children | c749364c2283 |
files | toolfactory/galaxy-tool-test 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 toolfactory/rgToolFactory2.py toolfactory/rgToolFactory2.xml toolfactory/testtf.sh toolfactory/whoosh.sh |
diffstat | 12 files changed, 583 insertions(+), 1820 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/galaxy-tool-test Mon Nov 23 02:22:01 2020 +0000 @@ -0,0 +1,457 @@ +#!/usr/bin/env python + +import argparse +import datetime as dt +import json +import logging +import os +import sys +import tempfile +from collections import namedtuple +from concurrent.futures import thread, ThreadPoolExecutor + +import yaml + +from galaxy.tool_util.verify.interactor import ( + DictClientTestConfig, + GalaxyInteractorApi, + verify_tool, +) + +DESCRIPTION = """Script to quickly run a tool test against a running Galaxy instance.""" +DEFAULT_SUITE_NAME = "Galaxy Tool Tests" +ALL_TESTS = -1 +ALL_TOOLS = "*" +ALL_VERSION = "*" +LATEST_VERSION = None + + +TestReference = namedtuple("TestReference", ["tool_id", "tool_version", "test_index"]) +TestException = namedtuple("TestException", ["tool_id", "exception", "was_recorded"]) + + +class Results: + + def __init__(self, default_suitename, test_json, append=False): + self.test_json = test_json or "-" + test_results = [] + test_exceptions = [] + suitename = default_suitename + if append: + assert test_json != "-" + with open(test_json) as f: + previous_results = json.load(f) + test_results = previous_results["tests"] + if "suitename" in previous_results: + suitename = previous_results["suitename"] + self.test_results = test_results + self.test_exceptions = test_exceptions + self.suitename = suitename + + def register_result(self, result): + self.test_results.append(result) + + def register_exception(self, test_exception): + self.test_exceptions.append(test_exception) + + def already_successful(self, test_reference): + test_id = _test_id_for_reference(test_reference) + for test_result in self.test_results: + if test_result.get('id') != test_id: + continue + + has_data = test_result.get('has_data', False) + if has_data: + test_data = test_result.get("data", {}) + if 'status' in test_data and test_data['status'] == 'success': + return True + + return False + + def write(self): + tests = sorted(self.test_results, key=lambda el: el['id']) + n_passed, n_failures, n_skips = 0, 0, 0 + n_errors = len([e for e in self.test_exceptions if not e.was_recorded]) + for test in tests: + has_data = test.get('has_data', False) + if has_data: + test_data = test.get("data", {}) + if 'status' not in test_data: + raise Exception(f"Test result data {test_data} doesn't contain a status key.") + status = test_data['status'] + if status == "success": + n_passed += 1 + elif status == "error": + n_errors += 1 + elif status == "skip": + n_skips += 1 + elif status == "failure": + n_failures += 1 + report_obj = { + 'version': '0.1', + 'suitename': self.suitename, + 'results': { + 'total': n_passed + n_failures + n_skips + n_errors, + 'errors': n_errors, + 'failures': n_failures, + 'skips': n_skips, + }, + 'tests': tests, + } + if self.test_json == "-": + print(json.dumps(report_obj)) + else: + with open(self.test_json, "w") as f: + json.dump(report_obj, f) + + def info_message(self): + messages = [] + passed_tests = self._tests_with_status('success') + messages.append("Passed tool tests ({}): {}".format( + len(passed_tests), + [t["id"] for t in passed_tests] + )) + failed_tests = self._tests_with_status('failure') + messages.append("Failed tool tests ({}): {}".format( + len(failed_tests), + [t["id"] for t in failed_tests] + )) + skiped_tests = self._tests_with_status('skip') + messages.append("Skipped tool tests ({}): {}".format( + len(skiped_tests), + [t["id"] for t in skiped_tests] + )) + errored_tests = self._tests_with_status('error') + messages.append("Errored tool tests ({}): {}".format( + len(errored_tests), + [t["id"] for t in errored_tests] + )) + return "\n".join(messages) + + @property + def success_count(self): + self._tests_with_status('success') + + @property + def skip_count(self): + self._tests_with_status('skip') + + @property + def error_count(self): + return self._tests_with_status('error') + len(self.test_exceptions) + + @property + def failure_count(self): + return self._tests_with_status('failure') + + def _tests_with_status(self, status): + return [t for t in self.test_results if t.get("data", {}).get("status") == status] + + +def test_tools( + galaxy_interactor, + test_references, + results, + log=None, + parallel_tests=1, + history_per_test_case=False, + no_history_cleanup=False, + retries=0, + verify_kwds=None, +): + """Run through tool tests and write report. + + Refactor this into Galaxy in 21.01. + """ + verify_kwds = (verify_kwds or {}).copy() + tool_test_start = dt.datetime.now() + history_created = False + if history_per_test_case: + test_history = None + else: + history_created = True + test_history = galaxy_interactor.new_history(history_name=f"History for {results.suitename}") + verify_kwds.update({ + "no_history_cleanup": no_history_cleanup, + "test_history": test_history, + }) + with ThreadPoolExecutor(max_workers=parallel_tests) as executor: + try: + for test_reference in test_references: + _test_tool( + executor=executor, + test_reference=test_reference, + results=results, + galaxy_interactor=galaxy_interactor, + log=log, + retries=retries, + verify_kwds=verify_kwds, + ) + finally: + # Always write report, even if test was cancelled. + try: + executor.shutdown(wait=True) + except KeyboardInterrupt: + executor._threads.clear() + thread._threads_queues.clear() + results.write() + if log: + log.info("Report written to '%s'", os.path.abspath(results.test_json)) + log.info(results.info_message()) + log.info("Total tool test time: {}".format(dt.datetime.now() - tool_test_start)) + if history_created and not no_history_cleanup: + galaxy_interactor.delete_history(test_history) + + +def _test_id_for_reference(test_reference): + tool_id = test_reference.tool_id + tool_version = test_reference.tool_version + test_index = test_reference.test_index + + if tool_version and tool_id.endswith("/" + tool_version): + tool_id = tool_id[:-len("/" + tool_version)] + + label_base = tool_id + if tool_version: + label_base += "/" + str(tool_version) + + test_id = label_base + "-" + str(test_index) + return test_id + + +def _test_tool( + executor, + test_reference, + results, + galaxy_interactor, + log, + retries, + verify_kwds, +): + tool_id = test_reference.tool_id + tool_version = test_reference.tool_version + test_index = test_reference.test_index + # If given a tool_id with a version suffix, strip it off so we can treat tool_version + # correctly at least in client_test_config. + if tool_version and tool_id.endswith("/" + tool_version): + tool_id = tool_id[:-len("/" + tool_version)] + + test_id = _test_id_for_reference(test_reference) + + def run_test(): + run_retries = retries + job_data = None + job_exception = None + + def register(job_data_): + nonlocal job_data + job_data = job_data_ + + try: + while run_retries >= 0: + job_exception = None + try: + if log: + log.info("Executing test '%s'", test_id) + verify_tool( + tool_id, galaxy_interactor, test_index=test_index, tool_version=tool_version, + register_job_data=register, **verify_kwds + ) + if log: + log.info("Test '%s' passed", test_id) + break + except Exception as e: + if log: + log.warning("Test '%s' failed", test_id, exc_info=True) + + job_exception = e + run_retries -= 1 + finally: + if job_data is not None: + results.register_result({ + "id": test_id, + "has_data": True, + "data": job_data, + }) + if job_exception is not None: + was_recorded = job_data is not None + test_exception = TestException(tool_id, job_exception, was_recorded) + results.register_exception(test_exception) + + executor.submit(run_test) + + +def build_case_references( + galaxy_interactor, + tool_id=ALL_TOOLS, + tool_version=LATEST_VERSION, + test_index=ALL_TESTS, + page_size=0, + page_number=0, + check_against=None, + log=None, +): + test_references = [] + if tool_id == ALL_TOOLS: + tests_summary = galaxy_interactor.get_tests_summary() + for tool_id, tool_versions_dict in tests_summary.items(): + for tool_version, summary in tool_versions_dict.items(): + for test_index in range(summary["count"]): + test_reference = TestReference(tool_id, tool_version, test_index) + test_references.append(test_reference) + else: + assert tool_id + tool_test_dicts = galaxy_interactor.get_tool_tests(tool_id, tool_version=tool_version) or {} + for i, tool_test_dict in enumerate(tool_test_dicts): + this_tool_version = tool_test_dict.get("tool_version", tool_version) + this_test_index = i + if test_index == ALL_TESTS or i == test_index: + test_reference = TestReference(tool_id, this_tool_version, this_test_index) + test_references.append(test_reference) + + if check_against: + filtered_test_references = [] + for test_reference in test_references: + if check_against.already_successful(test_reference): + if log is not None: + log.debug(f"Found successful test for {test_reference}, skipping") + continue + filtered_test_references.append(test_reference) + log.info(f"Skipping {len(test_references)-len(filtered_test_references)} out of {len(test_references)} tests.") + test_references = filtered_test_references + + if page_size > 0: + slice_start = page_size * page_number + slice_end = page_size * (page_number + 1) + test_references = test_references[slice_start:slice_end] + + return test_references + + +def main(argv=None): + if argv is None: + argv = sys.argv[1:] + + args = _arg_parser().parse_args(argv) + log = setup_global_logger(__name__, verbose=args.verbose) + client_test_config_path = args.client_test_config + if client_test_config_path is not None: + log.debug(f"Reading client config path {client_test_config_path}") + with open(client_test_config_path) as f: + client_test_config = yaml.full_load(f) + else: + client_test_config = {} + + def get_option(key): + arg_val = getattr(args, key, None) + if arg_val is None and key in client_test_config: + val = client_test_config.get(key) + else: + val = arg_val + return val + + output_json_path = get_option("output_json") + galaxy_interactor_kwds = { + "galaxy_url": get_option("galaxy_url"), + "master_api_key": get_option("admin_key"), + "api_key": get_option("key"), + "keep_outputs_dir": args.output, + "download_attempts": get_option("download_attempts"), + "download_sleep": get_option("download_sleep"), + } + tool_id = args.tool_id + tool_version = args.tool_version + tools_client_test_config = DictClientTestConfig(client_test_config.get("tools")) + verbose = args.verbose + + galaxy_interactor = GalaxyInteractorApi(**galaxy_interactor_kwds) + results = Results(args.suite_name, output_json_path, append=args.append) + check_against = None if not args.skip_successful else results + test_references = build_case_references( + galaxy_interactor, + tool_id=tool_id, + tool_version=tool_version, + test_index=args.test_index, + page_size=args.page_size, + page_number=args.page_number, + check_against=check_against, + log=log, + ) + log.debug(f"Built {len(test_references)} test references to executed.") + verify_kwds = dict( + client_test_config=tools_client_test_config, + force_path_paste=args.force_path_paste, + skip_with_reference_data=not args.with_reference_data, + quiet=not verbose, + ) + test_tools( + galaxy_interactor, + test_references, + results, + log=log, + parallel_tests=args.parallel_tests, + history_per_test_case=args.history_per_test_case, + no_history_cleanup=args.no_history_cleanup, + verify_kwds=verify_kwds, + ) + exceptions = results.test_exceptions + if exceptions: + exception = exceptions[0] + if hasattr(exception, "exception"): + exception = exception.exception + raise exception + + +def setup_global_logger(name, log_file=None, verbose=False): + formatter = logging.Formatter('%(asctime)s %(levelname)-5s - %(message)s') + console = logging.StreamHandler() + console.setFormatter(formatter) + + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG if verbose else logging.INFO) + logger.addHandler(console) + + if not log_file: + # delete = false is chosen here because it is always nice to have a log file + # ready if you need to debug. Not having the "if only I had set a log file" + # moment after the fact. + temp = tempfile.NamedTemporaryFile(prefix="ephemeris_", delete=False) + log_file = temp.name + file_handler = logging.FileHandler(log_file) + logger.addHandler(file_handler) + logger.info(f"Storing log file in: {log_file}") + return logger + + +def _arg_parser(): + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument('-u', '--galaxy-url', default="http://localhost:8080", help='Galaxy URL') + parser.add_argument('-k', '--key', default=None, help='Galaxy User API Key') + parser.add_argument('-a', '--admin-key', default=None, help='Galaxy Admin API Key') + parser.add_argument('--force_path_paste', default=False, action="store_true", help='This requires Galaxy-side config option "allow_path_paste" enabled. Allows for fetching test data locally. Only for admins.') + parser.add_argument('-t', '--tool-id', default=ALL_TOOLS, help='Tool ID') + parser.add_argument('--tool-version', default=None, help='Tool Version (if tool id supplied). Defaults to just latest version, use * to test all versions') + parser.add_argument('-i', '--test-index', default=ALL_TESTS, type=int, help='Tool Test Index (starting at 0) - by default all tests will run.') + parser.add_argument('-o', '--output', default=None, help='directory to dump outputs to') + parser.add_argument('--append', default=False, action="store_true", help="Extend a test record json (created with --output-json) with additional tests.") + parser.add_argument('--skip-successful', default=False, action="store_true", help="When used with --append, skip previously run successful tests.") + parser.add_argument('-j', '--output-json', default=None, help='output metadata json') + parser.add_argument('--verbose', default=False, action="store_true", help="Verbose logging.") + parser.add_argument('-c', '--client-test-config', default=None, help="Test config YAML to help with client testing") + parser.add_argument('--suite-name', default=DEFAULT_SUITE_NAME, help="Suite name for tool test output") + parser.add_argument('--with-reference-data', dest="with_reference_data", default=False, action="store_true") + parser.add_argument('--skip-with-reference-data', dest="with_reference_data", action="store_false", help="Skip tests the Galaxy server believes use data tables or loc files.") + parser.add_argument('--history-per-suite', dest="history_per_test_case", default=False, action="store_false", help="Create new history per test suite (all tests in same history).") + parser.add_argument('--history-per-test-case', dest="history_per_test_case", action="store_true", help="Create new history per test case.") + parser.add_argument('--no-history-cleanup', default=False, action="store_true", help="Perserve histories created for testing.") + parser.add_argument('--parallel-tests', default=1, type=int, help="Parallel tests.") + parser.add_argument('--retries', default=0, type=int, help="Retry failed tests.") + parser.add_argument('--page-size', default=0, type=int, help="If positive, use pagination and just run one 'page' to tool tests.") + parser.add_argument('--page-number', default=0, type=int, help="If page size is used, run this 'page' of tests - starts with 0.") + parser.add_argument('--download-attempts', default=1, type=int, help="Galaxy may return a transient 500 status code for download if test results are written but not yet accessible.") + parser.add_argument('--download-sleep', default=1, type=int, help="If download attempts is greater than 1, the amount to sleep between download attempts.") + return parser + + +if __name__ == "__main__": + main()
--- a/toolfactory/galaxyxml/__init__.py Sun Nov 22 06:29:33 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -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
--- a/toolfactory/galaxyxml/tool/__init__.py Sun Nov 22 06:29:33 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -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()
--- a/toolfactory/galaxyxml/tool/import_xml.py Sun Nov 22 06:29:33 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,713 +0,0 @@ -import logging -import xml.etree.ElementTree as ET - -import galaxyxml.tool as gxt -import galaxyxml.tool.parameters as gxtp - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -class GalaxyXmlParser(object): - """ - Class to import content from an existing Galaxy XML wrapper. - """ - - def _init_tool(self, xml_root): - """ - Init tool from existing xml tool. - - :param xml_root: root of the galaxy xml file. - :type xml_root: :class:`xml.etree._Element` - """ - version_cmd = None - description = None - for child in xml_root: - if child.tag == "description": - description = child.text - elif child.tag == "command": - executable = child.text.split()[0] - command = child.text - elif child.tag == "version_command": - version_cmd = child.text - - tool = gxt.Tool( - xml_root.attrib["name"], - xml_root.attrib["id"], - xml_root.attrib.get("version", None), - description, - executable, - hidden=xml_root.attrib.get("hidden", False), - tool_type=xml_root.attrib.get("tool_type", None), - URL_method=xml_root.attrib.get("URL_method", None), - workflow_compatible=xml_root.attrib.get("workflow_compatible", True), - version_command=version_cmd, - ) - tool.command = command - return tool - - def _load_description(self, tool, desc_root): - """ - <description> is already loaded during initiation. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param desc_root: root of <description> tag. - :type desc_root: :class:`xml.etree._Element` - """ - logger.info("<description> is loaded during initiation of the object.") - - def _load_version_command(self, tool, vers_root): - """ - <version_command> is already loaded during initiation. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param vers_root: root of <version_command> tag. - :type vers_root: :class:`xml.etree._Element` - """ - logger.info("<version_command> is loaded during initiation of the object.") - - def _load_stdio(self, tool, stdio_root): - """ - So far, <stdio> is automatically generated by galaxyxml. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param desc_root: root of <stdio> tag. - :type desc_root: :class:`xml.etree._Element` - """ - logger.info("<stdio> is not loaded but automatically generated by galaxyxml.") - - def _load_command(self, tool, desc_root): - """ - <command> is already loaded during initiation. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param desc_root: root of <command> tag. - :type desc_root: :class:`xml.etree._Element` - """ - logger.info("<command> is loaded during initiation of the object.") - - def _load_help(self, tool, help_root): - """ - Load the content of the <help> into the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param requirements_root: root of <help> tag. - :type requirements_root: :class:`xml.etree._Element` - """ - tool.help = help_root.text - - def _load_requirements(self, tool, requirements_root): - """ - Add <requirements> to the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param requirements_root: root of <requirements> tag. - :type requirements_root: :class:`xml.etree._Element` - """ - tool.requirements = gxtp.Requirements() - for req in requirements_root: - req_type = req.attrib["type"] - value = req.text - if req.tag == "requirement": - version = req.attrib.get("version", None) - tool.requirements.append(gxtp.Requirement(req_type, value, version=version)) - elif req.tag == "container": - tool.requirements.append(gxtp.Container(req_type, value)) - else: - logger.warning(req.tag + " is not a valid tag for requirements child") - - def _load_edam_topics(self, tool, topics_root): - """ - Add <edam_topics> to the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param topics_root: root of <edam_topics> tag. - :type topics_root: :class:`xml.etree._Element` - """ - tool.edam_topics = gxtp.EdamTopics() - for edam_topic in topics_root: - tool.edam_topics.append(gxtp.EdamTopic(edam_topic.text)) - - def _load_edam_operations(self, tool, operations_root): - """ - Add <edam_operations> to the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param operations_root: root of <edam_operations> tag. - :type operations_root: :class:`xml.etree._Element` - """ - tool.edam_operations = gxtp.EdamOperations() - for edam_op in operations_root: - tool.edam_operations.append(gxtp.EdamOperation(edam_op.text)) - - def _load_configfiles(self, tool, configfiles_root): - """ - Add <configfiles> to the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param configfiles_root: root of <configfiles> tag. - :type configfiles_root: :class:`xml.etree._Element` - """ - tool.configfiles = gxtp.Configfiles() - for conf in configfiles_root: - name = conf.attrib["name"] - value = conf.text - tool.configfiles.append(gxtp.Configfile(name, value)) - - def _load_citations(self, tool, citations_root): - """ - Add <citations> to the tool. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param citations_root: root of <citations> tag. - :type citations_root: :class:`xml.etree._Element` - """ - tool.citations = gxtp.Citations() - for cit in citations_root: - cit_type = cit.attrib["type"] - value = cit.text - tool.citations.append(gxtp.Citation(cit_type, value)) - - def _load_inputs(self, tool, inputs_root): - """ - Add <inputs> to the tool using the :class:`galaxyxml.tool.import_xml.InputsParser` object. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param inputs_root: root of <inputs> tag. - :type inputs_root: :class:`xml.etree._Element` - """ - tool.inputs = gxtp.Inputs() - inp_parser = InputsParser() - inp_parser.load_inputs(tool.inputs, inputs_root) - - def _load_outputs(self, tool, outputs_root): - """ - Add <outputs> to the tool using the :class:`galaxyxml.tool.import_xml.OutputsParser` object. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param outputs_root: root of <outputs> tag. - :type outputs_root: :class:`xml.etree._Element` - """ - tool.outputs = gxtp.Outputs() - out_parser = OutputsParser() - out_parser.load_outputs(tool.outputs, outputs_root) - - def _load_tests(self, tool, tests_root): - """ - Add <tests> to the tool using the :class:`galaxyxml.tool.import_xml.TestsParser` object. - - :param tool: Tool object from galaxyxml. - :type tool: :class:`galaxyxml.tool.Tool` - :param tests_root: root of <tests> tag. - :type tests_root: :class:`xml.etree._Element` - """ - tool.tests = gxtp.Tests() - tests_parser = TestsParser() - tests_parser.load_tests(tool.tests, tests_root) - - def import_xml(self, xml_path): - """ - Load existing xml into the :class:`galaxyxml.tool.Tool` object. - - :param xml_path: Path of the XML to be loaded. - :type xml_path: STRING - :return: XML content in the galaxyxml model. - :rtype: :class:`galaxyxml.tool.Tool` - """ - xml_root = ET.parse(xml_path).getroot() - tool = self._init_tool(xml_root) - # Now we import each tag's field - for child in xml_root: - try: - getattr(self, "_load_{}".format(child.tag))(tool, child) - except AttributeError: - logger.warning(child.tag + " tag is not processed.") - return tool - - -class InputsParser(object): - """ - Class to parse content of the <inputs> tag from a Galaxy XML wrapper. - """ - - def _load_text_param(self, root, text_param): - """ - Add <param type="text" /> to the root. - - :param root: root to append the param to. - :param text_param: root of <param> tag. - :type text_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.TextParam( - text_param.attrib["name"], - optional=text_param.get("optional", None), - label=text_param.get("label", None), - help=text_param.get("help", None), - value=text_param.get("value", None), - ) - ) - - def _load_data_param(self, root, data_param): - """ - Add <param type="data" /> to the root. - - :param root: root to append the param to. - :param data_param: root of <param> tag. - :type data_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.DataParam( - data_param.attrib["name"], - optional=data_param.attrib.get("optional", None), - label=data_param.attrib.get("label", None), - help=data_param.attrib.get("help", None), - format=data_param.attrib.get("format", None), - multiple=data_param.attrib.get("multiple", None), - ) - ) - - def _load_boolean_param(self, root, bool_param): - """ - Add <param type="boolean" /> to the root. - - :param root: root to append the param to. - :param bool_param: root of <param> tag. - :type bool_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.BooleanParam( - bool_param.attrib["name"], - optional=bool_param.attrib.get("optional", None), - label=bool_param.attrib.get("label", None), - help=bool_param.attrib.get("help", None), - checked=bool_param.attrib.get("checked", False), - truevalue=bool_param.attrib.get("truevalue", None), - falsevalue=bool_param.attrib.get("falsevalue", None), - ) - ) - - def _load_integer_param(self, root, int_param): - """ - Add <param type="integer" /> to the root. - - :param root: root to append the param to. - :param int_param: root of <param> tag. - :type int_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.IntegerParam( - int_param.attrib["name"], - int_param.attrib.get("value", None), - optional=int_param.attrib.get("optional", None), - label=int_param.attrib.get("label", None), - help=int_param.attrib.get("help", None), - min=int_param.attrib.get("min", None), - max=int_param.attrib.get("max", None), - ) - ) - - def _load_float_param(self, root, float_param): - """ - Add <param type="float" /> to the root. - - :param root: root to append the param to. - :param float_param: root of <param> tag. - :type float_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.FloatParam( - float_param.attrib["name"], - float_param.attrib.get("value", None), - optional=float_param.attrib.get("optional", None), - label=float_param.attrib.get("label", None), - help=float_param.attrib.get("help", None), - min=float_param.attrib.get("min", None), - max=float_param.attrib.get("max", None), - ) - ) - - def _load_option_select(self, root, option): - """ - Add <option> to the root (usually <param type="select" />). - - :param root: root to append the param to. - :param option: root of <option> tag. - :type float_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.SelectOption( - option.attrib.get("value", None), option.text, selected=option.attrib.get("selected", False) - ) - ) - - def _load_column_options(self, root, column): - """ - Add <column> to the root (usually <options>). - - :param root: root to append the param to. - :param option: root of <column> tag. - :type float_param: :class:`xml.etree._Element` - """ - root.append(gxtp.Column(column.attrib["name"], column.attrib["index"])) - - def _load_filter_options(self, root, filter): - """ - Add <filter> to the root (usually <options>). - - :param root: root to append the param to. - :param option: root of <filter> tag. - :type float_param: :class:`xml.etree._Element` - """ - root.append( - gxtp.Filter( - filter.attrib["type"], - column=filter.attrib.get("column", None), - name=filter.attrib.get("name", None), - ref=filter.attrib.get("ref", None), - key=filter.attrib.get("key", None), - multiple=filter.attrib.get("multiple", None), - separator=filter.attrib.get("separator", None), - keep=filter.attrib.get("keep", None), - value=filter.attrib.get("value", None), - ref_attribute=filter.attrib.get("ref_attribute", None), - index=filter.attrib.get("index", None), - ) - ) - - def _load_options_select(self, root, options): - """ - Add <options> to the root (usually <param type="select" />). - - :param root: root to append the param to. - :param option: root of <options> tag. - :type float_param: :class:`xml.etree._Element` - """ - opts = gxtp.Options( - from_dataset=options.attrib.get("from_dataset", None), - from_file=options.attrib.get("from_file", None), - from_data_table=options.attrib.get("from_data_table", None), - from_parameter=options.attrib.get("from_parameter", None), - ) - # Deal with child nodes (usually filter and column) - for opt_child in options: - try: - getattr(self, "_load_{}_options".format(opt_child.tag))(opts, opt_child) - except AttributeError: - logger.warning(opt_child.tag + " tag is not processed for <options>.") - root.append(opts) - - def _load_select_param(self, root, sel_param): - """ - Add <param type="select" /> to the root. - - :param root: root to append the param to. - :param sel_param: root of <param> tag. - :type sel_param: :class:`xml.etree._Element` - """ - select_param = gxtp.SelectParam( - sel_param.attrib["name"], - optional=sel_param.attrib.get("optional", None), - label=sel_param.attrib.get("label", None), - help=sel_param.attrib.get("help", None), - data_ref=sel_param.attrib.get("data_ref", None), - display=sel_param.attrib.get("display", None), - multiple=sel_param.attrib.get("multiple", None), - ) - # Deal with child nodes (usually option and options) - for sel_child in sel_param: - try: - getattr(self, "_load_{}_select".format(sel_child.tag))(select_param, sel_child) - except AttributeError: - logger.warning(sel_child.tag + " tag is not processed for <param type='select'>.") - root.append(select_param) - - def _load_param(self, root, param_root): - """ - Method to select which type of <param> is being added to the root. - - :param root: root to attach param to. - :param param_root: root of <param> tag. - :type param_root: :class:`xml.etree._Element` - """ - param_type = param_root.attrib["type"] - try: - getattr(self, "_load_{}_param".format(param_type))(root, param_root) - except AttributeError: - logger.warning(param_type + " tag is not processed for <param>.") - - def _load_when(self, root, when_root): - """ - Add <when> to the root (usually <conditional>). - - :param root: root to append when to. - :param when_root: root of <when> tag. - :type when_root: :class:`xml.etree._Element` - """ - when = gxtp.When(when_root.attrib["value"]) - # Deal with child nodes - self.load_inputs(when, when_root) - root.append(when) - - def _load_conditional(self, root, conditional_root): - """ - Add <conditional> to the root. - - :param root: root to append conditional to. - :param conditional_root: root of <conditional> tag. - :type conditional_root: :class:`xml.etree._Element` - """ - value_ref_in_group = conditional_root.attrib.get("value_ref_in_group", None) - # Other optional parameters need to be added to conditional object - conditional = gxtp.Conditional( - conditional_root.attrib["name"], - value_from=conditional_root.attrib.get("value_from", None), - value_ref=conditional_root.attrib.get("value_ref", None), - value_ref_in_group=value_ref_in_group, - label=conditional_root.attrib.get("label", None), - ) - # Deal with child nodes - self.load_inputs(conditional, conditional_root) - root.append(conditional) - - def _load_section(self, root, section_root): - """ - Add <section> to the root. - - :param root: root to append conditional to. - :param section_root: root of <section> tag. - :type section_root: :class:`xml.etree._Element` - """ - section = gxtp.Section( - section_root.attrib["name"], - section_root.attrib["title"], - expanded=section_root.attrib.get("expanded", None), - help=section_root.attrib.get("help", None), - ) - # Deal with child nodes - self.load_inputs(section, section_root) - root.append(section) - - def _load_repeat(self, root, repeat_root): - """ - Add <repeat> to the root. - - :param root: root to append repeat to. - :param repeat_root: root of <repeat> tag. - :param repeat_root: :class:`xml.etree._Element` - """ - repeat = gxtp.Repeat( - repeat_root.attrib["name"], - repeat_root.attrib["title"], - min=repeat_root.attrib.get("min", None), - max=repeat_root.attrib.get("max", None), - default=repeat_root.attrib.get("default", None), - ) - # Deal with child nodes - self.load_inputs(repeat, repeat_root) - root.append(repeat) - - def load_inputs(self, root, inputs_root): - """ - Add <inputs.tag> to the root (it can be any tags with children such as - <inputs>, <repeat>, <section> ...) - - :param root: root to attach inputs to (either <inputs> or <when>). - :param inputs_root: root of <inputs> tag. - :type inputs_root: :class:`xml.etree._Element` - """ - for inp_child in inputs_root: - try: - getattr(self, "_load_{}".format(inp_child.tag))(root, inp_child) - except AttributeError: - logger.warning(inp_child.tag + " tag is not processed for <" + inputs_root.tag + "> tag.") - - -class OutputsParser(object): - """ - Class to parse content of the <outputs> tag from a Galaxy XML wrapper. - """ - - def _load_data(self, outputs_root, data_root): - """ - Add <data> to <outputs>. - - :param outputs_root: <outputs> root to append <data> to. - :param data_root: root of <data> tag. - :param data_root: :class:`xml.etree._Element` - """ - data = gxtp.OutputData( - data_root.attrib.get("name", None), - data_root.attrib.get("format", None), - format_source=data_root.attrib.get("format_source", None), - metadata_source=data_root.attrib.get("metadata_source", None), - label=data_root.attrib.get("label", None), - from_work_dir=data_root.attrib.get("from_work_dir", None), - hidden=data_root.attrib.get("hidden", False), - ) - # Deal with child nodes - for data_child in data_root: - try: - getattr(self, "_load_{}".format(data_child.tag))(data, data_child) - except AttributeError: - logger.warning(data_child.tag + " tag is not processed for <data>.") - outputs_root.append(data) - - def _load_change_format(self, root, chfmt_root): - """ - Add <change_format> to root (<data>). - - :param root: root to append <change_format> to. - :param chfm_root: root of <change_format> tag. - :param chfm_root: :class:`xml.etree._Element` - """ - change_format = gxtp.ChangeFormat() - for chfmt_child in chfmt_root: - change_format.append( - gxtp.ChangeFormatWhen( - chfmt_child.attrib["input"], chfmt_child.attrib["format"], chfmt_child.attrib["value"] - ) - ) - root.append(change_format) - - def _load_collection(self, outputs_root, coll_root): - """ - Add <collection> to <outputs>. - - :param outputs_root: <outputs> root to append <collection> to. - :param coll_root: root of <collection> tag. - :param coll_root: :class:`xml.etree._Element` - """ - collection = gxtp.OutputCollection( - coll_root.attrib["name"], - type=coll_root.attrib.get("type", None), - label=coll_root.attrib.get("label", None), - format_source=coll_root.attrib.get("format_source", None), - type_source=coll_root.attrib.get("type_source", None), - structured_like=coll_root.attrib.get("structured_like", None), - inherit_format=coll_root.attrib.get("inherit_format", None), - ) - # Deal with child nodes - for coll_child in coll_root: - try: - getattr(self, "_load_{}".format(coll_child.tag))(collection, coll_child) - except AttributeError: - logger.warning(coll_child.tag + " tag is not processed for <collection>.") - outputs_root.append(collection) - - def _load_discover_datasets(self, root, disc_root): - """ - Add <discover_datasets> to root (<collection>). - - :param root: root to append <collection> to. - :param disc_root: root of <discover_datasets> tag. - :param disc_root: :class:`xml.etree._Element` - """ - root.append( - gxtp.DiscoverDatasets( - disc_root.attrib["pattern"], - directory=disc_root.attrib.get("directory", None), - format=disc_root.attrib.get("format", None), - ext=disc_root.attrib.get("ext", None), - visible=disc_root.attrib.get("visible", None), - ) - ) - - def _load_filter(self, root, filter_root): - """ - Add <filter> to root (<collection> or <data>). - - :param root: root to append <collection> to. - :param coll_root: root of <filter> tag. - :param coll_root: :class:`xml.etree._Element` - """ - root.append(gxtp.OutputFilter(filter_root.text)) - - def load_outputs(self, root, outputs_root): - """ - Add <outputs> to the root. - - :param root: root to attach <outputs> to (<tool>). - :param tests_root: root of <outputs> tag. - :type tests_root: :class:`xml.etree._Element` - """ - for out_child in outputs_root: - try: - getattr(self, "_load_{}".format(out_child.tag))(root, out_child) - except AttributeError: - logger.warning(out_child.tag + " tag is not processed for <outputs>.") - - -class TestsParser(object): - """ - Class to parse content of the <tests> tag from a Galaxy XML wrapper. - """ - - def _load_param(self, test_root, param_root): - """ - Add <param> to the <test>. - - :param root: <test> root to append <param> to. - :param repeat_root: root of <param> tag. - :param repeat_root: :class:`xml.etree._Element` - """ - test_root.append( - gxtp.TestParam( - param_root.attrib["name"], - value=param_root.attrib.get("value", None), - ftype=param_root.attrib.get("ftype", None), - dbkey=param_root.attrib.get("dbkey", None), - ) - ) - - def _load_output(self, test_root, output_root): - """ - Add <output> to the <test>. - - :param root: <test> root to append <output> to. - :param repeat_root: root of <output> tag. - :param repeat_root: :class:`xml.etree._Element` - """ - test_root.append( - gxtp.TestOutput( - name=output_root.attrib.get("name", None), - file=output_root.attrib.get("file", None), - ftype=output_root.attrib.get("ftype", None), - sort=output_root.attrib.get("sort", None), - value=output_root.attrib.get("value", None), - md5=output_root.attrib.get("md5", None), - checksum=output_root.attrib.get("checksum", None), - compare=output_root.attrib.get("compare", None), - lines_diff=output_root.attrib.get("lines_diff", None), - delta=output_root.attrib.get("delta", None), - ) - ) - - def load_tests(self, root, tests_root): - """ - Add <tests> to the root. - - :param root: root to attach <tests> to (<tool>). - :param tests_root: root of <tests> tag. - :type tests_root: :class:`xml.etree._Element` - """ - for test_root in tests_root: - test = gxtp.Test() - for test_child in test_root: - try: - getattr(self, "_load_{}".format(test_child.tag))(test, test_child) - except AttributeError: - logger.warning(test_child.tag + " tag is not processed within <test>.") - root.append(test)
--- a/toolfactory/galaxyxml/tool/parameters/__init__.py Sun Nov 22 06:29:33 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,739 +0,0 @@ -from builtins import object -from builtins import str - -from galaxyxml import Util - -from lxml import etree - - - -class XMLParam(object): - name = "node" - - def __init__(self, *args, **kwargs): - # http://stackoverflow.com/a/12118700 - self.children = [] - kwargs = {k: v for k, v in list(kwargs.items()) if v is not None} - kwargs = Util.coerce(kwargs, kill_lists=True) - kwargs = Util.clean_kwargs(kwargs, final=True) - self.node = etree.Element(self.name, **kwargs) - - def append(self, sub_node): - if self.acceptable_child(sub_node): - # If one of ours, they aren't etree nodes, they're custom objects - if issubclass(type(sub_node), XMLParam): - self.node.append(sub_node.node) - self.children.append(sub_node) - else: - raise Exception( - "Child was unacceptable to parent (%s is not appropriate for %s)" % (type(self), type(sub_node)) - ) - else: - raise Exception( - "Child was unacceptable to parent (%s is not appropriate for %s)" % (type(self), type(sub_node)) - ) - - def validate(self): - # Very few need validation, but some nodes we may want to have - # validation routines on. Should only be called when DONE. - for child in self.children: - # If any child fails to validate return false. - if not child.validate(): - return False - return True - - def cli(self): - lines = [] - for child in self.children: - lines.append(child.command_line()) - # lines += child.command_line() - return "\n".join(lines) - - def command_line(self): - return None - - -class RequestParamTranslation(XMLParam): - name = "request_param_translation" - - def __init__(self, **kwargs): - self.node = etree.Element(self.name) - - def acceptable_child(self, child): - return isinstance(child, RequestParamTranslation) - - -class RequestParam(XMLParam): - name = "request_param" - - def __init__(self, galaxy_name, remote_name, missing, **kwargs): - # TODO: bulk copy locals into self.attr? - self.galaxy_name = galaxy_name - # http://stackoverflow.com/a/1408860 - params = Util.clean_kwargs(locals().copy()) - super(RequestParam, self).__init__(**params) - - def acceptable_child(self, child): - return isinstance(child, AppendParam) and self.galaxy_name == "URL" - - -class AppendParam(XMLParam): - name = "append_param" - - def __init__(self, separator="&", first_separator="?", join="=", **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(AppendParam, self).__init__(**params) - - def acceptable_child(self, child): - return isinstance(child, AppendParamValue) - - -class AppendParamValue(XMLParam): - name = "value" - - def __init__(self, name="_export", missing="1", **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(AppendParamValue, self).__init__(**params) - - def acceptable_child(self, child): - return False - - -class EdamOperations(XMLParam): - name = "edam_operations" - - def acceptable_child(self, child): - return issubclass(type(child), EdamOperation) - - def has_operation(self, edam_operation): - """ - Check the presence of a given edam_operation. - - :type edam_operation: STRING - """ - for operation in self.children: - if operation.node.text == edam_operation: - return True - return False - - -class EdamOperation(XMLParam): - name = "edam_operation" - - def __init__(self, value): - super(EdamOperation, self).__init__() - self.node.text = str(value) - - -class EdamTopics(XMLParam): - name = "edam_topics" - - def acceptable_child(self, child): - return issubclass(type(child), EdamTopic) - - def has_topic(self, edam_topic): - """ - Check the presence of a given edam_topic. - - :type edam_topic: STRING - """ - for topic in self.children: - if topic.node.text == edam_topic: - return True - return False - - -class EdamTopic(XMLParam): - name = "edam_topic" - - def __init__(self, value): - super(EdamTopic, self).__init__() - self.node.text = str(value) - - -class Requirements(XMLParam): - name = "requirements" - # This bodes to be an issue -__- - - def acceptable_child(self, child): - return issubclass(type(child), Requirement) or issubclass(type(child), Container) - - -class Requirement(XMLParam): - name = "requirement" - - def __init__(self, type, value, version=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - passed_kwargs = {} - passed_kwargs["version"] = params["version"] - passed_kwargs["type"] = params["type"] - super(Requirement, self).__init__(**passed_kwargs) - self.node.text = str(value) - - -class Container(XMLParam): - name = "container" - - def __init__(self, type, value, **kwargs): - params = Util.clean_kwargs(locals().copy()) - passed_kwargs = {} - passed_kwargs["type"] = params["type"] - super(Container, self).__init__(**passed_kwargs) - self.node.text = str(value) - - -class Configfiles(XMLParam): - name = "configfiles" - - def acceptable_child(self, child): - return issubclass(type(child), Configfile) or issubclass(type(child), ConfigfileDefaultInputs) - - -class Configfile(XMLParam): - name = "configfile" - - def __init__(self, name, text, **kwargs): - params = Util.clean_kwargs(locals().copy()) - passed_kwargs = {} - passed_kwargs["name"] = params["name"] - super(Configfile, self).__init__(**passed_kwargs) - self.node.text = etree.CDATA(str(text)) - - -class ConfigfileDefaultInputs(XMLParam): - name = "inputs" - - def __init__(self, name, **kwargs): - params = Util.clean_kwargs(locals().copy()) - passed_kwargs = {} - passed_kwargs["name"] = params["name"] - super(ConfigfileDefaultInputs, self).__init__(**passed_kwargs) - - -class Inputs(XMLParam): - name = "inputs" - # This bodes to be an issue -__- - - def __init__(self, action=None, check_value=None, method=None, target=None, nginx_upload=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(Inputs, self).__init__(**params) - - def acceptable_child(self, child): - return issubclass(type(child), InputParameter) - - -class InputParameter(XMLParam): - def __init__(self, name, **kwargs): - # TODO: look at - self.mako_identifier = name - # We use kwargs instead of the usual locals(), so manually copy the - # name to kwargs - if name is not None: - kwargs["name"] = name - - # Handle positional parameters - if "positional" in kwargs and kwargs["positional"]: - self.positional = True - else: - self.positional = False - - if "num_dashes" in kwargs: - self.num_dashes = kwargs["num_dashes"] - del kwargs["num_dashes"] - else: - self.num_dashes = 0 - - self.space_between_arg = " " - - # Not sure about this :( - # https://wiki.galaxyproject.org/Tools/BestPractices#Parameter_help - if "label" in kwargs: - # TODO: replace with positional attribute - if len(self.flag()) > 0: - if kwargs["label"] is None: - kwargs["label"] = "Author did not provide help for this parameter... " - if not self.positional: - kwargs["argument"] = self.flag() - - super(InputParameter, self).__init__(**kwargs) - - def command_line(self): - before = self.command_line_before() - cli = self.command_line_actual() - after = self.command_line_after() - - complete = [x for x in (before, cli, after) if x is not None] - return "\n".join(complete) - - def command_line_before(self): - try: - return self.command_line_before_override - except Exception: - return None - - def command_line_after(self): - try: - return self.command_line_after_override - except Exception: - return None - - def command_line_actual(self): - try: - return self.command_line_override - except Exception: - if self.positional: - return self.mako_name() - else: - return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name()) - - def mako_name(self): - # TODO: enhance logic to check up parents for things like - # repeat>condotion>param - return "$" + self.mako_identifier - - def flag(self): - flag = "-" * self.num_dashes - return flag + self.mako_identifier - - -class Section(InputParameter): - name = "section" - - def __init__(self, name, title, expanded=None, help=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(Section, self).__init__(**params) - - def acceptable_child(self, child): - return issubclass(type(child), InputParameter) - - -class Repeat(InputParameter): - name = "repeat" - - def __init__(self, name, title, min=None, max=None, default=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - # Allow overriding - self.command_line_before_override = "#for $i in $%s:" % name - self.command_line_after_override = "#end for" - # self.command_line_override - super(Repeat, self).__init__(**params) - - def acceptable_child(self, child): - return issubclass(type(child), InputParameter) - - def command_line_actual(self): - if hasattr(self, "command_line_override"): - return self.command_line_override - else: - return "%s" % self.mako_name() - - -class Conditional(InputParameter): - name = "conditional" - - def __init__(self, name, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(Conditional, self).__init__(**params) - - def acceptable_child(self, child): - return issubclass(type(child), InputParameter) and not isinstance(child, Conditional) - - def validate(self): - # Find a way to check if one of the kids is a WHEN - pass - - -class When(InputParameter): - name = "when" - - def __init__(self, value): - params = Util.clean_kwargs(locals().copy()) - super(When, self).__init__(None, **params) - - def acceptable_child(self, child): - return issubclass(type(child), InputParameter) - - -class Param(InputParameter): - name = "param" - - # This...isn't really valid as-is, and shouldn't be used. - def __init__(self, name, optional=None, label=None, help=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - params["type"] = self.type - super(Param, self).__init__(**params) - - if type(self) == Param: - raise Exception("Param class is not an actual parameter type, use a subclass of Param") - - def acceptable_child(self, child): - return issubclass(type(child, InputParameter) or isinstance(child), ValidatorParam) - - -class TextParam(Param): - type = "text" - - def __init__(self, name, optional=None, label=None, help=None, value=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(TextParam, self).__init__(**params) - - def command_line_actual(self): - try: - return self.command_line_override - except Exception: - if self.positional: - return self.mako_name() - else: - return f"{self.flag}{self.space_between_arg}'{self.mako_name()}'" - - -class _NumericParam(Param): - def __init__(self, name, value, optional=None, label=None, help=None, min=None, max=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(_NumericParam, self).__init__(**params) - - -class IntegerParam(_NumericParam): - type = "integer" - - -class FloatParam(_NumericParam): - type = "float" - - -class BooleanParam(Param): - type = "boolean" - - def __init__( - self, name, optional=None, label=None, help=None, checked=False, truevalue=None, falsevalue=None, **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - - super(BooleanParam, self).__init__(**params) - if truevalue is None: - # If truevalue and falsevalue are None, then we use "auto", the IUC - # recommended default. - # - # truevalue is set to the parameter's value, and falsevalue is not. - # - # Unfortunately, mako_identifier is set as a result of the super - # call, which we shouldn't call TWICE, so we'll just hack around this :( - # params['truevalue'] = '%s%s' % (self.) - self.node.attrib["truevalue"] = self.flag() - - if falsevalue is None: - self.node.attrib["falsevalue"] = "" - - def command_line_actual(self): - if hasattr(self, "command_line_override"): - return self.command_line_override - else: - return "%s" % self.mako_name() - - -class DataParam(Param): - type = "data" - - def __init__(self, name, optional=None, label=None, help=None, format=None, multiple=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(DataParam, self).__init__(**params) - - -class SelectParam(Param): - type = "select" - - def __init__( - self, - name, - optional=None, - label=None, - help=None, - data_ref=None, - display=None, - multiple=None, - options=None, - default=None, - **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - del params["options"] - del params["default"] - - super(SelectParam, self).__init__(**params) - - if options is not None and default is not None: - if default not in options: - raise Exception("Specified a default that isn't in options") - - if options: - for k, v in list(sorted(options.items())): - selected = k == default - self.append(SelectOption(k, v, selected=selected)) - - def acceptable_child(self, child): - return issubclass(type(child), SelectOption) or issubclass(type(child), Options) - - -class SelectOption(InputParameter): - name = "option" - - def __init__(self, value, text, selected=False, **kwargs): - params = Util.clean_kwargs(locals().copy()) - - passed_kwargs = {} - if selected: - passed_kwargs["selected"] = "true" - passed_kwargs["value"] = params["value"] - - super(SelectOption, self).__init__(None, **passed_kwargs) - self.node.text = str(text) - - -class Options(InputParameter): - name = "options" - - def __init__(self, from_dataset=None, from_file=None, from_data_table=None, from_parameter=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(Options, self).__init__(None, **params) - - def acceptable_child(self, child): - return issubclass(type(child), Column) or issubclass(type(child), Filter) - - -class Column(InputParameter): - name = "column" - - def __init__(self, name, index, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(Column, self).__init__(**params) - - -class Filter(InputParameter): - name = "filter" - - def __init__( - self, - type, - column=None, - name=None, - ref=None, - key=None, - multiple=None, - separator=None, - keep=None, - value=None, - ref_attribute=None, - index=None, - **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - super(Filter, self).__init__(**params) - - -class ValidatorParam(InputParameter): - name = "validator" - - def __init__( - self, - type, - message=None, - filename=None, - metadata_name=None, - metadata_column=None, - line_startswith=None, - min=None, - max=None, - **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - super(ValidatorParam, self).__init__(**params) - - -class Outputs(XMLParam): - name = "outputs" - - def acceptable_child(self, child): - return isinstance(child, OutputData) or isinstance(child, OutputCollection) - - -class OutputData(XMLParam): - """Copypasta of InputParameter, needs work - """ - - name = "data" - - def __init__( - self, - name, - format, - format_source=None, - metadata_source=None, - label=None, - from_work_dir=None, - hidden=False, - **kwargs - ): - # TODO: validate format_source&metadata_source against something in the - # XMLParam children tree. - self.mako_identifier = name - if "num_dashes" in kwargs: - self.num_dashes = kwargs["num_dashes"] - del kwargs["num_dashes"] - else: - self.num_dashes = 0 - self.space_between_arg = " " - params = Util.clean_kwargs(locals().copy()) - - super(OutputData, self).__init__(**params) - - def command_line(self): - if hasattr(self, "command_line_override"): - return self.command_line_override - else: - return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name()) - - def mako_name(self): - return "$" + self.mako_identifier - - def flag(self): - flag = "-" * self.num_dashes - return flag + self.mako_identifier - - def acceptable_child(self, child): - return isinstance(child, OutputFilter) or isinstance(child, ChangeFormat) or isinstance(child, DiscoverDatasets) - - -class OutputFilter(XMLParam): - name = "filter" - - def __init__(self, text, **kwargs): - params = Util.clean_kwargs(locals().copy()) - del params["text"] - super(OutputFilter, self).__init__(**params) - self.node.text = text - - def acceptable_child(self, child): - return False - - -class ChangeFormat(XMLParam): - name = "change_format" - - def __init__(self, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(ChangeFormat, self).__init__(**params) - - def acceptable_child(self, child): - return isinstance(child, ChangeFormatWhen) - - -class ChangeFormatWhen(XMLParam): - name = "when" - - def __init__(self, input, format, value, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(ChangeFormatWhen, self).__init__(**params) - - def acceptable_child(self, child): - return False - - -class OutputCollection(XMLParam): - name = "collection" - - def __init__( - self, - name, - type=None, - label=None, - format_source=None, - type_source=None, - structured_like=None, - inherit_format=None, - **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - super(OutputCollection, self).__init__(**params) - - def acceptable_child(self, child): - return isinstance(child, OutputData) or isinstance(child, OutputFilter) or isinstance(child, DiscoverDatasets) - - -class DiscoverDatasets(XMLParam): - name = "discover_datasets" - - def __init__(self, pattern, directory=None, format=None, ext=None, visible=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(DiscoverDatasets, self).__init__(**params) - - -class Tests(XMLParam): - name = "tests" - - def acceptable_child(self, child): - return issubclass(type(child), Test) - - -class Test(XMLParam): - name = "test" - - def acceptable_child(self, child): - return isinstance(child, TestParam) or isinstance(child, TestOutput) - - -class TestParam(XMLParam): - name = "param" - - def __init__(self, name, value=None, ftype=None, dbkey=None, **kwargs): - params = Util.clean_kwargs(locals().copy()) - super(TestParam, self).__init__(**params) - - -class TestOutput(XMLParam): - name = "output" - - def __init__( - self, - name=None, - file=None, - ftype=None, - sort=None, - value=None, - md5=None, - checksum=None, - compare=None, - lines_diff=None, - delta=None, - **kwargs - ): - params = Util.clean_kwargs(locals().copy()) - super(TestOutput, self).__init__(**params) - - -class Citations(XMLParam): - name = "citations" - - def acceptable_child(self, child): - return issubclass(type(child), Citation) - - def has_citation(self, type, value): - """ - Check the presence of a given citation. - - :type type: STRING - :type value: STRING - """ - for citation in self.children: - if citation.node.attrib["type"] == type and citation.node.text == value: - return True - return False - - -class Citation(XMLParam): - name = "citation" - - def __init__(self, type, value): - passed_kwargs = {} - passed_kwargs["type"] = type - super(Citation, self).__init__(**passed_kwargs) - self.node.text = str(value)
--- a/toolfactory/rgToolFactory2.py Sun Nov 22 06:29:33 2020 +0000 +++ b/toolfactory/rgToolFactory2.py Mon Nov 23 02:22:01 2020 +0000 @@ -23,6 +23,7 @@ # well well. sh run_tests.sh --id rgtf2 --report_file tool_tests_tool_conf.html functional.test_toolbox # does the needful. Use GALAXY_TEST_SAVE /foo to save outputs - only the tar.gz - not the rest sadly # GALAXY_TEST_NO_CLEANUP GALAXY_TEST_TMP_DIR=wherever +# planemo test --engine docker_galaxy --test_data ./test-data/ --docker_extra_volume ./test-data rgToolFactory2.xml import argparse import datetime @@ -670,6 +671,93 @@ logging.debug("run done") return retval + + def gal_tool_test(self): + """ + This handy script writes test outputs even if they don't exist + galaxy-tool-test [-h] [-u GALAXY_URL] [-k KEY] [-a ADMIN_KEY] [--force_path_paste] [-t TOOL_ID] [--tool-version TOOL_VERSION] + [-i TEST_INDEX] [-o OUTPUT] [--append] [-j OUTPUT_JSON] [--verbose] [-c CLIENT_TEST_CONFIG] + galaxy-tool-test -u http://localhost:8080 -a 3c9afe09f1b7892449d266109639c104 -o /tmp/foo -t hello -j /tmp/foo/hello.json --verbose + handy - just leaves outputs in -o + """ + if os.path.exists(self.tlog): + tout = open(self.tlog, "a") + else: + tout = open(self.tlog, "w") + testouts = tempfile.mkdtemp(suffix=None, prefix="tftemp") + dummy, tfile = tempfile.mkstemp() + cll = [ + os.path.join(self.args.tool_dir,"galaxy-tool-test"), + "-u", + self.args.galaxy_url, + "-k", + self.args.galaxy_api_key, + "-t", + self.args.tool_name, + "-o", + testouts, + ] + subp = subprocess.run( + cll, shell=False, stderr=dummy, stdout=dummy + ) + outfiles = [] + for p in self.outfiles: + oname = p[ONAMEPOS] + outfiles.append(oname) + with os.scandir(testouts) as outs: + for entry in outs: + if not entry.is_file(): + continue + dest = os.path.join(self.tooloutdir, entry.name) + src = os.path.join(testouts, entry.name) + shutil.copyfile(src, dest) + dest = os.path.join(self.testdir, entry.name) + src = os.path.join(testouts, entry.name) + shutil.copyfile(src, dest) + dest = os.path.join(self.repdir,f"{entry.name}_sample") + tout.write(f"## found and moved output {entry.name} to {dest}\n") + tout.close() + shutil.rmtree(testouts) + return subp.returncode + + def gal_test(self): + """ + Uses the built in galaxy tool tester run_test.sh + + export GALAXY_TEST_SAVE="./foo" && export GALAXY_TEST_NO_CLEANUP="1" \ + && export GALAXY_TEST_TMP_DIR=./foo && sh run_tests.sh --id rgtf2 --report_file tool_tests_tool_conf.html functional.test_toolbox + + """ + testdir = tempfile.mkdtemp(suffix=None, prefix="tftemp") + tool_test_rep = f"{self.tool_name}_galaxy_test_report_html.html" + if os.path.exists(self.tlog): + tout = open(self.tlog, "a") + else: + tout = open(self.tlog, "w") + + ourenv = os.environ + ourenv["GALAXY_TEST_SAVE"] = testdir + ourenv["GALAXY_TEST_NO_CLEANUP"] = "1" + ourenv["GALAXY_TEST_TMP_DIR"] = testdir + + cll = [ + "sh", f"{self.args.galaxy_root}/run_tests.sh", "--id", self.args.tool_name, + "--report_file", os.path.join(testdir,tool_test_rep), "functional.test_toolbox", + ] + subp = subprocess.run( + cll, env = ourenv, + shell=False, cwd=self.args.galaxy_root, stderr=tout, stdout=tout + ) + src = os.path.join(testdir, tool_test_rep) + if os.path.isfile(src): + dest = os.path.join(self.repdir, tool_test_rep) + shutil.copyfile(src, dest) + else: + tout.write(f"### {src} not found\n") + tout.close() + return subp.returncode + + def shedLoad(self): """ {'deleted': False, @@ -753,7 +841,7 @@ tout.close() return subp.returncode - def planemo_shedload(self): + def planemo_shedLoad(self): """ planemo shed_create --shed_target testtoolshed planemo shed_init --name=<name> @@ -999,74 +1087,6 @@ return subp.returncode - def gal_test(self, genoutputs=True): - """ -export GALAXY_TEST_SAVE="./foo" && export GALAXY_TEST_NO_CLEANUP="1" \ -&& export GALAXY_TEST_TMP_DIR=./foo && sh run_tests.sh --id rgtf2 --report_file tool_tests_tool_conf.html functional.test_toolbox - - """ - tool_test_rep_path = os.path.join(self.repdir,f"{self.tool_name}_galaxy_test_report_html.html") - if os.path.exists(self.tlog): - tout = open(self.tlog, "a") - else: - tout = open(self.tlog, "w") - testdir = '/tmp/testdir' - ourenv = os.environ - ourenv["GALAXY_TEST_SAVE"] = os.abspath(testdir) - ourenv["GALAXY_TEST_NO_CLEANUP"] = "1" - ourenv["GALAXY_TEST_TMP_DIR"] = os.abspath(testdir) - if genoutputs: - - if not os.path.exists(testdir): - os.mkdir(testdir) - dummy = open('failedtest.log','w') - cll = [ - "sh", f"{self.args.galaxy_root}/run_tests.sh", "--id", self.args.tool_name, - "--report_file", tool_test_rep_path, "functional.test_toolbox", - ] - subp = subprocess.run( - cll, - shell=False, - env = ourenv, - cwd=self.testdir, - stderr=dummy, - stdout=dummy, - ) - dummy.close() - # if all went well, tgz is in ./test and down a nest of tmp directories lurk the output files - outfiles = [] - for p in self.outfiles: - oname = p[ONAMEPOS] - outfiles.append(oname) - paths = [] - for root, dirs, files in os.walk(testdir): - for f in files: - tout.write(f"{root} {f}\n") - if f in outfiles: - paths.append([root,f]) - tout.write(f"## found output {f}") - - if len(paths) != len(self.outfiles): - tout.write(f"## problem - outfiles not all found") - for path in paths: - fname = path[1] - src = os.path.join(path[0],fname) - dest = os.path.join(self.testdir,f"{fname}_sample") - shutil.copyfile(src,dest) - else: - cll = [ - "sh", f"{self.args.galaxy_root}/run_tests.sh", "--id", self.args.tool_name, - "--report_file", tool_test_rep_path, "functional.test_toolbox", - ] - subp = subprocess.run( - cll, env = ourenv, - shell=False, cwd=self.testdir, stderr=tout, stdout=tout - ) - tout.close() - return subp.returncode - - - def writeShedyml(self): """for planemo""" yuser = self.args.user_email.split("@")[0] @@ -1112,57 +1132,23 @@ for p in self.outfiles: oname = p[ONAMEPOS] - src = os.path.join(self.testdir,oname) - if os.path.isfile(src): - dest = os.path.join(self.testdir, "%s_sample" % oname) - shutil.copyfile(src, dest) - dest = os.path.join(self.repdir, "%s.sample" % (oname)) - shutil.copyfile(src, dest) - else: - print( - "### problem - output file %s not found in tooloutdir %s" - % (src, self.tooloutdir) - ) + tdest = os.path.join(self.testdir, "%s_sample" % oname) + if not os.path.isfile(tdest): + src = os.path.join(self.testdir,oname) + if os.path.isfile(src): + shutil.copyfile(src, tdest) + dest = os.path.join(self.repdir, "%s.sample" % (oname)) + shutil.copyfile(src, dest) + else: + print( + "### problem - output file %s not found in testdir %s" + % (tdest, self.testdir) + ) tf = tarfile.open(self.newtarpath, "w:gz") tf.add(name=self.tooloutdir, arcname=self.tool_name, filter=exclude_function) tf.close() shutil.copyfile(self.newtarpath, self.args.new_tool) - def fakeToolTar(self): - """move fake outputs into test-data and prepare the tarball""" - excludeme = "tool_test_output" - - def exclude_function(tarinfo): - filename = tarinfo.name - return ( - None - if filename.startswith(excludeme) - else tarinfo - ) - - for p in self.outfiles: - oname = p[ONAMEPOS] - src = os.path.join(self.testdir,oname) - if os.path.isfile(src): - dest = os.path.join(self.testdir, "%s_sample" % oname) - shutil.copyfile(src, dest) - dest = os.path.join(self.repdir, "%s.sample" % (oname)) - shutil.copyfile(src, dest) - else: - with open(src,'w') as fayk: - fayk.write('fake!\n') - dest = os.path.join(self.testdir, "%s_sample" % oname) - shutil.copyfile(src, dest) - print( - "### problem - output file %s not found in tooloutdir %s so faked" - % (src, self.tooloutdir) - ) - tf = tarfile.open(self.newtarpath, "w:gz") - tf.add(name=self.tooloutdir, arcname=self.tool_name, filter=exclude_function) - tf.close() - shutil.copyfile(self.newtarpath, self.args.new_tool) - - def moveRunOutputs(self): """need to move planemo or run outputs into toolfactory collection""" with os.scandir(self.tooloutdir) as outs: @@ -1254,12 +1240,16 @@ r.moveRunOutputs() r.makeToolTar() else: - r.fakeToolTar() + r.makeToolTar() + #r.planemo_shedLoad() r.shedLoad() r.eph_galaxy_load() - retcode = r.gal_test(genoutputs=True) # this fails + retcode = r.gal_tool_test() # writes outputs r.makeToolTar() - retcode = r.gal_test(genoutputs=False) + #r.planemo_shedLoad() + r.shedLoad() + r.eph_galaxy_load() + retcode = r.gal_test() r.moveRunOutputs() r.makeToolTar() print(f"second galaxy_test returned {retcode}")
--- a/toolfactory/rgToolFactory2.xml Sun Nov 22 06:29:33 2020 +0000 +++ b/toolfactory/rgToolFactory2.xml Mon Nov 23 02:22:01 2020 +0000 @@ -135,8 +135,6 @@ <requirement type="package" version="0.4.11">galaxyxml</requirement> <requirement type="package" version="0.14.0">bioblend</requirement> <requirement type="package" version="0.10.6">ephemeris</requirement> - <requirement type="package" version="0.72.0">planemo</requirement> - <requirement type="package" version="4.3.1">docker-py</requirement> </requirements> <command ><![CDATA[
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/testtf.sh Mon Nov 23 02:22:01 2020 +0000 @@ -0,0 +1,2 @@ +planemo test --no_cleanup --no_dependency_resolution --skip_venv --galaxy_root ~/galaxy ~/galaxy/tools/tool_makers/toolfactory &>foo +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolfactory/whoosh.sh Mon Nov 23 02:22:01 2020 +0000 @@ -0,0 +1,16 @@ +# using the galaxy venv this seems to work +. /galaxy_venv/bin/activate +python scripts/tool_shed/build_ts_whoosh_index.py -c config/tool_shed.yml --config-section tool_shed +# eeesh. /etc/galaxy is where the actual galaxy.yml lives - despite other configs being where they might be expected +# fix tool_shed.yml to 0.0.0.0 and admin email +# add tgz to datatypes :( +# need to source a venv in /export/tool_deps/toolfactorydeps/0.01/fubar/toolfactorydeps/9e9428fe9134/env.sh +# as nothing was done by the setup_virtualenv step apparently. +# gcc and friends for planemo pip installation +# File "/galaxy-central/lib/galaxy/tool_util/verify/interactor.py", line 595, in <listcomp> +# test_user = [user for user in all_users if user["email"] == email][0] +# add local to tool_sheds_conf.xml +# <tool_sheds> +# <tool_shed name="local" url="http://localhost:9009"/> +# mercurial > 5.5!! +