diff --git a/lib/galaxy/util/xml_macros.py b/lib/galaxy/util/xml_macros.py
index f03b8dac8b5..b02796080e1 100644
--- a/lib/galaxy/util/xml_macros.py
+++ b/lib/galaxy/util/xml_macros.py
@@ -19,6 +19,7 @@ def load_with_references(path):
# Collect tokens
tokens = _macros_of_type(root, 'token', lambda el: el.text or '')
+ tokens = expand_nested_tokens(tokens)
# Expand xml macros
macro_dict = _macros_of_type(root, 'xml', lambda el: XmlMacroDef(el))
@@ -82,8 +83,18 @@ def _macros_of_type(root, type, el_func):
return macro_dict
+def expand_nested_tokens(tokens):
+ for token_name in tokens.keys():
+ for current_token_name, current_token_value in tokens.items():
+ if token_name in current_token_value:
+ if token_name == current_token_name:
+ raise Exception("Token '%s' cannot contain itself" % token_name)
+ tokens[current_token_name] = current_token_value.replace(token_name, tokens[token_name])
+ return tokens
+
+
def _expand_tokens(elements, tokens):
- if not tokens or not elements:
+ if not tokens or elements is None:
return
for element in elements:
@@ -103,11 +114,11 @@ def _expand_tokens_for_el(element, tokens):
_expand_tokens(list(element), tokens)
-def _expand_tokens_str(str, tokens):
+def _expand_tokens_str(s, tokens):
for key, value in tokens.items():
- if str.find(key) > -1:
- str = str.replace(key, value)
- return str
+ if key in s:
+ s = s.replace(key, value)
+ return s
def _expand_macros(elements, macros, tokens):
@@ -139,7 +150,7 @@ def _expand_macro(element, expand_el, macros, tokens):
# HACK for elementtree, newer implementations (etree/lxml) won't
# require this parent_map data structure but elementtree does not
- # track parents or recongnize .find('..').
+ # track parents or recognize .find('..').
# TODO fix this now that we're not using elementtree
parent_map = dict((c, p) for p in element.iter() for c in p)
_xml_replace(expand_el, expanded_elements, parent_map)
diff --git a/test/unit/tools/test_parsing.py b/test/unit/tools/test_parsing.py
index 1734dadbca3..1ca7308d27a 100644
--- a/test/unit/tools/test_parsing.py
+++ b/test/unit/tools/test_parsing.py
@@ -41,6 +41,42 @@ TOOL_XML_1 = """
"""
+TOOL_WITH_TOKEN = r"""
+
+
+
+
+
+
+
+
+@NESTED_TOKEN@
+
+
+"""
+
+TOOL_WITH_RECURSIVE_TOKEN = r"""
+
+
+
+
+
+
+@NESTED_TOKEN@
+
+
+"""
+
TOOL_YAML_1 = """
name: "Bowtie Mapper"
class: GalaxyTool
@@ -253,6 +289,16 @@ class XmlLoaderTestCase(BaseLoaderTestCase):
def test_refresh_option(self):
assert self._tool_source.parse_refresh() is False
+ def test_nested_token(self):
+ tool_source = self._get_tool_source(source_contents=TOOL_WITH_TOKEN)
+ command = tool_source.parse_command()
+ assert command
+ assert '@' not in command
+
+ def test_recursive_token(self):
+ with self.assertRaises(Exception):
+ self._get_tool_source(source_contents=TOOL_WITH_RECURSIVE_TOKEN)
+
class YamlLoaderTestCase(BaseLoaderTestCase):
source_file_name = "bwa.yml"