Implement profile="XX.XX" on tools, better defaults for newer tools.

- set -e in tool command scripts.
 - Disable implicit extra file collection. All dynamic extra file collection requires a <discover_datasets tag.
 - Disable format="input" - require explicit metadata targets (metadata_source, format_source).
 - Disable interpreter= - hacky and doesn't work as expected in too many circumstances. Use $__tool_directory__.
 - Disable $param_file - grepping the command for the string is impercise. If useful we can add flag to match the flag used to produce inputs as a JSON file.
 - Disable default version of 1.0.0, the IUC has made it clear they prefer all tools have an explicit version.

Fixes #1609.
This commit is contained in:
John Chilton
2016-02-22 10:47:11 +00:00
parent d020522650
commit b92074e6ff
9 changed files with 69 additions and 11 deletions
+13 -2
View File
@@ -20,6 +20,7 @@ from mako.template import Template
from paste import httpexceptions
from six import string_types
from galaxy.version import VERSION_MAJOR
from galaxy import model
from galaxy.managers import histories
from galaxy.datatypes.metadata import JobExternalOutputMetadataWrapper
@@ -460,6 +461,8 @@ class Tool( object, Dictifiable ):
"""
Read tool configuration from the element `root` and fill in `self`.
"""
self.legacy_defaults = tool_source.legacy_defaults
tool_profile = tool_source.parse_profile()
# Get the UNIQUE id for the tool
self.old_id = tool_source.parse_id()
if guid is None:
@@ -469,6 +472,11 @@ class Tool( object, Dictifiable ):
if not self.id:
raise Exception( "Missing tool 'id'" )
if not self.legacy_defaults and VERSION_MAJOR < tool_profile:
template = "The tool %s targets version %s of Galaxy, you should upgrade Galaxy to ensure proper functioning of this tool."
message = template % (self.id, tool_profile)
log.warn(message)
# Get the (user visible) name of the tool
self.name = tool_source.parse_name()
if not self.name:
@@ -476,8 +484,11 @@ class Tool( object, Dictifiable ):
self.version = tool_source.parse_version()
if not self.version:
# For backward compatibility, some tools may not have versions yet.
self.version = "1.0.0"
if self.legacy_defaults:
# For backward compatibility, some tools may not have versions yet.
self.version = "1.0.0"
else:
raise Exception( "Missing tool version.")
# Support multi-byte tools
self.is_multi_byte = tool_source.parse_is_multi_byte()
+6 -2
View File
@@ -224,7 +224,10 @@ class DefaultToolAction( object ):
# Deal with input dataset names, 'dbkey' and types
input_names = []
input_ext = 'data'
# format='input" previously would give you a random extension from
# the input extensions, now it should just give "input" as the output
# format.
input_ext = 'data' if tool.legacy_defaults else "input"
input_dbkey = incoming.get( "dbkey", "?" )
for name, data in reversed(inp_data.items()):
if not data:
@@ -239,7 +242,8 @@ class DefaultToolAction( object ):
else: # HDA
if data.hid:
input_names.append( 'data %s' % data.hid )
input_ext = data.ext
if tool.legacy_defaults:
input_ext = data.ext
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
+1 -1
View File
@@ -526,7 +526,7 @@ class ToolEvaluator( object ):
param_dict = self.param_dict
directory = self.local_working_directory
command = self.tool.command
if command and "$param_file" in command:
if self.tool.legacy_defaults and command and "$param_file" in command:
fd, param_filename = tempfile.mkstemp( dir=directory )
os.close( fd )
f = open( param_filename, "w" )
+3
View File
@@ -133,6 +133,9 @@ class CwlToolSource(ToolSource):
containers=containers,
))
def parse_profile(self):
return "16.04"
class CwlPageSource(PageSource):
+11
View File
@@ -11,6 +11,11 @@ class ToolSource(object):
__metaclass__ = ABCMeta
default_is_multi_byte = False
@property
def legacy_defaults(self):
# Restore oldest defaults for XML-based tools.
return self.parse_profile() == "legacy"
@abstractmethod
def parse_id(self):
""" Parse an ID describing the abstract tool. This is not the
@@ -158,6 +163,12 @@ class ToolSource(object):
doesn't define help text.
"""
@abstractmethod
def parse_profile(self):
""" Return tool profile to use, currently this should be a Galaxy
version (> 16.04) or 'legacy'.
"""
def parse_tests_to_dict(self):
return {'tests': []}
@@ -23,9 +23,9 @@ INPUT_DBKEY_TOKEN = "__input__"
LEGACY_DEFAULT_DBKEY = None # don't use __input__ for legacy default collection
def dataset_collector_descriptions_from_elem( elem ):
def dataset_collector_descriptions_from_elem( elem, legacy=True ):
primary_dataset_elems = elem.findall( "discover_datasets" )
if not primary_dataset_elems:
if len(primary_dataset_elems) == 0 and legacy:
return [ DEFAULT_DATASET_COLLECTOR_DESCRIPTION ]
else:
return map( lambda elem: DatasetCollectionDescription( **elem.attrib ), primary_dataset_elems )
+27 -4
View File
@@ -112,7 +112,12 @@ class XmlToolSource(ToolSource):
def parse_interpreter(self):
command_el = self._command_el
return (command_el is not None) and command_el.get("interpreter", None)
interpreter = (command_el is not None) and command_el.get("interpreter", None)
if not self.legacy_defaults:
log.warn("Deprecated interpeter attribute on command element is now ignored.")
interpreter = None
return interpreter
def parse_version_command(self):
version_cmd = self.root.find("version_command")
@@ -195,7 +200,7 @@ class XmlToolSource(ToolSource):
dataset_collector_descriptions = None
if collection_elem.find( "discover_datasets" ) is not None:
dataset_collector_descriptions = dataset_collector_descriptions_from_elem( collection_elem )
dataset_collector_descriptions = dataset_collector_descriptions_from_elem( collection_elem, legacy=False )
structure = ToolOutputCollectionStructure(
collection_type=collection_type,
collection_type_source=collection_type_source,
@@ -262,7 +267,7 @@ class XmlToolSource(ToolSource):
output.from_work_dir = data_elem.get("from_work_dir", None)
output.hidden = string_as_bool( data_elem.get("hidden", "") )
output.actions = ToolOutputActionGroup( output, data_elem.find( 'actions' ) )
output.dataset_collector_descriptions = dataset_collector_descriptions_from_elem( data_elem )
output.dataset_collector_descriptions = dataset_collector_descriptions_from_elem( data_elem, legacy=self.legacy_defaults )
return output
def parse_stdio(self):
@@ -277,6 +282,8 @@ class XmlToolSource(ToolSource):
return aggressive_error_checks()
else:
raise ValueError("Unknown detect_errors value encountered [%s]" % detect_errors)
elif len(self.root.findall('stdio')) == 0 and not self.legacy_defaults:
return error_on_exit_code()
else:
parser = StdioParser(self.root)
return parser.stdio_exit_codes, parser.stdio_regexes
@@ -285,8 +292,10 @@ class XmlToolSource(ToolSource):
command_el = self._command_el
if command_el is not None:
return string_as_bool(command_el.get("strict", "False"))
else:
elif self.legacy_defaults:
return False
else:
return True
def parse_help(self):
help_elem = self.root.find( 'help' )
@@ -307,6 +316,20 @@ class XmlToolSource(ToolSource):
return rval
@property
def legacy_defaults(self):
# Pre-16.04 or default XML defaults
# - Use standard error for error detection.
# - Don't run shells with -e
# - Auto-check for implicit multiple outputs.
# - Auto-check for $param_file.
# - Enable buggy interpreter attribute.
return self.parse_profile() == "legacy"
def parse_profile(self):
profile = self.root.get("profile", "legacy")
return profile
def _test_elem_to_dict(test_elem, i):
rval = dict(
+5
View File
@@ -168,6 +168,11 @@ class YamlToolSource(ToolSource):
return rval
def parse_profile(self):
"""
"""
return self.root_dict.get("profile", "16.04")
def _parse_test(i, test_dict):
inputs = test_dict["inputs"]
+1
View File
@@ -256,6 +256,7 @@ class TestComputeEnviornment( SimpleComputeEnvironment ):
class MockTool( object ):
def __init__( self, app ):
self.legacy_defaults = True
self.app = app
self.hooks_called = []
self.environment_variables = []