diff --git a/lib/galaxy/tool_util/linters/inputs.py b/lib/galaxy/tool_util/linters/inputs.py
index 90e4d6fe504..7c8a47c69af 100644
--- a/lib/galaxy/tool_util/linters/inputs.py
+++ b/lib/galaxy/tool_util/linters/inputs.py
@@ -77,7 +77,6 @@ def lint_inputs(tool_xml, lint_ctx):
options = param.findall("./options")
filters = param.findall("./options/filter")
select_options = param.findall('./option')
- select_options_text = [option.text.strip() if option.text is not None else option.attrib.get("value", "").capitalize() for option in select_options]
if dynamic_options is not None:
lint_ctx.warn(f"Select parameter [{param_name}] uses deprecated 'dynamic_options' attribute.")
@@ -137,9 +136,20 @@ def lint_inputs(tool_xml, lint_ctx):
lint_ctx.error(f"Select parameter [{param_name}] has option without value")
if any(option.text is None for option in select_options):
lint_ctx.warn(f"Select parameter [{param_name}] has option without text")
- if len(set(select_options_text)) != len(select_options_text):
+
+ select_options_texts = list()
+ select_options_values = list()
+ for option in select_options:
+ value = option.attrib.get("value", "")
+ if option.text is None:
+ text = value.capitalize()
+ else:
+ text = option.text
+ select_options_texts.append((text, option.attrib.get("selected", "false")))
+ select_options_values.append((value, option.attrib.get("selected", "false")))
+ if len(set(select_options_texts)) != len(select_options_texts):
lint_ctx.error(f"Select parameter [{param_name}] has multiple options with the same text content")
- if len({option.attrib.get("value") for option in select_options}) != len(select_options):
+ if len(set(select_options_values)) != len(select_options_values):
lint_ctx.error(f"Select parameter [{param_name}] has multiple options with the same value")
multiple = string_as_bool(param_attrib.get("multiple", "false"))
diff --git a/lib/galaxy/tool_util/parser/xml.py b/lib/galaxy/tool_util/parser/xml.py
index e78ab2e0838..170ddf92241 100644
--- a/lib/galaxy/tool_util/parser/xml.py
+++ b/lib/galaxy/tool_util/parser/xml.py
@@ -1124,12 +1124,31 @@ class XmlInputSource(InputSource):
return options_elem
def parse_static_options(self):
+ """
+ >>> from galaxy.util import parse_xml_string_to_etree
+ >>> xml = ''
+ >>> xis = XmlInputSource(parse_xml_string_to_etree(xml).getroot())
+ >>> xis.parse_static_options()
+ [('A', 'a', False), ('B', 'b', False)]
+ >>> xml = ''
+ >>> xis = XmlInputSource(parse_xml_string_to_etree(xml).getroot())
+ >>> xis.parse_static_options()
+ [('a', 'a', True), ('b', 'b', False)]
+ """
static_options = list()
elem = self.input_elem
for option in elem.findall("option"):
value = option.get("value")
+ text = option.text or value
selected = string_as_bool(option.get("selected", False))
- static_options.append((option.text or value, value, selected))
+ present = False
+ for i, o in enumerate(static_options):
+ if o[1] == value:
+ present = True
+ static_options[i] = (text, value, selected)
+ break
+ if not present:
+ static_options.append((text, value, selected))
return static_options
def parse_optional(self, default=None):
diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd
index 8ef122388f3..0ffb5a25046 100644
--- a/lib/galaxy/tool_util/xsd/galaxy.xsd
+++ b/lib/galaxy/tool_util/xsd/galaxy.xsd
@@ -3372,17 +3372,23 @@ value is ``select`` (i.e. ````).
```
An option can also be annotated with ``selected="true"`` to specify a
-default option.
+default option (note that the first option is selected automatically
+if ``optional="false"``).
```xml
-
-
+
+
```
+
+In general the values and the texts for the options need to be unique,
+but it is possible to specify an option two times if the 2nd has a different
+value for the ``selected`` attribute. This is handy if an option list is
+defined in a macro and different default value(s) are used.
]]>
diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py
index c856715d434..7756b5993c2 100644
--- a/test/unit/tool_util/test_tool_linters.py
+++ b/test/unit/tool_util/test_tool_linters.py
@@ -107,6 +107,17 @@ SELECT_DUPLICATED_OPTIONS = """
"""
+SELECT_DUPLICATED_OPTIONS_WITH_DIFF_SELECTED = """
+
+
+
+
+
+
+
+
+"""
+
SELECT_DEPRECATIONS = """
The BWA Mapper
@@ -331,6 +342,11 @@ TESTS = [
and 'Select parameter [select] has multiple options with the same value' in x.error_messages
and len(x.warn_messages) == 0 and len(x.error_messages) == 2
),
+ (
+ SELECT_DUPLICATED_OPTIONS_WITH_DIFF_SELECTED, inputs.lint_inputs,
+ lambda x:
+ len(x.warn_messages) == 0 and len(x.error_messages) == 0
+ ),
(
SELECT_DEPRECATIONS, inputs.lint_inputs,
lambda x:
@@ -407,6 +423,7 @@ TEST_IDS = [
'lint no when',
'radio select incompatibilities',
'select duplicated options',
+ 'select duplicated options with different selected',
'select deprecations',
'select option definitions',
'validator imcompatibilities',