From c32e05ee788e99e0631575dd5acf11950032f329 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Tue, 29 Nov 2022 17:58:42 -0500 Subject: [PATCH] Type more of tool test code. --- lib/galaxy/tool_util/parser/interface.py | 29 +++++++++++++++++++++++- lib/galaxy/tool_util/parser/xml.py | 20 ++++++++++------ lib/galaxy/tool_util/parser/yaml.py | 22 ++++++++++-------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/lib/galaxy/tool_util/parser/interface.py b/lib/galaxy/tool_util/parser/interface.py index aa19ae2918b..390c59e84da 100644 --- a/lib/galaxy/tool_util/parser/interface.py +++ b/lib/galaxy/tool_util/parser/interface.py @@ -7,19 +7,46 @@ from abc import ( ) from os.path import join from typing import ( + Any, Dict, List, Optional, Tuple, + Union, ) import packaging.version +from typing_extensions import TypedDict from galaxy.util.path import safe_walk from .util import _parse_name NOT_IMPLEMENTED_MESSAGE = "Galaxy tool format does not yet support this tool feature." +AssertionDict = TypedDict( + "AssertionDict", {"tag": str, "attributes": Dict[str, Any], "children": Optional[List[Dict[str, Any]]]} +) +AssertionList = Optional[List[AssertionDict]] +XmlInt = Union[str, int] +ToolSourceTest = TypedDict( + "ToolSourceTest", + { + "inputs": Any, + "outputs": Any, + "output_collections": List[Any], + "stdout": AssertionList, + "stderr": AssertionList, + "expect_exit_code": Optional[XmlInt], + "expect_failure": bool, + "expect_test_failure": bool, + "maxseconds": Optional[XmlInt], + "expect_num_outputs": Optional[XmlInt], + "command": AssertionList, + "command_version": AssertionList, + }, +) +ToolSourceTests = TypedDict("ToolSourceTests", {"tests": List[ToolSourceTest]}) + class ToolSource(metaclass=ABCMeta): """This interface represents an abstract source to parse tool @@ -268,7 +295,7 @@ class ToolSource(metaclass=ABCMeta): paths_and_modtimes[self.source_path] = os.path.getmtime(self.source_path) return paths_and_modtimes - def parse_tests_to_dict(self): + def parse_tests_to_dict(self) -> ToolSourceTests: return {"tests": []} def __str__(self): diff --git a/lib/galaxy/tool_util/parser/xml.py b/lib/galaxy/tool_util/parser/xml.py index edbe5e44792..1c601308722 100644 --- a/lib/galaxy/tool_util/parser/xml.py +++ b/lib/galaxy/tool_util/parser/xml.py @@ -3,7 +3,10 @@ import logging import math import re import uuid -from typing import Optional +from typing import ( + List, + Optional, +) import packaging.version @@ -19,6 +22,7 @@ from galaxy.util import ( xml_to_string, ) from .interface import ( + AssertionList, InputSource, PageSource, PagesSource, @@ -26,6 +30,8 @@ from .interface import ( TestCollectionDef, TestCollectionOutputDef, ToolSource, + ToolSourceTest, + ToolSourceTests, ) from .output_actions import ToolOutputActionGroup from .output_collection_def import dataset_collector_descriptions_from_elem @@ -537,10 +543,10 @@ class XmlToolSource(ToolSource): def source_path(self): return self._source_path - def parse_tests_to_dict(self): + def parse_tests_to_dict(self) -> ToolSourceTests: tests_elem = self.root.find("tests") - tests = [] - rval = dict(tests=tests) + tests: List[ToolSourceTest] = [] + rval: ToolSourceTests = dict(tests=tests) if tests_elem is not None: for i, test_elem in enumerate(tests_elem.findall("test")): @@ -587,8 +593,8 @@ class XmlToolSource(ToolSource): return creators -def _test_elem_to_dict(test_elem, i, profile=None): - rval = dict( +def _test_elem_to_dict(test_elem, i, profile=None) -> ToolSourceTest: + rval: ToolSourceTest = dict( outputs=__parse_output_elems(test_elem), output_collections=__parse_output_collection_elems(test_elem, profile=profile), inputs=__parse_input_elems(test_elem, i), @@ -737,7 +743,7 @@ def __parse_assert_list(output_elem): return __parse_assert_list_from_elem(assert_elem) -def __parse_assert_list_from_elem(assert_elem): +def __parse_assert_list_from_elem(assert_elem) -> AssertionList: assert_list = None def convert_elem(elem): diff --git a/lib/galaxy/tool_util/parser/yaml.py b/lib/galaxy/tool_util/parser/yaml.py index 743f0c8fff4..8531d5e1726 100644 --- a/lib/galaxy/tool_util/parser/yaml.py +++ b/lib/galaxy/tool_util/parser/yaml.py @@ -12,10 +12,14 @@ from galaxy.tool_util.parser.util import ( DEFAULT_DELTA_FRAC, ) from .interface import ( + AssertionDict, + AssertionList, InputSource, PageSource, PagesSource, ToolSource, + ToolSourceTest, + ToolSourceTests, ) from .output_collection_def import dataset_collector_descriptions_from_output_dict from .output_objects import ( @@ -177,9 +181,9 @@ class YamlToolSource(ToolSource): ) return output_collection - def parse_tests_to_dict(self): - tests = [] - rval = dict(tests=tests) + def parse_tests_to_dict(self) -> ToolSourceTests: + tests: List[ToolSourceTest] = [] + rval: ToolSourceTests = dict(tests=tests) for i, test_dict in enumerate(self.root_dict.get("tests", [])): tests.append(_parse_test(i, test_dict)) @@ -206,7 +210,7 @@ class YamlToolSource(ToolSource): return json.dumps(self.root_dict) -def _parse_test(i, test_dict): +def _parse_test(i, test_dict) -> ToolSourceTest: inputs = test_dict["inputs"] if is_dict(inputs): new_inputs = [] @@ -231,7 +235,7 @@ def _parse_test(i, test_dict): name = output["name"] value = output.get("file", None) attributes = output - new_outputs.append((name, value, attributes)) + new_outputs.append({"name": name, "value": value, "attributes": attributes}) for output in new_outputs: attributes = output["attributes"] @@ -246,8 +250,6 @@ def _parse_test(i, test_dict): attributes["extra_files"] = [] # TODO attributes["metadata"] = {} - # TODO - assert_list = [] assert_list = __to_test_assert_list(attributes.get("asserts", [])) attributes["assert_list"] = assert_list _ensure_has(attributes, defaults) @@ -264,7 +266,7 @@ def _parse_test(i, test_dict): return test_dict -def __to_test_assert_list(assertions): +def __to_test_assert_list(assertions) -> AssertionList: def expand_dict_form(item): key, value = item new_value = value.copy() @@ -274,7 +276,7 @@ def __to_test_assert_list(assertions): if is_dict(assertions): assertions = map(expand_dict_form, assertions.items()) - assert_list = [] + assert_list: List[AssertionDict] = [] for assertion in assertions: # TODO: not handling nested assertions correctly, # not sure these are used though. @@ -282,7 +284,7 @@ def __to_test_assert_list(assertions): if "children" in assertion: children = assertion["children"] del assertion["children"] - assert_dict = dict( + assert_dict: AssertionDict = dict( tag=assertion["that"], attributes=assertion, children=children,