diff --git a/lib/galaxy/tool_util/linters/inputs.py b/lib/galaxy/tool_util/linters/inputs.py index a1fd3e08816..5e3f28d7b2c 100644 --- a/lib/galaxy/tool_util/linters/inputs.py +++ b/lib/galaxy/tool_util/linters/inputs.py @@ -109,7 +109,7 @@ PARAMETER_VALIDATOR_TYPE_COMPATIBILITY = { } PARAM_TYPE_CHILD_COMBINATIONS = [ - ("./options", ["select", "drill_down"]), + ("./options", ["data", "select", "drill_down"]), ("./options/option", ["drill_down"]), ("./column", ["data_column"]), ] @@ -168,6 +168,37 @@ def lint_inputs(tool_xml, lint_ctx): lint_ctx.warn( f"Param input [{param_name}] with no format specified - 'data' format will be assumed.", node=param ) + options = param.findall("./options") + has_options_filter_attribute = False + if len(options) == 1: + for oa in options[0].attrib: + if oa == "options_filter_attribute": + has_options_filter_attribute = True + else: + lint_ctx.error(f"Data parameter [{param_name}] uses invalid attribute: {oa}", node=param) + elif len(options) > 1: + lint_ctx.error(f"Data parameter [{param_name}] contains multiple options elements.", node=options[1]) + # for data params only filters with key='build' of type='data_meta' are allowed + filters = param.findall("./options/filter") + for f in filters: + if not f.get("ref"): + lint_ctx.error( + f"Data parameter [{param_name}] filter needs to define a ref attribute", + node=f, + ) + if has_options_filter_attribute: + if f.get("type") != "data_meta": + lint_ctx.error( + f'Data parameter [{param_name}] for filters only type="data_meta" is allowed, found type="{f.get("type")}"', + node=f, + ) + else: + if f.get("key") != "dbkey" or f.get("type") != "data_meta": + lint_ctx.error( + f'Data parameter [{param_name}] for filters only type="data_meta" and key="dbkey" are allowed, found type="{f.get("type")}" and key="{f.get("key")}"', + node=f, + ) + elif param_type == "select": # get dynamic/statically defined options dynamic_options = param.get("dynamic_options", None) diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd index 882b0437dd1..4666f85a31b 100644 --- a/lib/galaxy/tool_util/xsd/galaxy.xsd +++ b/lib/galaxy/tool_util/xsd/galaxy.xsd @@ -3968,14 +3968,17 @@ dataset for the contained input of the type specified using the ``type`` tag. `` tag when the ``type`` attribute value is ``select`` or -``data`` and used to dynamically generated lists of options. This tag set -dynamically creates a list of options whose values can be -obtained from a predefined file stored locally or a dataset selected from the -current history. +``data`` and used to dynamically generated lists of options. + +For data parameters this tag can be used to restrict possible input datasets to datasets that match the ``dbkey`` of another data input by including a ``data_meta`` filter. See for +instance here: [/tools/maf/interval2maf.xml](https://github.com/galaxyproject/galaxy/blob/master/tools/maf/interval2maf.xml) + +For select parameters this tag set dynamically creates a list of options whose +values can be obtained from a predefined file stored locally or a dataset +selected from the current history. There are at least five basic ways to use this tag - four of these correspond to a ``from_XXX`` attribute on the ``options`` directive and the other is to diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 5b37c932232..089c07dd02a 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -192,6 +192,43 @@ INPUTS_DATA_PARAM = """ """ +INPUTS_DATA_PARAM_OPTIONS = """ + + + + + + + + + +""" + +INPUTS_DATA_PARAM_OPTIONS_FILTER_ATTRIBUTE = """ + + + + + + + + + +""" + +INPUTS_DATA_PARAM_INVALIDOPTIONS = """ + + + + + + + + + + +""" + INPUTS_CONDITIONAL = """ @@ -1034,6 +1071,42 @@ def test_inputs_data_param(lint_ctx): assert not lint_ctx.error_messages +def test_inputs_data_param_options(lint_ctx): + tool_source = get_xml_tool_source(INPUTS_DATA_PARAM_OPTIONS) + run_lint(lint_ctx, inputs.lint_inputs, tool_source) + assert not lint_ctx.valid_messages + assert "Found 1 input parameters." in lint_ctx.info_messages + assert len(lint_ctx.info_messages) == 1 + assert not lint_ctx.warn_messages + assert not lint_ctx.error_messages + + +def test_inputs_data_param_options_filter_attribute(lint_ctx): + tool_source = get_xml_tool_source(INPUTS_DATA_PARAM_OPTIONS_FILTER_ATTRIBUTE) + run_lint(lint_ctx, inputs.lint_inputs, tool_source) + assert not lint_ctx.valid_messages + assert "Found 1 input parameters." in lint_ctx.info_messages + assert len(lint_ctx.info_messages) == 1 + assert not lint_ctx.warn_messages + assert not lint_ctx.error_messages + + +def test_inputs_data_param_invalid_options(lint_ctx): + tool_source = get_xml_tool_source(INPUTS_DATA_PARAM_INVALIDOPTIONS) + run_lint(lint_ctx, inputs.lint_inputs, tool_source) + assert not lint_ctx.valid_messages + assert "Found 1 input parameters." in lint_ctx.info_messages + assert len(lint_ctx.info_messages) == 1 + assert not lint_ctx.warn_messages + assert "Data parameter [valid_name] contains multiple options elements." in lint_ctx.error_messages + assert "Data parameter [valid_name] filter needs to define a ref attribute" in lint_ctx.error_messages + assert ( + 'Data parameter [valid_name] for filters only type="data_meta" and key="dbkey" are allowed, found type="expression" and key="None"' + in lint_ctx.error_messages + ) + assert len(lint_ctx.error_messages) == 3 + + def test_inputs_conditional(lint_ctx): tool_source = get_xml_tool_source(INPUTS_CONDITIONAL) run_lint(lint_ctx, inputs.lint_inputs, tool_source) @@ -1219,7 +1292,7 @@ def test_inputs_type_child_combinations(lint_ctx): assert not lint_ctx.valid_messages assert not lint_ctx.warn_messages assert ( - "Parameter [text_param] './options' tags are only allowed for parameters of type ['select', 'drill_down']" + "Parameter [text_param] './options' tags are only allowed for parameters of type ['data', 'select', 'drill_down']" in lint_ctx.error_messages ) assert (