From 6633f66fdcc68598264c05046a34d2a2eddc02aa Mon Sep 17 00:00:00 2001 From: John Chilton Date: Tue, 8 Oct 2024 12:10:36 -0400 Subject: [PATCH] Fix param models for dynamic options and truevalue/falsevalue --- lib/galaxy/tool_util/parameters/convert.py | 1 - lib/galaxy/tool_util/parameters/factory.py | 7 ++++++- lib/galaxy/tool_util/parser/interface.py | 4 ++++ lib/galaxy/tool_util/parser/xml.py | 18 ++++++++++++++---- lib/galaxy/tools/parameters/basic.py | 2 ++ test/unit/app/tools/test_dynamic_options.py | 8 +++++--- test/unit/tool_util/test_parameter_convert.py | 13 +++++++++++++ 7 files changed, 44 insertions(+), 9 deletions(-) diff --git a/lib/galaxy/tool_util/parameters/convert.py b/lib/galaxy/tool_util/parameters/convert.py index 94e4d0c5e569..63dbb9ab58b9 100644 --- a/lib/galaxy/tool_util/parameters/convert.py +++ b/lib/galaxy/tool_util/parameters/convert.py @@ -312,7 +312,6 @@ def _fill_default_for(tool_state: Dict[str, Any], parameter: ToolParameterT) -> ) test_value = validate_explicit_conditional_test_value(test_parameter_name, explicit_test_value) when = _select_which_when(conditional, test_value, conditional_state) - test_parameter = conditional.test_parameter _fill_default_for(conditional_state, test_parameter) _fill_defaults(conditional_state, when) elif parameter_type in ["gx_repeat"]: diff --git a/lib/galaxy/tool_util/parameters/factory.py b/lib/galaxy/tool_util/parameters/factory.py index 13d52d5f9c77..932195effb9d 100644 --- a/lib/galaxy/tool_util/parameters/factory.py +++ b/lib/galaxy/tool_util/parameters/factory.py @@ -276,7 +276,12 @@ def _from_input_source_galaxy(input_source: InputSource, profile: float) -> Tool default_test_value = cond_test_parameter_default_value(test_parameter) for value, case_inputs_sources in input_source.parse_when_input_sources(): if isinstance(test_parameter, BooleanParameterModel): - # TODO: investigate truevalue/falsevalue when... + true_value = test_param_input_source.get("truevalue") + false_value = test_param_input_source.get("falsevalue") + if isinstance(value, str) and value == true_value: + value = True + elif isinstance(value, str) and value == false_value: + value = False typed_value = string_as_bool(value) else: typed_value = value diff --git a/lib/galaxy/tool_util/parser/interface.py b/lib/galaxy/tool_util/parser/interface.py index b9218ad54270..af72bf4a4825 100644 --- a/lib/galaxy/tool_util/parser/interface.py +++ b/lib/galaxy/tool_util/parser/interface.py @@ -435,6 +435,10 @@ def elem(self) -> Element: # used with other tool sources. raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE) + @abstractmethod + def get_dynamic_options_code(self) -> Optional[str]: + """If dynamic options are a piece of code to eval, return it.""" + @abstractmethod def get_data_table_name(self) -> Optional[str]: """If dynamic options are loaded from a data table, return the name.""" diff --git a/lib/galaxy/tool_util/parser/xml.py b/lib/galaxy/tool_util/parser/xml.py index 06a25c78f29b..aba5a6874248 100644 --- a/lib/galaxy/tool_util/parser/xml.py +++ b/lib/galaxy/tool_util/parser/xml.py @@ -1300,18 +1300,23 @@ def parse_input_sources(self): class XmlDynamicOptions(DynamicOptions): - def __init__(self, options_elem: Element): + def __init__(self, options_elem: Element, dynamic_option_code: Optional[str]): self._options_elem = options_elem + self._dynamic_options_code = dynamic_option_code def elem(self) -> Element: return self._options_elem + def get_dynamic_options_code(self) -> Optional[str]: + """If dynamic options are a piece of code to eval, return it.""" + return self._dynamic_options_code + def get_data_table_name(self) -> Optional[str]: """If dynamic options are loaded from a data table, return the name.""" - return self._options_elem.get("from_data_table") + return self._options_elem.get("from_data_table") if self._options_elem is not None else None def get_index_file_name(self) -> Optional[str]: - return self._options_elem.get("from_file") + return self._options_elem.get("from_file") if self._options_elem is not None else None class XmlInputSource(InputSource): @@ -1349,7 +1354,12 @@ def parse_validators(self) -> List[AnyValidatorModel]: def parse_dynamic_options(self) -> Optional[XmlDynamicOptions]: """Return a XmlDynamicOptions to describe dynamic options if options elem is available.""" options_elem = self.input_elem.find("options") - return XmlDynamicOptions(options_elem) if options_elem is not None else None + dynamic_option_code = self.input_elem.get("dynamic_options") + is_dynamic = options_elem is not None or dynamic_option_code is not None + if is_dynamic: + return XmlDynamicOptions(options_elem, dynamic_option_code) + else: + return None def parse_static_options(self) -> List[Tuple[str, str, bool]]: """ diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py index 4860f97b5d51..7ed9c657691a 100644 --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -123,6 +123,8 @@ def parse_dynamic_options(param, input_source): if not dynamic_options_config: return None options_elem = dynamic_options_config.elem() + if options_elem is None: + return None return dynamic_options.DynamicOptions(options_elem, param) diff --git a/test/unit/app/tools/test_dynamic_options.py b/test/unit/app/tools/test_dynamic_options.py index 77d9eb263ecf..05ec365a379e 100644 --- a/test/unit/app/tools/test_dynamic_options.py +++ b/test/unit/app/tools/test_dynamic_options.py @@ -6,9 +6,11 @@ def get_from_url_option(): - tool_param = Bunch() - tool_param.tool = Bunch() - tool_param.tool.app = Bunch() + tool_param = Bunch( + tool=Bunch( + app=Bunch(), + ), + ) return DynamicOptions( XML( diff --git a/test/unit/tool_util/test_parameter_convert.py b/test/unit/tool_util/test_parameter_convert.py index a14eab6d5657..7f5deff41e85 100644 --- a/test/unit/tool_util/test_parameter_convert.py +++ b/test/unit/tool_util/test_parameter_convert.py @@ -217,6 +217,19 @@ def test_fill_defaults(): assert with_defaults["cond"]["cond"] == "single" assert with_defaults["cond"]["inner_cond"]["inner_cond"] == "single" + # dynamic parameters should just stay empty - null would cause runtime to skip over population + with_defaults = fill_state_for({}, "parameters/gx_select_dynamic", partial=True) + assert "parameter" not in with_defaults + + # dynamic parameters should just stay empty - null would cause runtime to skip over population + with_defaults = fill_state_for( + {"conditional_parameter": {"test_parameter": False}}, + "parameters/gx_conditional_boolean_discriminate_on_string_value", + ) + assert "conditional_parameter" in with_defaults + assert "boolean_parameter" in with_defaults["conditional_parameter"] + assert with_defaults["conditional_parameter"]["boolean_parameter"] is False + def _fake_dereference(input: DataRequestUri) -> DataRequestInternalHda: return DataRequestInternalHda(id=EXAMPLE_ID_1)