diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py
index 32a3e799965..189bb3b7cba 100755
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -7,6 +7,9 @@ import glob
import json
import logging
import os
+import re
+import tarfile
+import tempfile
import threading
import urllib
from datetime import datetime
@@ -1979,6 +1982,108 @@ class Tool( object, Dictifiable ):
"""
return output_collect.collect_dynamic_collections( self, output, **kwds )
+ def to_archive(self):
+ tool = self.tool
+ tarball_files = []
+ temp_files = []
+ tool_xml = open( os.path.abspath( tool.config_file ), 'r' ).read()
+ # Retrieve tool help images and rewrite the tool's xml into a temporary file with the path
+ # modified to be relative to the repository root.
+ image_found = False
+ if tool.help is not None:
+ tool_help = tool.help._source
+ # Check each line of the rendered tool help for an image tag that points to a location under static/
+ for help_line in tool_help.split( '\n' ):
+ image_regex = re.compile( 'img alt="[^"]+" src="\${static_path}/([^"]+)"' )
+ matches = re.search( image_regex, help_line )
+ if matches is not None:
+ tool_help_image = matches.group(1)
+ tarball_path = tool_help_image
+ filesystem_path = os.path.abspath( os.path.join( self.app.config.root, 'static', tool_help_image ) )
+ if os.path.exists( filesystem_path ):
+ tarball_files.append( ( filesystem_path, tarball_path ) )
+ image_found = True
+ tool_xml = tool_xml.replace( '${static_path}/%s' % tarball_path, tarball_path )
+ # If one or more tool help images were found, add the modified tool XML to the tarball instead of the original.
+ if image_found:
+ fd, new_tool_config = tempfile.mkstemp( suffix='.xml' )
+ os.close( fd )
+ open( new_tool_config, 'w' ).write( tool_xml )
+ tool_tup = ( os.path.abspath( new_tool_config ), os.path.split( tool.config_file )[-1] )
+ temp_files.append( os.path.abspath( new_tool_config ) )
+ else:
+ tool_tup = ( os.path.abspath( tool.config_file ), os.path.split( tool.config_file )[-1] )
+ tarball_files.append( tool_tup )
+ # TODO: This feels hacky.
+ tool_command = tool.command.strip().split()[0]
+ tool_path = os.path.dirname( os.path.abspath( tool.config_file ) )
+ # Add the tool XML to the tuple that will be used to populate the tarball.
+ if os.path.exists( os.path.join( tool_path, tool_command ) ):
+ tarball_files.append( ( os.path.join( tool_path, tool_command ), tool_command ) )
+ # Find and add macros and code files.
+ for external_file in tool.get_externally_referenced_paths( os.path.abspath( tool.config_file ) ):
+ external_file_abspath = os.path.abspath( os.path.join( tool_path, external_file ) )
+ tarball_files.append( ( external_file_abspath, external_file ) )
+ if os.path.exists( os.path.join( tool_path, "Dockerfile" ) ):
+ tarball_files.append( ( os.path.join( tool_path, "Dockerfile" ), "Dockerfile" ) )
+ # Find tests, and check them for test data.
+ tests = tool.tests
+ if tests is not None:
+ for test in tests:
+ # Add input file tuples to the list.
+ for input in test.inputs:
+ for input_value in test.inputs[ input ]:
+ input_path = os.path.abspath( os.path.join( 'test-data', input_value ) )
+ if os.path.exists( input_path ):
+ td_tup = ( input_path, os.path.join( 'test-data', input_value ) )
+ tarball_files.append( td_tup )
+ # And add output file tuples to the list.
+ for label, filename, _ in test.outputs:
+ output_filepath = os.path.abspath( os.path.join( 'test-data', filename ) )
+ if os.path.exists( output_filepath ):
+ td_tup = ( output_filepath, os.path.join( 'test-data', filename ) )
+ tarball_files.append( td_tup )
+ for param in tool.input_params:
+ # Check for tool data table definitions.
+ if hasattr( param, 'options' ):
+ if hasattr( param.options, 'tool_data_table' ):
+ data_table = param.options.tool_data_table
+ if hasattr( data_table, 'filenames' ):
+ data_table_definitions = []
+ for data_table_filename in data_table.filenames:
+ # FIXME: from_shed_config seems to always be False.
+ if not data_table.filenames[ data_table_filename ][ 'from_shed_config' ]:
+ tar_file = data_table.filenames[ data_table_filename ][ 'filename' ] + '.sample'
+ sample_file = os.path.join( data_table.filenames[ data_table_filename ][ 'tool_data_path' ],
+ tar_file )
+ # Use the .sample file, if one exists. If not, skip this data table.
+ if os.path.exists( sample_file ):
+ tarfile_path, tarfile_name = os.path.split( tar_file )
+ tarfile_path = os.path.join( 'tool-data', tarfile_name )
+ tarball_files.append( ( sample_file, tarfile_path ) )
+ data_table_definitions.append( data_table.xml_string )
+ if len( data_table_definitions ) > 0:
+ # Put the data table definition XML in a temporary file.
+ table_definition = '\n\n %s'
+ table_definition = table_definition % '\n'.join( data_table_definitions )
+ fd, table_conf = tempfile.mkstemp()
+ os.close( fd )
+ open( table_conf, 'w' ).write( table_definition )
+ tarball_files.append( ( table_conf, os.path.join( 'tool-data', 'tool_data_table_conf.xml.sample' ) ) )
+ temp_files.append( table_conf )
+ # Create the tarball.
+ fd, tarball_archive = tempfile.mkstemp( suffix='.tgz' )
+ os.close( fd )
+ tarball = tarfile.open( name=tarball_archive, mode='w:gz' )
+ # Add the files from the previously generated list.
+ for fspath, tarpath in tarball_files:
+ tarball.add( fspath, arcname=tarpath )
+ tarball.close()
+ # Delete any temporary files that were generated.
+ for temp_file in temp_files:
+ os.remove( temp_file )
+ return tarball_archive
+
def to_dict( self, trans, link_details=False, io_details=False ):
""" Returns dict of tool. """
diff --git a/lib/galaxy/tools/toolbox/base.py b/lib/galaxy/tools/toolbox/base.py
index b1fd6335820..098874d3dd7 100644
--- a/lib/galaxy/tools/toolbox/base.py
+++ b/lib/galaxy/tools/toolbox/base.py
@@ -1,14 +1,13 @@
import logging
import os
-import re
import string
-import tarfile
-import tempfile
from markupsafe import escape
from six.moves.urllib.parse import urlparse
from six import iteritems
+from galaxy.exceptions import ObjectNotFound
+
from galaxy.util.dictifiable import Dictifiable
from galaxy.util.odict import odict
@@ -763,109 +762,10 @@ class AbstractToolBox( Dictifiable, ManagesIntegratedToolPanelMixin, object ):
"""
# Make sure the tool is actually loaded.
if tool_id not in self._tools_by_id:
- return None, False, "No tool with id %s" % escape( tool_id )
+ raise ObjectNotFound("No tool found with id '%s'." % escape( tool_id ))
else:
tool = self._tools_by_id[ tool_id ]
- tarball_files = []
- temp_files = []
- tool_xml = open( os.path.abspath( tool.config_file ), 'r' ).read()
- # Retrieve tool help images and rewrite the tool's xml into a temporary file with the path
- # modified to be relative to the repository root.
- image_found = False
- if tool.help is not None:
- tool_help = tool.help._source
- # Check each line of the rendered tool help for an image tag that points to a location under static/
- for help_line in tool_help.split( '\n' ):
- image_regex = re.compile( 'img alt="[^"]+" src="\${static_path}/([^"]+)"' )
- matches = re.search( image_regex, help_line )
- if matches is not None:
- tool_help_image = matches.group(1)
- tarball_path = tool_help_image
- filesystem_path = os.path.abspath( os.path.join( trans.app.config.root, 'static', tool_help_image ) )
- if os.path.exists( filesystem_path ):
- tarball_files.append( ( filesystem_path, tarball_path ) )
- image_found = True
- tool_xml = tool_xml.replace( '${static_path}/%s' % tarball_path, tarball_path )
- # If one or more tool help images were found, add the modified tool XML to the tarball instead of the original.
- if image_found:
- fd, new_tool_config = tempfile.mkstemp( suffix='.xml' )
- os.close( fd )
- open( new_tool_config, 'w' ).write( tool_xml )
- tool_tup = ( os.path.abspath( new_tool_config ), os.path.split( tool.config_file )[-1] )
- temp_files.append( os.path.abspath( new_tool_config ) )
- else:
- tool_tup = ( os.path.abspath( tool.config_file ), os.path.split( tool.config_file )[-1] )
- tarball_files.append( tool_tup )
- # TODO: This feels hacky.
- tool_command = tool.command.strip().split()[0]
- tool_path = os.path.dirname( os.path.abspath( tool.config_file ) )
- # Add the tool XML to the tuple that will be used to populate the tarball.
- if os.path.exists( os.path.join( tool_path, tool_command ) ):
- tarball_files.append( ( os.path.join( tool_path, tool_command ), tool_command ) )
- # Find and add macros and code files.
- for external_file in tool.get_externally_referenced_paths( os.path.abspath( tool.config_file ) ):
- external_file_abspath = os.path.abspath( os.path.join( tool_path, external_file ) )
- tarball_files.append( ( external_file_abspath, external_file ) )
- if os.path.exists( os.path.join( tool_path, "Dockerfile" ) ):
- tarball_files.append( ( os.path.join( tool_path, "Dockerfile" ), "Dockerfile" ) )
- # Find tests, and check them for test data.
- tests = tool.tests
- if tests is not None:
- for test in tests:
- # Add input file tuples to the list.
- for input in test.inputs:
- for input_value in test.inputs[ input ]:
- input_path = os.path.abspath( os.path.join( 'test-data', input_value ) )
- if os.path.exists( input_path ):
- td_tup = ( input_path, os.path.join( 'test-data', input_value ) )
- tarball_files.append( td_tup )
- # And add output file tuples to the list.
- for label, filename, _ in test.outputs:
- output_filepath = os.path.abspath( os.path.join( 'test-data', filename ) )
- if os.path.exists( output_filepath ):
- td_tup = ( output_filepath, os.path.join( 'test-data', filename ) )
- tarball_files.append( td_tup )
- for param in tool.input_params:
- # Check for tool data table definitions.
- if hasattr( param, 'options' ):
- if hasattr( param.options, 'tool_data_table' ):
- data_table = param.options.tool_data_table
- if hasattr( data_table, 'filenames' ):
- data_table_definitions = []
- for data_table_filename in data_table.filenames:
- # FIXME: from_shed_config seems to always be False.
- if not data_table.filenames[ data_table_filename ][ 'from_shed_config' ]:
- tar_file = data_table.filenames[ data_table_filename ][ 'filename' ] + '.sample'
- sample_file = os.path.join( data_table.filenames[ data_table_filename ][ 'tool_data_path' ],
- tar_file )
- # Use the .sample file, if one exists. If not, skip this data table.
- if os.path.exists( sample_file ):
- tarfile_path, tarfile_name = os.path.split( tar_file )
- tarfile_path = os.path.join( 'tool-data', tarfile_name )
- tarball_files.append( ( sample_file, tarfile_path ) )
- data_table_definitions.append( data_table.xml_string )
- if len( data_table_definitions ) > 0:
- # Put the data table definition XML in a temporary file.
- table_definition = '\n\n %s'
- table_definition = table_definition % '\n'.join( data_table_definitions )
- fd, table_conf = tempfile.mkstemp()
- os.close( fd )
- open( table_conf, 'w' ).write( table_definition )
- tarball_files.append( ( table_conf, os.path.join( 'tool-data', 'tool_data_table_conf.xml.sample' ) ) )
- temp_files.append( table_conf )
- # Create the tarball.
- fd, tarball_archive = tempfile.mkstemp( suffix='.tgz' )
- os.close( fd )
- tarball = tarfile.open( name=tarball_archive, mode='w:gz' )
- # Add the files from the previously generated list.
- for fspath, tarpath in tarball_files:
- tarball.add( fspath, arcname=tarpath )
- tarball.close()
- # Delete any temporary files that were generated.
- for temp_file in temp_files:
- os.remove( temp_file )
- return tarball_archive, True, None
- return None, False, "An unknown error occurred."
+ return tool.to_archive()
def reload_tool_by_id( self, tool_id ):
"""
diff --git a/lib/galaxy/web/base/controllers/admin.py b/lib/galaxy/web/base/controllers/admin.py
index 360e2c98907..57ffca7b485 100644
--- a/lib/galaxy/web/base/controllers/admin.py
+++ b/lib/galaxy/web/base/controllers/admin.py
@@ -68,26 +68,24 @@ class Admin( object ):
def package_tool( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
toolbox = self.app.toolbox
tool_id = None
if params.get( 'package_tool_button', False ):
tool_id = params.get('tool_id', None)
- tool_tarball, success, message = trans.app.toolbox.package_tool( trans, tool_id )
- if success:
+ try:
+ tool_tarball = trans.app.toolbox.package_tool( trans, tool_id )
trans.response.set_content_type( 'application/x-gzip' )
download_file = open( tool_tarball )
os.unlink( tool_tarball )
tarball_path, filename = os.path.split( tool_tarball )
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % ( tool_id )
return download_file
- else:
- status = 'error'
- return trans.fill_template( '/admin/package_tool.mako',
- tool_id=tool_id,
- toolbox=toolbox,
- message=message,
- status=status )
+ except Exception:
+ return trans.fill_template( '/admin/package_tool.mako',
+ tool_id=tool_id,
+ toolbox=toolbox,
+ message=message,
+ status='error' )
@web.expose
@web.require_admin
diff --git a/lib/galaxy/webapps/galaxy/api/tools.py b/lib/galaxy/webapps/galaxy/api/tools.py
index e4a3112d07e..dfa2febb1e8 100644
--- a/lib/galaxy/webapps/galaxy/api/tools.py
+++ b/lib/galaxy/webapps/galaxy/api/tools.py
@@ -198,12 +198,11 @@ class ToolsController( BaseAPIController, UsesVisualizationMixin ):
@web.expose_api_raw
@web.require_admin
def download( self, trans, id, **kwds ):
- tool_tarball, success, message = trans.app.toolbox.package_tool( trans, id )
- if success:
- trans.response.set_content_type( 'application/x-gzip' )
- download_file = open( tool_tarball )
- trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % ( id )
- return download_file
+ tool_tarball = trans.app.toolbox.package_tool(trans, id)
+ trans.response.set_content_type('application/x-gzip')
+ download_file = open(tool_tarball, "rb")
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.tgz"' % (id)
+ return download_file
@expose_api_anonymous
def create( self, trans, payload, **kwd ):