Merge pull request #1764 from guerler/fix_check_and_update_000

Use visit inputs for check_and_update_param_values
This commit is contained in:
John Chilton
2016-03-01 12:40:16 -05:00
14 changed files with 180 additions and 333 deletions
@@ -361,12 +361,10 @@ EditorFormView = Backbone.View.extend({
self.canvas_manager.draw_overview();
// Determine if any parameters were 'upgraded' and provide message
upgrade_message = "";
$.each( data.upgrade_messages, function( step_id, messages ) {
_.each( data.upgrade_messages, function( messages, step_id ) {
var details = "";
Utils.deepeach( [ messages ], function( d ) {
$.each( d, function( i, v ) {
details += typeof v === "string" ? "<li>" + v + "</li>" : "";
});
_.each( messages, function( m ) {
details += "<li>" + m + "</li>";
});
if ( details ) {
upgrade_message += "<li>Step " + ( parseInt( step_id, 10 ) + 1 ) + ": " + self.workflow.nodes[ step_id ].name + "<ul>" + details + "</ul></li>";
+39 -38
View File
@@ -16,6 +16,7 @@ import shutil
import subprocess
import sys
import traceback
from xml.etree import ElementTree
from galaxy import model, util
from galaxy.util.xml_macros import load
from galaxy.datatypes import metadata
@@ -112,6 +113,15 @@ class JobConfiguration( object ):
"""
DEFAULT_NWORKERS = 4
JOB_RESOURCE_CONDITIONAL_XML = """<conditional name="__job_resource">
<param name="__job_resource__select" type="select" label="Job Resource Parameters">
<option value="no">Use default job resource parameters</option>
<option value="yes">Specify job resource parameters</option>
</param>
<when value="no"/>
<when value="yes"/>
</conditional>"""
def __init__(self, app):
"""Parse the job configuration XML.
"""
@@ -354,53 +364,44 @@ class JobConfiguration( object ):
log.debug('Done loading job configuration')
def get_tool_resource_parameters( self, tool_id ):
def get_tool_resource_xml( self, tool_id, tool_type ):
""" Given a tool id, return XML elements describing parameters to
insert into job resources.
:tool id: A tool ID (a string)
:tool type: A tool type (a string)
:returns: List of parameter elements.
"""
fields = []
if not tool_id:
return fields
# TODO: Only works with exact matches, should handle different kinds of ids
# the way destination lookup does.
resource_group = None
if tool_id in self.tools:
resource_group = self.tools[ tool_id ][ 0 ].get_resource_group()
resource_group = resource_group or self.default_resource_group
if resource_group and resource_group in self.resource_groups:
fields_names = self.resource_groups[ resource_group ]
fields = [ self.resource_parameters[ n ] for n in fields_names ]
return fields
if tool_id and tool_type is 'default':
# TODO: Only works with exact matches, should handle different kinds of ids
# the way destination lookup does.
resource_group = None
if tool_id in self.tools:
resource_group = self.tools[ tool_id ][ 0 ].get_resource_group()
resource_group = resource_group or self.default_resource_group
if resource_group and resource_group in self.resource_groups:
fields_names = self.resource_groups[ resource_group ]
fields = [ self.resource_parameters[ n ] for n in fields_names ]
if fields:
conditional_element = ElementTree.fromstring( self.JOB_RESOURCE_CONDITIONAL_XML )
when_yes_elem = conditional_element.findall( 'when' )[ 1 ]
for parameter in fields:
when_yes_elem.append( parameter )
return conditional_element
def __parse_resource_parameters( self ):
if not os.path.exists( self.app.config.job_resource_params_file ):
return
resource_param_file = self.app.config.job_resource_params_file
try:
resource_definitions = util.parse_xml( resource_param_file )
except Exception as e:
raise config_exception(e, resource_param_file)
resource_definitions_root = resource_definitions.getroot()
# TODO: Also handling conditionals would be awesome!
for parameter_elem in resource_definitions_root.findall( "param" ):
name = parameter_elem.get( "name" )
# Considered prepending __job_resource_param__ here and then
# stripping it off when making it available to dynamic job
# destination. Not needed because resource parameters are wrapped
# in a conditional.
# # expanded_name = "__job_resource_param__%s" % name
# # parameter_elem.set( "name", expanded_name )
self.resource_parameters[ name ] = parameter_elem
if os.path.exists( self.app.config.job_resource_params_file ):
resource_param_file = self.app.config.job_resource_params_file
try:
resource_definitions = util.parse_xml( resource_param_file )
except Exception as e:
raise config_exception( e, resource_param_file )
resource_definitions_root = resource_definitions.getroot()
# TODO: Also handling conditionals would be awesome!
for parameter_elem in resource_definitions_root.findall( "param" ):
name = parameter_elem.get( "name" )
self.resource_parameters[ name ] = parameter_elem
def __get_default(self, parent, names):
"""
+41 -134
View File
@@ -33,8 +33,7 @@ from galaxy.tools.parameters import params_to_incoming, check_param, params_from
from galaxy.tools.parameters import output_collect
from galaxy.tools.parameters.basic import (BaseURLToolParameter,
DataToolParameter, DataCollectionToolParameter, HiddenToolParameter,
SelectToolParameter, ToolParameter,
contains_workflow_parameter)
SelectToolParameter, ToolParameter)
from galaxy.tools.parameters.grouping import Conditional, ConditionalWhen, Repeat, Section, UploadDataset
from galaxy.tools.parameters.input_translation import ToolInputTranslator
from galaxy.tools.test import parse_tests
@@ -66,17 +65,6 @@ import galaxy.jobs
log = logging.getLogger( __name__ )
JOB_RESOURCE_CONDITIONAL_XML = """<conditional name="__job_resource">
<param name="__job_resource__select" type="select" label="Job Resource Parameters">
<option value="no">Use default job resource parameters</option>
<option value="yes">Specify job resource parameters</option>
</param>
<when value="no"></when>
<when value="yes">
</when>
</conditional>"""
HELP_UNINITIALIZED = threading.Lock()
@@ -137,27 +125,8 @@ class ToolBox( BaseGalaxyToolBox ):
tool_type = tool_source.parse_tool_type()
ToolClass = tool_types.get( tool_type )
else:
# Normal tool - only insert dynamic resource parameters for these
# tools.
root = getattr( tool_source, "root", None )
# TODO: mucking with the XML directly like this is terrible,
# modify inputs directly post load if possible.
if root is not None and hasattr( self.app, "job_config" ): # toolshed may not have job_config?
tool_id = root.get( 'id' )
parameters = self.app.job_config.get_tool_resource_parameters( tool_id )
if parameters:
inputs = root.find('inputs')
# If tool has not inputs, create some so we can insert conditional
if inputs is None:
inputs = ElementTree.fromstring( "<inputs></inputs>")
root.append( inputs )
# Insert a conditional allowing user to specify resource parameters.
conditional_element = ElementTree.fromstring( JOB_RESOURCE_CONDITIONAL_XML )
when_yes_elem = conditional_element.findall( "when" )[ 1 ]
for parameter in parameters:
when_yes_elem.append( parameter )
inputs.append( conditional_element )
# Normal tool
root = getattr( tool_source, 'root', None )
ToolClass = Tool
tool = ToolClass( config_file, tool_source, self.app, guid=guid, repository_id=repository_id, **kwds )
return tool
@@ -339,6 +308,8 @@ class Tool( object, Dictifiable ):
self.lineage_ids = []
# populate toolshed repository info, if available
self.populate_tool_shed_info()
# add tool resource parameters
self.populate_resource_parameters( tool_source )
# Parse XML element containing configuration
try:
self.parse( tool_source, guid=guid )
@@ -547,8 +518,9 @@ class Tool( object, Dictifiable ):
# Handle toolshed guids
self_ids = [ self.id.lower(), self.id.lower().rsplit('/', 1)[0], self.old_id.lower() ]
self.all_ids = self_ids
# In the toolshed context, there is no job config.
if 'job_config' in dir(self.app):
if hasattr( self.app, 'job_config' ):
self.job_tool_configurations = self.app.job_config.get_job_tool_configurations(self_ids)
# Is this a 'hidden' tool (hidden in tool menu)
@@ -728,7 +700,8 @@ class Tool( object, Dictifiable ):
# Parse the actual parameters
# Handle multiple page case
for page_source in pages.page_sources:
display, inputs = self.parse_input_page( page_source, enctypes )
inputs = self.parse_input_elem( page_source, enctypes )
display = page_source.parse_display()
self.inputs_by_page.append( inputs )
self.inputs.update( inputs )
self.display_by_page.append( display )
@@ -807,17 +780,6 @@ class Tool( object, Dictifiable ):
citations.append( citation )
return citations
def parse_input_page( self, page_source, enctypes ):
"""
Parse a page of inputs. This basically just calls 'parse_input_elem',
but it also deals with possible 'display' elements which are supported
only at the top/page level (not in groups).
"""
inputs = self.parse_input_elem( page_source, enctypes )
# Display
display = page_source.parse_display()
return display, inputs
def parse_input_elem( self, page_source, enctypes, context=None ):
"""
Parse a parent element whose children are inputs -- these could be
@@ -946,6 +908,17 @@ class Tool( object, Dictifiable ):
context[ name ].refresh_on_change = True
return param
def populate_resource_parameters( self, tool_source ):
root = getattr( tool_source, 'root', None )
if root is not None and hasattr( self.app, 'job_config' ) and hasattr( self.app.job_config, 'get_tool_resource_xml' ):
resource_xml = self.app.job_config.get_tool_resource_xml( root.get( 'id' ), self.tool_type )
if resource_xml is not None:
inputs = root.find( 'inputs' )
if inputs is None:
inputs = ElementTree.fromstring( '<inputs/>' )
root.append( inputs )
inputs.append( resource_xml )
def populate_tool_shed_info( self ):
if self.repository_id is not None and self.app.name == 'galaxy':
repository_id = self.app.security.decode_id( self.repository_id )
@@ -1097,7 +1070,7 @@ class Tool( object, Dictifiable ):
return self.code_namespace[name]
return None
def visit_inputs( self, value, callback ):
def visit_inputs( self, values, callback ):
"""
Call the function `callback` on each parameter of this tool. Visits
grouping parameters recursively and constructs unique prefixes for
@@ -1106,13 +1079,8 @@ class Tool( object, Dictifiable ):
`callback( level_prefix, parameter, parameter_value )`
"""
# HACK: Yet another hack around check_values -- WHY HERE?
if not self.check_values:
return
for input in self.inputs.itervalues():
if isinstance( input, ToolParameter ):
callback( "", input, value[input.name] )
else:
input.visit_inputs( "", value[input.name], callback )
if self.check_values:
visit_input_values( self.inputs, values, callback )
def handle_input( self, trans, incoming, history=None ):
"""
@@ -1280,87 +1248,24 @@ class Tool( object, Dictifiable ):
"""
messages = {}
request_context = WorkRequestContext( app=trans.app, user=trans.user, history=trans.history, workflow_building_mode=workflow_building_mode )
self.check_and_update_param_values_helper( self.inputs, values, request_context, messages, update_values=update_values )
return messages
def check_and_update_param_values_helper( self, inputs, values, trans, messages, context=None, prefix="", update_values=True ):
"""
Recursive helper for `check_and_update_param_values_helper`
"""
context = ExpressionContext( values, context )
for input in inputs.itervalues():
# No value, insert the default
if input.name not in values:
if isinstance( input, Conditional ):
cond_messages = {}
if not input.is_job_resource_conditional:
cond_messages = { input.test_param.name: "No value found for '%s%s', using default" % ( prefix, input.test_param.label ) }
messages[ input.name ] = cond_messages
test_value = input.test_param.get_initial_value( trans, context )
current_case = input.get_current_case( test_value )
self.check_and_update_param_values_helper( input.cases[ current_case ].inputs, {}, trans, cond_messages, context, prefix, update_values=update_values )
elif isinstance( input, Repeat ):
if input.min:
messages[ input.name ] = []
for i in range( input.min ):
rep_prefix = prefix + '%s %d > ' % ( input.title, i + 1 )
rep_dict = dict()
messages[ input.name ].append( rep_dict )
self.check_and_update_param_values_helper( input.inputs, {}, trans, rep_dict, context, rep_prefix, update_values=update_values )
elif isinstance( input, Section ):
messages[ input.name ] = {}
self.check_and_update_param_values_helper( input.inputs, {}, trans, messages[ input.name ], context, prefix, update_values=update_values )
else:
messages[ input.name ] = "No value found for '%s%s', using default" % ( prefix, input.label )
values[ input.name ] = input.get_initial_value( trans, context )
# Value, visit recursively as usual
else:
if isinstance( input, Repeat ):
for i, d in enumerate( values[ input.name ] ):
rep_prefix = prefix + '%s %d > ' % ( input.title, i + 1 )
self.check_and_update_param_values_helper( input.inputs, d, trans, messages, context, rep_prefix, update_values=update_values )
elif isinstance( input, Conditional ):
group_values = values[ input.name ]
use_initial_value = False
if '__current_case__' in group_values:
if int( group_values[ '__current_case__' ] ) >= len( input.cases ):
use_initial_value = True
else:
use_initial_value = True
if input.test_param.name not in group_values or use_initial_value:
# No test param invalidates the whole conditional
values[ input.name ] = group_values = input.get_initial_value( trans, context )
messages[ input.test_param.name ] = "No value found for '%s%s', using default" % ( prefix, input.test_param.label )
current_case = group_values[ '__current_case__' ]
for child_input in input.cases[current_case].inputs.itervalues():
messages[ child_input.name ] = "Value no longer valid for '%s%s', replacing with default" % ( prefix, child_input.label )
else:
current = group_values[ '__current_case__' ]
self.check_and_update_param_values_helper( input.cases[current].inputs, group_values, trans, messages, context, prefix, update_values=update_values )
elif isinstance( input, Section ):
messages[ input.name ] = {}
self.check_and_update_param_values_helper( input.inputs, values[ input.name ], trans, messages[ input.name ], context, prefix, update_values=update_values )
else:
# Regular tool parameter, no recursion needed
def validate_inputs( input, value, error, parent, context, prefixed_name, prefixed_label, **kwargs ):
if not error:
value, error = check_param( request_context, input, value, context )
if error:
if update_values:
try:
value = values[ input.name ]
if not trans.workflow_building_mode:
input.value_from_basic( input.value_to_basic( value, trans.app ), trans.app, ignore_errors=False )
input.validate( value, trans )
else:
# skip check if is workflow parameters
ck_param = True
search = input.type in [ 'text' ]
if trans.workflow_building_mode and contains_workflow_parameter( values[ input.name ], search=search ):
ck_param = False
# this will fail when a parameter's type has changed to a non-compatible one: e.g. conditional group changed to dataset input
if ck_param:
input.value_from_basic( input.value_to_basic( value, self.app ), self.app, ignore_errors=False )
value = input.get_initial_value( request_context, context )
if not prefixed_name.startswith( '__' ):
messages[ prefixed_name ] = '%s Using default: \'%s\'.' % ( error, value )
parent[ input.name ] = value
except:
log.info( "Parameter validation failed.", exc_info=True )
messages[ input.name ] = "Value no longer valid for '%s%s', replacing with default" % ( prefix, input.label )
if update_values:
values[ input.name ] = input.get_initial_value( trans, context )
messages[ prefixed_name ] = 'Attempt to replace invalid value for \'%s\' failed.' % ( prefixed_label )
else:
messages[ prefixed_name ] = error
visit_input_values( self.inputs, values, validate_inputs )
return messages
def build_dependency_shell_commands( self, job_directory=None ):
"""Return a list of commands to be run to populate the current environment to include this tools requirements."""
@@ -1703,10 +1608,11 @@ class Tool( object, Dictifiable ):
# load job parameters into incoming
tool_message = ''
tool_warnings = ''
if job:
try:
job_params = job.get_param_values( self.app, ignore_errors=True )
self.check_and_update_param_values( job_params, request_context, update_values=False )
tool_warnings = self.check_and_update_param_values( job_params, request_context, update_values=False )
self._map_source_to_history( request_context, self.inputs, job_params )
tool_message = self._compare_tool_version( job )
params_to_incoming( kwd, self.inputs, job_params, self.app )
@@ -1873,6 +1779,7 @@ class Tool( object, Dictifiable ):
'biostar_url' : self.app.config.biostar_url,
'sharable_url' : self.tool_shed_repository.get_sharable_url( self.app ) if self.tool_shed_repository else None,
'message' : tool_message,
'warnings' : tool_warnings,
'versions' : tool_versions,
'requirements' : [ { 'name' : r.name, 'version' : r.version } for r in self.requirements ],
'errors' : state_errors,
+6 -10
View File
@@ -50,7 +50,7 @@ class DefaultToolAction( object ):
current_user_roles = trans.get_current_user_roles()
input_datasets = odict()
def visitor( prefix, input, value, parent=None ):
def visitor( input, value, prefix, parent=None, **kwargs ):
def process_dataset( data, formats=None ):
if not data:
@@ -98,12 +98,12 @@ class DefaultToolAction( object ):
else:
raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' % ( input_datasets[ prefix + input.name + str( i + 1 ) ].extension, conversion_extensions ) )
if parent:
parent[input.name][i] = input_datasets[ prefix + input.name + str( i + 1 ) ]
parent[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ]
for conversion_name, conversion_data in conversions:
# allow explicit conversion to be stored in job_parameter table
parent[ conversion_name ][i] = conversion_data.id # a more robust way to determine JSONable value is desired
parent[ conversion_name ][ i ] = conversion_data.id # a more robust way to determine JSONable value is desired
else:
param_values[input.name][i] = input_datasets[ prefix + input.name + str( i + 1 ) ]
param_values[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ]
for conversion_name, conversion_data in conversions:
# allow explicit conversion to be stored in job_parameter table
param_values[ conversion_name ][i] = conversion_data.id # a more robust way to determine JSONable value is desired
@@ -143,10 +143,6 @@ class DefaultToolAction( object ):
# Skipping implicit conversion stuff for now, revisit at
# some point and figure out if implicitly converting a
# dataset collection makes senese.
# if i == 0:
# # Allow copying metadata to output, first item will be source.
# input_datasets[ prefix + input.name ] = data.dataset_instance
input_datasets[ prefix + input.name + str( i + 1 ) ] = data
tool.visit_inputs( param_values, visitor )
@@ -160,7 +156,7 @@ class DefaultToolAction( object ):
input_dataset_collections = dict()
def visitor( prefix, input, value, parent=None ):
def visitor( input, value, prefix, parent=None, **kwargs ):
if isinstance( input, DataToolParameter ):
values = value
if not isinstance( values, list ):
@@ -322,7 +318,7 @@ class DefaultToolAction( object ):
metadata_source = output.metadata_source
if metadata_source:
if isinstance( metadata_source, string_types ):
metadata_source = inp_data[metadata_source]
metadata_source = inp_data.get( metadata_source )
if metadata_source is not None:
data.init_meta( copy_from=metadata_source )
-10
View File
@@ -34,16 +34,6 @@ def execute( trans, tool, param_combinations, history, rerun_remap_job_id=None,
# Only workflow invocation code gets to set this, ignore user supplied
# values or rerun parameters.
del params[ '__workflow_invocation_uuid__' ]
# If this is a workflow, everything has now been connected so we should validate
# the state we about to execute one last time. Consider whether tool executions
# should run this as well.
if workflow_invocation_uuid:
messages = tool.check_and_update_param_values( params, trans, update_values=False )
if messages:
execution_tracker.record_error( messages )
return
job, result = tool.handle_single_execution( trans, rerun_remap_job_id, params, history, collection_info, execution_cache )
if job:
message = EXECUTION_SUCCESS_MESSAGE % (tool.id, job.id, job_timer)
+43 -15
View File
@@ -10,7 +10,7 @@ from galaxy.util.expressions import ExpressionContext
REPLACE_ON_TRUTHY = object()
def visit_input_values( inputs, input_values, callback, name_prefix="", label_prefix="", no_replacement_value=REPLACE_ON_TRUTHY, context=None ):
def visit_input_values( inputs, input_values, callback, name_prefix='', label_prefix='', parent_prefix='', context=None, no_replacement_value=REPLACE_ON_TRUTHY ):
"""
Given a tools parameter definition (`inputs`) and a specific set of
parameter `values`, call `callback` for each non-grouping parameter,
@@ -18,15 +18,35 @@ def visit_input_values( inputs, input_values, callback, name_prefix="", label_pr
and a display label.
If the callback returns a value, it will be replace the old value.
>>> from xml.etree.ElementTree import XML
>>> from galaxy.util.odict import odict
>>> from galaxy.tools.parameters.basic import TextToolParameter
>>> from galaxy.tools.parameters.grouping import Repeat
>>> a = TextToolParameter( None, XML( '<param name="a" type="text"/>' ) )
>>> b = Repeat()
>>> c = TextToolParameter( None, XML( '<param name="c" type="text"/>' ) )
>>> b.name = 'b'
>>> b.inputs = { 'c': c }
>>> def visitor( input, value, prefixed_name, **kwargs ):
... print '%s, %s, %s' % ( input.name, prefixed_name, value )
>>> visit_input_values( odict( [ ('a', a), ('b', b ) ] ), odict( [ ('a', 1), ('b', [ { 'c': 3 } ] ) ] ), visitor )
a, a, 1
c, b_0|c, 3
"""
def callback_helper( input, input_values, context, name_prefix, label_prefix ):
def callback_helper( input, input_values, name_prefix, label_prefix, parent_prefix, context=None, error=None ):
args = {
'input' : input,
'parent' : input_values,
'value' : input_values.get( input.name ),
'prefixed_name' : '%s%s' % ( name_prefix, input.name ),
'prefixed_label' : '%s%s' % ( label_prefix, input.label ),
'context' : context
'prefixed_label' : '%s%s' % ( label_prefix, input.label or input.name ),
'prefix' : parent_prefix,
'context' : context,
'error' : error
}
if input.name not in input_values:
args[ 'error' ] = 'No value found for \'%s\'.' % args.get( 'prefixed_label' )
new_value = callback( **args )
if no_replacement_value is REPLACE_ON_TRUTHY:
replace = bool( new_value )
@@ -36,25 +56,33 @@ def visit_input_values( inputs, input_values, callback, name_prefix="", label_pr
input_values[ input.name ] = new_value
context = ExpressionContext( input_values, context )
payload = { 'parent_prefix': name_prefix, 'context': context, 'no_replacement_value': no_replacement_value }
for input in inputs.itervalues():
if isinstance( input, Repeat ) or isinstance( input, UploadDataset ):
for i, d in enumerate( input_values[ input.name ] ):
index = d[ '__index__' ]
new_name_prefix = name_prefix + '%s_%d|' % ( input.name, index )
values = input_values[ input.name ] = input_values.get( input.name, [] )
for i, d in enumerate( values ):
d[ '__index__' ] = i
new_name_prefix = name_prefix + '%s_%d|' % ( input.name, i )
new_label_prefix = label_prefix + '%s %d > ' % ( input.title, i + 1 )
visit_input_values( input.inputs, d, callback, new_name_prefix, new_label_prefix, no_replacement_value=no_replacement_value, context=context )
visit_input_values( input.inputs, d, callback, new_name_prefix, new_label_prefix, **payload )
elif isinstance( input, Conditional ):
values = input_values[ input.name ]
values = input_values[ input.name ] = input_values.get( input.name, {} )
new_name_prefix = name_prefix + input.name + '|'
callback_helper( input.test_param, values, context, new_name_prefix, label_prefix )
values[ '__current_case__' ] = input.get_current_case( values.get( input.test_param.name ) )
visit_input_values( input.cases[ values[ '__current_case__' ] ].inputs, values, callback, new_name_prefix, label_prefix, no_replacement_value=no_replacement_value, context=context )
case_error = None
try:
input.get_current_case( values[ input.test_param.name ] )
except:
case_error = 'The selected case is unavailable/invalid.'
pass
callback_helper( input.test_param, values, new_name_prefix, label_prefix, parent_prefix, context=context, error=case_error )
values[ '__current_case__' ] = input.get_current_case( values[ input.test_param.name ] )
visit_input_values( input.cases[ values[ '__current_case__' ] ].inputs, values, callback, new_name_prefix, label_prefix, **payload )
elif isinstance( input, Section ):
values = input_values[ input.name ]
values = input_values[ input.name ] = input_values.get( input.name, {} )
new_name_prefix = name_prefix + input.name + '|'
visit_input_values( input.inputs, values, callback, new_name_prefix, label_prefix, no_replacement_value=no_replacement_value, context=context )
visit_input_values( input.inputs, values, callback, new_name_prefix, label_prefix, **payload )
else:
callback_helper( input, input_values, context, name_prefix, label_prefix )
callback_helper( input, input_values, name_prefix, label_prefix, parent_prefix, context=context )
def check_param( trans, param, incoming_value, param_values ):
+11 -18
View File
@@ -58,6 +58,7 @@ class ToolParameter( object, Dictifiable ):
self.name = ToolParameter.parse_name( input_source )
self.type = input_source.get("type")
self.hidden = input_source.get("hidden", False)
self.optional = input_source.parse_optional()
self.is_dynamic = False
self.label = input_source.parse_label()
self.help = input_source.parse_help()
@@ -66,7 +67,6 @@ class ToolParameter( object, Dictifiable ):
self.sanitizer = ToolParameterSanitizer.from_element( sanitizer_elem )
else:
self.sanitizer = None
self.html = "no html set"
try:
# These don't do anything right? These we should
# delete these two lines and eliminate checks for
@@ -75,9 +75,6 @@ class ToolParameter( object, Dictifiable ):
self.condition = input_source.elem().get( "condition", None )
except Exception:
self.repeat = None
# Optional DataToolParameters are used in tools like GMAJ and LAJ
self.optional = input_source.parse_optional()
self.validators = []
for elem in input_source.parse_validator_elems():
self.validators.append( validation.Validator.from_element( self, elem ) )
@@ -89,10 +86,7 @@ class ToolParameter( object, Dictifiable ):
def get_label( self ):
"""Return user friendly name for the parameter"""
if self.label:
return self.label
else:
return self.name
return self.label if self.label else self.name
def get_html_field( self, trans=None, value=None, other_values={} ):
raise TypeError( "Abstract Method" )
@@ -191,10 +185,9 @@ class ToolParameter( object, Dictifiable ):
return value
def validate( self, value, trans=None ):
if value == "" and self.optional:
return
for validator in self.validators:
validator.validate( value, trans )
if value is not '' or not self.optional:
for validator in self.validators:
validator.validate( value, trans )
def to_dict( self, trans, view='collection', value_mapper=None, other_values={} ):
""" to_dict tool parameter. This can be overridden by subclasses. """
@@ -211,23 +204,23 @@ class ToolParameter( object, Dictifiable ):
def build( cls, tool, param ):
"""Factory method to create parameter of correct type"""
param_name = cls.parse_name( param )
param_type = param.get("type")
param_type = param.get( 'type' )
if not param_type:
raise ValueError( "Tool parameter '%s' requires a 'type'" % ( param_name ) )
elif param_type not in parameter_types:
raise ValueError( "Tool parameter '%s' uses an unknown type '%s'" % ( param_name, param_type ) )
else:
return parameter_types[param_type]( tool, param )
return parameter_types[ param_type ]( tool, param )
@classmethod
def parse_name(cls, input_source):
name = input_source.get("name", None)
name = input_source.get( 'name' )
if name is None:
argument = input_source.get("argument")
argument = input_source.get( 'argument' )
if argument:
name = argument.lstrip("-")
name = argument.lstrip( '-' )
else:
raise ValueError("Tool parameter must specify a name.")
raise ValueError( "Tool parameter must specify a name." )
return name
+9 -57
View File
@@ -9,8 +9,6 @@ import os
import StringIO
import unicodedata
from six import text_type
from basic import ToolParameter
from galaxy.datatypes import sniff
from galaxy.util import inflector
from galaxy.util import relpath
@@ -113,15 +111,6 @@ class Repeat( Group ):
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
for i, d in enumerate( value ):
for input in self.inputs.itervalues():
new_prefix = prefix + "%s_%d|" % ( self.name, i )
if isinstance( input, ToolParameter ):
callback( new_prefix, input, d[input.name], parent=d )
else:
input.visit_inputs( new_prefix, d[input.name], callback )
def get_initial_value( self, trans, context ):
rval = []
for i in range( self.default ):
@@ -177,13 +166,6 @@ class Section( Group ):
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
for input in self.inputs.itervalues():
if isinstance( input, ToolParameter ):
callback( prefix, input, value[input.name], parent=value )
else:
input.visit_inputs( prefix, value[input.name], callback )
def get_initial_value( self, trans, context ):
rval = {}
child_context = ExpressionContext( rval, context )
@@ -289,15 +271,6 @@ class UploadDataset( Group ):
rval.append( rval_dict )
return rval
def visit_inputs( self, prefix, value, callback ):
for i, d in enumerate( value ):
for input in self.inputs.itervalues():
new_prefix = prefix + "%s_%d|" % ( self.name, i )
if isinstance( input, ToolParameter ):
callback( new_prefix, input, d[input.name], parent=d )
else:
input.visit_inputs( new_prefix, d[input.name], callback )
def get_initial_value( self, trans, context ):
d_type = self.get_datatype( trans, context )
rval = []
@@ -565,9 +538,9 @@ class Conditional( Group ):
def value_to_basic( self, value, app ):
rval = dict()
current_case = rval['__current_case__'] = value['__current_case__']
rval[ self.test_param.name ] = self.test_param.value_to_basic( value[ self.test_param.name ], app )
for input in self.cases[current_case].inputs.itervalues():
current_case = rval[ '__current_case__' ] = self.get_current_case( value[ self.test_param.name ] )
for input in self.cases[ current_case ].inputs.itervalues():
if input.name in value: # parameter might be absent in unverified workflow
rval[ input.name ] = input.value_to_basic( value[ input.name ], app )
return rval
@@ -575,37 +548,20 @@ class Conditional( Group ):
def value_from_basic( self, value, app, ignore_errors=False ):
rval = dict()
try:
current_case = rval['__current_case__'] = value['__current_case__']
# Test param
if ignore_errors and self.test_param.name not in value:
# If ignoring errors, do nothing. However this is potentially very
# problematic since if we are missing the value of test param,
# the entire conditional is wrong.
pass
else:
rval[ self.test_param.name ] = self.test_param.value_from_basic( value[ self.test_param.name ], app, ignore_errors )
rval[ self.test_param.name ] = self.test_param.value_from_basic( value.get( self.test_param.name ), app, ignore_errors )
current_case = rval[ '__current_case__' ] = self.get_current_case( rval[ self.test_param.name ] )
# Inputs associated with current case
for input in self.cases[current_case].inputs.itervalues():
if ignore_errors and input.name not in value:
# If we do not have a value, and are ignoring errors, we simply
# do nothing. There will be no value for the parameter in the
# conditional's values dictionary.
pass
else:
for input in self.cases[ current_case ].inputs.itervalues():
# If we do not have a value, and are ignoring errors, we simply
# do nothing. There will be no value for the parameter in the
# conditional's values dictionary.
if not ignore_errors or input.name in value:
rval[ input.name ] = input.value_from_basic( value[ input.name ], app, ignore_errors )
except Exception, e:
if not ignore_errors:
raise e
return rval
def visit_inputs( self, prefix, value, callback ):
current_case = value['__current_case__']
for input in self.cases[current_case].inputs.itervalues():
if isinstance( input, ToolParameter ):
callback( prefix, input, value[input.name], parent=value )
else:
input.visit_inputs( prefix, value[input.name], callback )
def get_initial_value( self, trans, context ):
# State for a conditional is a plain dictionary.
rval = {}
@@ -633,10 +589,6 @@ class Conditional( Group ):
cond_dict[ "test_param" ] = nested_to_dict( self.test_param )
return cond_dict
@property
def is_job_resource_conditional(self):
return self.name == "__job_resource"
class ConditionalWhen( object, Dictifiable ):
dict_collection_visible_keys = ( 'value', )
+1 -1
View File
@@ -345,7 +345,7 @@ def __cleanup_param_values( inputs, values ):
# Scrub dynamic resource related parameters from workflows,
# they cause problems and the workflow probably should include
# their state in workflow encoding.
if input.is_job_resource_conditional:
if input.name == '__job_resource':
if input.name in values:
del values[input.name]
return
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -439,7 +439,7 @@ if wf_parms:
## <div class="form-row"><input type="submit" name="${step.id}|${prefix}${input.name}_add" value="Add new ${input.title}" /></div>
</div>
%elif input.type == "conditional":
%if input.is_job_resource_conditional:
%if input.name == '__job_resource':
<% continue %>
%endif
<% group_values = values[input.name] %>
+24 -40
View File
@@ -46,7 +46,6 @@ steps:
seed_source:
seed_source_selector: set_seed
seed: asdf
__current_case__: 1
label: nested_workflow
connect:
inner_input: first_cat#out_file1
@@ -165,7 +164,7 @@ class BaseWorkflowsApiTestCase( api.ApiTestCase, ImporterGalaxyInterface ):
invocation_details = invocation_details_response.json()
return invocation_details
def _run_jobs( self, has_workflow, history_id=None, wait=True, source_type=None, jobs_descriptions=None ):
def _run_jobs( self, has_workflow, history_id=None, wait=True, source_type=None, jobs_descriptions=None, expected_response=200, assert_ok=True ):
def read_test_data(test_dict):
test_data_resolver = TestDataResolver()
filename = test_data_resolver.get_filename(test_dict["value"])
@@ -238,33 +237,26 @@ class BaseWorkflowsApiTestCase( api.ApiTestCase, ImporterGalaxyInterface ):
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
url = "workflows/%s/usage" % ( workflow_id )
invocation_response = self._post( url, data=workflow_request )
self._assert_status_code_is( invocation_response, 200 )
self._assert_status_code_is( invocation_response, expected_response )
invocation = invocation_response.json()
invocation_id = invocation[ "id" ]
# Wait for workflow to become fully scheduled and then for all jobs
# complete.
if wait:
self._wait_for_workflow( workflow_id, invocation_id, history_id )
jobs = self._history_jobs( history_id )
return RunJobsSummary(
history_id=history_id,
workflow_id=workflow_id,
invocation_id=invocation_id,
inputs=inputs,
jobs=jobs,
)
def wait_for_invocation( self, workflow_id, invocation_id ):
self.workflow_populator.wait_for_invocation( workflow_id, invocation_id )
invocation_id = invocation.get( 'id' )
if invocation_id:
# Wait for workflow to become fully scheduled and then for all jobs
# complete.
if wait:
self.workflow_populator.wait_for_workflow( workflow_id, invocation_id, history_id, assert_ok=assert_ok )
jobs = self._history_jobs( history_id )
return RunJobsSummary(
history_id=history_id,
workflow_id=workflow_id,
invocation_id=invocation_id,
inputs=inputs,
jobs=jobs,
)
def _history_jobs( self, history_id ):
return self._get("jobs", { "history_id": history_id, "order_by": "create_time" } ).json()
def _wait_for_workflow( self, workflow_id, invocation_id, history_id, assert_ok=True ):
""" Wait for a workflow invocation to completely schedule and then history
to be complete. """
self.workflow_populator.wait_for_workflow(workflow_id, invocation_id, history_id, assert_ok=assert_ok)
# Workflow API TODO:
# - Allow history_id as param to workflow run action. (hist_id)
@@ -971,7 +963,6 @@ steps:
seed_source:
seed_source_selector: set_seed
seed: asdf
__current_case__: 1
test_data:
test_input: "hello world"
""", history_id=history_id, wait=False)
@@ -1009,13 +1000,13 @@ test_data:
type: raw
""", history_id=history_id, wait=True)
time.sleep(10)
self.wait_for_invocation( run_summary.workflow_id, run_summary.invocation_id )
self.workflow_populator.wait_for_invocation( run_summary.workflow_id, run_summary.invocation_id )
jobs = self._history_jobs( history_id )
assert len(jobs) == 1
def test_run_with_validated_parameter_connection_invalid( self ):
history_id = self.dataset_populator.new_history()
run_summary = self._run_jobs("""
self._run_jobs("""
class: GalaxyWorkflow
inputs:
- label: text_input
@@ -1030,10 +1021,7 @@ test_data:
text_input:
value: ""
type: raw
""", history_id=history_id, wait=False)
self.wait_for_invocation( run_summary.workflow_id, run_summary.invocation_id )
jobs = self._history_jobs( history_id )
assert len(jobs) == 0
""", history_id=history_id, wait=True, assert_ok=False )
def test_run_with_text_connection( self ):
history_id = self.dataset_populator.new_history()
@@ -1055,7 +1043,6 @@ steps:
seed_source_selector: set_seed
seed:
$link: text_input
__current_case__: 1
test_data:
data_input:
value: 1.bed
@@ -1070,9 +1057,9 @@ test_data:
self.assertEquals("chr5\t131424298\t131424460\tCCDS4149.1_cds_0_0_chr5_131424299_f\t0\t+\n", content)
def wait_for_invocation_and_jobs( self, history_id, workflow_id, invocation_id, assert_ok=True ):
self.wait_for_invocation( workflow_id, invocation_id )
self.workflow_populator.wait_for_invocation( workflow_id, invocation_id )
time.sleep(.5)
self.dataset_populator.wait_for_history( history_id, assert_ok=True )
self.dataset_populator.wait_for_history( history_id, assert_ok=assert_ok )
time.sleep(.5)
def test_cannot_run_inaccessible_workflow( self ):
@@ -1251,7 +1238,7 @@ steps:
self.dataset_populator.wait_for_history( history_id )
self.__review_paused_steps( workflow_id, invocation_id, order_index=2, action=True )
self._wait_for_workflow( workflow_id, invocation_id, history_id )
self.workflow_populator.wait_for_workflow( workflow_id, invocation_id, history_id )
time.sleep( 1 )
content = self.dataset_populator.get_history_dataset_details( history_id )
assert content[ "name" ] == "foo was replaced", content[ "name" ]
@@ -1363,17 +1350,14 @@ steps:
@skip_without_tool( "validation_repeat" )
def test_workflow_import_state_validation_1( self ):
history_id = self.dataset_populator.new_history()
run_summary = self._run_jobs("""
self._run_jobs("""
class: GalaxyWorkflow
steps:
- tool_id: validation_repeat
state:
r2:
- text: ""
""", history_id=history_id, wait=False)
self.wait_for_invocation( run_summary.workflow_id, run_summary.invocation_id )
jobs = self._history_jobs( history_id )
assert len(jobs) == 0
""", history_id=history_id, wait=False, expected_response=400 )
def _run_validation_workflow_with_substitions( self, substitions ):
workflow = self.workflow_populator.load_workflow_from_resource( "test_workflow_validation_1" )
-2
View File
@@ -33,7 +33,6 @@ steps:
seed_source:
seed_source_selector: set_seed
seed: asdf
__current_case__: 1
""")
workflow = self._get("workflows/%s/download" % workflow_id).json()
@@ -180,7 +179,6 @@ steps:
seed_source:
seed_source_selector: set_seed
seed: asdf
__current_case__: 1
label: nested_workflow
connect:
inner_input: first_cat#out_file1