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)