Revise late validation condition by considering the availability of options

This commit is contained in:
guerler
2015-04-08 17:30:38 -04:00
parent 291cde77c5
commit d0d1f1233a
2 changed files with 29 additions and 96 deletions
+4 -6
View File
@@ -59,12 +59,10 @@ def check_param( trans, param, incoming_value, param_values, source='html' ):
value = param.from_html( value, trans, param_values )
else:
value = param.from_json( value, trans, param_values )
# Only validate if late validation is not needed
if not param.need_late_validation( trans, param_values ):
# Allow the value to be converted if necessary
filtered_value = param.filter_value( value, trans, param_values )
# Then do any further validation on the value
param.validate( filtered_value, trans.history )
# Allow the value to be converted if necessary
filtered_value = param.filter_value( value, trans, param_values )
# Then do any further validation on the value
param.validate( filtered_value, trans.history )
elif value is None and isinstance( param, SelectToolParameter ):
# An empty select list or column list
param.validate( value, trans.history )
+25 -90
View File
@@ -841,9 +841,9 @@ class SelectToolParameter( ToolParameter ):
def get_html_field( self, trans=None, value=None, context={} ):
# Dynamic options are not yet supported in workflow, allow
# specifying the value as text for now.
if self.need_late_validation( trans, context ):
if value is not None:
assert isinstance( value, UnvalidatedValue ), "Late validation needed for '%s', but provided value (%s) is not of type UnvalidatedValue (%s)." % ( self.name, value, type( value ) )
options = self.get_options( trans, context )
if self.need_late_validation( trans, options ):
if isinstance( value, UnvalidatedValue ):
value = value.value
if self.multiple:
if value is None:
@@ -859,7 +859,6 @@ class SelectToolParameter( ToolParameter ):
# We could have an unvalidated value here when e.g. running a workflow.
value = [ val.value if isinstance( val, UnvalidatedValue ) else val for val in value ]
field = form_builder.SelectField( self.name, self.multiple, self.display, self.refresh_on_change, refresh_on_change_values=self.refresh_on_change_values )
options = self.get_options( trans, context )
for text, optval, selected in options:
if isinstance( optval, UnvalidatedValue ):
optval = optval.value
@@ -870,7 +869,8 @@ class SelectToolParameter( ToolParameter ):
return field
def from_html( self, value, trans=None, context={} ):
if self.need_late_validation( trans, context ):
legal_values = self.get_legal_values( trans, context )
if self.need_late_validation( trans, legal_values ):
if self.multiple:
# While it is generally allowed that a select value can be '',
# we do not allow this to be the case in a dynamically
@@ -885,7 +885,6 @@ class SelectToolParameter( ToolParameter ):
# use \r\n to separate lines.
value = value.split()
return UnvalidatedValue( value )
legal_values = self.get_legal_values( trans, context )
if not legal_values and self.optional:
return None
if not legal_values:
@@ -952,61 +951,22 @@ class SelectToolParameter( ToolParameter ):
return UnvalidatedValue( value["value"] )
return super( SelectToolParameter, self ).value_from_basic( value, app, ignore_errors=ignore_errors )
def need_late_validation( self, trans, context ):
def need_late_validation( self, trans, options ):
"""
Determine whether we need to wait to validate this parameters value
given the current state. For parameters with static options this is
always false (can always validate immediately). For parameters with
dynamic options, we need to check whether the other parameters which
determine what options are valid have been set. For the old style
dynamic options which do not specify dependencies, this is always true
(must validate at runtime).
always false (can always validate immediately).
"""
# Option list is statically defined, never need late validation
if not self.is_dynamic:
return False
# Old style dynamic options, no dependency information so there isn't
# a lot we can do: if we're dealing with workflows, have to assume
# late validation no matter what.
if self.dynamic_options is not None and ( trans is None or trans.workflow_building_mode ):
if len(list(options)) == 0 and (trans is None or trans.workflow_building_mode):
return True
# If we got this far, we can actually look at the dependencies
# to see if their values will not be available until runtime.
for dep_name in self.get_dependencies():
# This may not be completely correct, but it's possible to go
# through the layers indicated below without assigning dep_value,
# which is even worse. TODO fix it?
dep_value = None
if dep_name in context:
dep_value = context[ dep_name ]
else:
# Quick hack to check deeper in the context.
# TODO: Context should really be scoped and the correct subset passed along.
# This happens specifically in all the GATK tools, the way the reference genome is handled.
for layer in context.itervalues():
if isinstance( layer, dict ) and self.name in layer and dep_name in layer:
dep_value = layer[dep_name]
# Dependency on a dataset that does not yet exist
if isinstance( dep_value, DummyDataset ):
return True
# Dependency on a value that has not been checked
if isinstance( dep_value, UnvalidatedValue ):
return True
# Dependency on a value that does not yet exist
if isinstance( dep_value, RuntimeValue ):
return True
#dataset not ready yet
if hasattr( self, 'ref_input' ) and isinstance( dep_value, self.tool.app.model.HistoryDatasetAssociation ) and ( dep_value.is_pending or not dep_value.datatype.matches_any( self.ref_input.formats ) ):
return True
# Dynamic, but all dependenceis are known and have values
return False
def get_initial_value( self, trans, context, history=None ):
# More working around dynamic options for workflow
if self.need_late_validation( trans, context ):
options = list( self.get_options( trans, context ) )
if self.need_late_validation( trans, options ):
# Really the best we can do?
return UnvalidatedValue( None )
options = list( self.get_options( trans, context ) )
value = [ optval for _, optval, selected in options if selected ]
if len( value ) == 0:
if not self.multiple and options:
@@ -1255,8 +1215,12 @@ class ColumnListParameter( SelectToolParameter ):
return []
column_list = None
for dataset in util.listify( dataset ):
# Handle columns not available.
if not dataset.metadata.columns:
unavailable = False
if not hasattr(dataset, 'metadata'):
unavailable = True
elif not dataset.metadata.columns:
unavailable = True
if unavailable:
default_column_list = []
if self.accept_default:
default_column_list.append( self.default_value or '1' )
@@ -1316,9 +1280,6 @@ class ColumnListParameter( SelectToolParameter ):
def get_initial_value( self, trans, context, history=None ):
if self.default_value is not None:
# dataset not ready / in workflow / etc
if self.need_late_validation( trans, context ):
return UnvalidatedValue( self.default_value )
return self.default_value
return SelectToolParameter.get_initial_value( self, trans, context )
@@ -1328,33 +1289,6 @@ class ColumnListParameter( SelectToolParameter ):
def get_dependencies( self ):
return [ self.data_ref ]
def need_late_validation( self, trans, context ):
if super( ColumnListParameter, self ).need_late_validation( trans, context ):
return True
if self.data_ref not in context:
return False
# Get the selected dataset if selected
referent = context[ self.data_ref ]
if getattr( referent, 'history_content_type', None ) == "dataset_collection":
# TODO: also check datasets have been populated.
referent = referent.collection.dataset_instances
datasets = util.listify( referent )
for dataset in datasets:
if dataset:
# Check if metadata is available
if not hasattr(dataset, 'metadata'):
return True
# Check if the dataset does not have the expected metadata for columns
if not dataset.metadata.columns:
# Only allow late validation if the dataset is not yet ready
# (since we have reason to expect the metadata to be ready eventually)
if dataset.is_pending or not dataset.datatype.matches_any( self.ref_input.formats ):
return True
# No late validation
return False
def to_dict( self, trans, view='collection', value_mapper=None, other_values={} ):
# call parent to_dict
d = super( ColumnListParameter, self ).to_dict( trans, other_values=other_values)
@@ -1558,9 +1492,9 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
def get_html_field( self, trans=None, value=None, other_values={} ):
# Dynamic options are not yet supported in workflow, allow
# specifying the value as text for now.
if self.need_late_validation( trans, other_values ):
if value is not None:
assert isinstance( value, UnvalidatedValue ), "Late validation needed for '%s', but provided value (%s) is not of type UnvalidatedValue (%s)." % ( self.name, value, type( value ) )
options = self.get_options( trans, value, other_values )
if self.need_late_validation( trans, options ):
if isinstance( value, UnvalidatedValue ):
value = value.value
if self.multiple:
if value is None:
@@ -1570,10 +1504,11 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
return form_builder.TextArea( self.name, value=value )
else:
return form_builder.TextField( self.name, value=(value or "") )
return form_builder.DrillDownField( self.name, self.multiple, self.display, self.refresh_on_change, self.get_options( trans, value, other_values ), value, refresh_on_change_values=self.refresh_on_change_values )
return form_builder.DrillDownField( self.name, self.multiple, self.display, self.refresh_on_change, options, value, refresh_on_change_values=self.refresh_on_change_values )
def from_html( self, value, trans=None, other_values={} ):
if self.need_late_validation( trans, other_values ):
legal_values = self.get_legal_values( trans, other_values )
if self.need_late_validation( trans, legal_values ):
if self.multiple:
if value == '': # No option selected
value = None
@@ -1589,7 +1524,6 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
if not( self.repeat ) and len( value ) > 1:
assert self.multiple, "Multiple values provided but parameter %s is not expecting multiple values" % self.name
rval = []
legal_values = self.get_legal_values( trans, other_values )
assert legal_values, "Parameter %s requires a value, but has no legal values defined" % self.name
for val in value:
if val not in legal_values:
@@ -1645,8 +1579,9 @@ class DrillDownSelectToolParameter( SelectToolParameter ):
initial_values.append( option['value'] )
recurse_options( initial_values, option['options'] )
# More working around dynamic options for workflow
if self.need_late_validation( trans, context ):
# Really the best we can do?
initial_values = []
recurse_options( initial_values, self.get_options( trans=trans, other_values=context ) )
if self.need_late_validation( trans, initial_values ):
return UnvalidatedValue( None )
initial_values = []
recurse_options( initial_values, self.get_options( trans=trans, other_values=context ) )