diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py
index 347f107f172..e36a0523fb4 100755
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -69,7 +69,7 @@ from galaxy.model.item_attrs import Dictifiable
from galaxy.model import Workflow
from tool_shed.util import common_util
from tool_shed.util import shed_util_common as suc
-from .loader import load_tool, template_macro_params
+from .loader import load_tool, template_macro_params, raw_tool_xml_tree
from .execute import execute as execute_job
from .wrappers import (
ToolParameterValueWrapper,
@@ -2989,6 +2989,21 @@ class Tool( object, Dictifiable ):
def get_default_history_by_trans( self, trans, create=False ):
return trans.get_history( create=create )
+ @classmethod
+ def get_externally_referenced_paths( self, path ):
+ """ Return relative paths to externally referenced files by the tool
+ described by file at `path`. External components should not assume things
+ about the structure of tool xml files (this is the tool's responsibility).
+ """
+ tree = raw_tool_xml_tree(path)
+ root = tree.getroot()
+ external_paths = []
+ for code_elem in root.findall( 'code' ):
+ external_path = code_elem.get( 'file' )
+ if external_path:
+ external_paths.append( external_path )
+ return external_paths
+
class OutputParameterJSONTool( Tool ):
"""
diff --git a/lib/galaxy/tools/loader.py b/lib/galaxy/tools/loader.py
index d696ce680e1..85082647997 100644
--- a/lib/galaxy/tools/loader.py
+++ b/lib/galaxy/tools/loader.py
@@ -10,7 +10,7 @@ def load_tool(path):
"""
Loads tool from file system and preprocesses tool macros.
"""
- tree = parse_xml(path)
+ tree = raw_tool_xml_tree(path)
root = tree.getroot()
_import_macros(root, path)
@@ -38,6 +38,14 @@ def template_macro_params(root):
return param_dict
+def raw_tool_xml_tree(path):
+ """ Load raw (no macro expansion) tree representation of tool represented
+ at the specified path.
+ """
+ tree = parse_xml(path)
+ return tree
+
+
def _import_macros(root, path):
tool_dir = os.path.dirname(path)
macros_el = root.find('macros')
diff --git a/lib/tool_shed/tools/tool_validator.py b/lib/tool_shed/tools/tool_validator.py
index 09afe10c971..dc3910c1695 100644
--- a/lib/tool_shed/tools/tool_validator.py
+++ b/lib/tool_shed/tools/tool_validator.py
@@ -3,6 +3,7 @@ import logging
import os
import tempfile
+from galaxy.tools import Tool
from galaxy.tools import parameters
from galaxy.tools.parameters import dynamic_options
@@ -311,15 +312,14 @@ class ToolValidator( object ):
message = ''
tmp_tool_config = hg_util.get_named_tmpfile_from_ctx( ctx, ctx_file, work_dir )
if tmp_tool_config:
- element_tree, error_message = xml_util.parse_xml( tmp_tool_config )
- if element_tree is None:
+ tool_element, error_message = xml_util.parse_xml( tmp_tool_config )
+ if tool_element is None:
return tool, message
- element_tree_root = element_tree.getroot()
- # Look for code files required by the tool config.
+ # Look for external files required by the tool config.
tmp_code_files = []
- for code_elem in element_tree_root.findall( 'code' ):
- code_file_name = code_elem.get( 'file' )
- tmp_code_file_name = hg_util.copy_file_from_manifest( repo, ctx, code_file_name, work_dir )
+ external_paths = Tool.get_externally_referenced_paths( tmp_tool_config )
+ for path in external_paths:
+ tmp_code_file_name = hg_util.copy_file_from_manifest( repo, ctx, path, work_dir )
if tmp_code_file_name:
tmp_code_files.append( tmp_code_file_name )
tool, valid, message = self.load_tool_from_config( repository_id, tmp_tool_config )
diff --git a/test/unit/tools/test_tool_external_files.py b/test/unit/tools/test_tool_external_files.py
new file mode 100644
index 00000000000..369bd8d22a6
--- /dev/null
+++ b/test/unit/tools/test_tool_external_files.py
@@ -0,0 +1,26 @@
+""" Unit test logic related to finding externally referenced files in tool
+descriptions.
+"""
+import tempfile
+import os
+import shutil
+from galaxy.tools import Tool
+
+
+def test_finds_external_code_file():
+ assert __external_files("""""") == ["foo.py"]
+
+
+def test_finds_skips_empty_code_file_attribute():
+ assert __external_files("""""") == []
+
+
+def __external_files(contents):
+ base_path = tempfile.mkdtemp()
+ try:
+ tool_path = os.path.join(base_path, "tool.xml")
+ with open(tool_path, "w") as f:
+ f.write(contents)
+ return Tool.get_externally_referenced_paths(tool_path)
+ finally:
+ shutil.rmtree(base_path)