mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #339 from gregvonkuster/dev
Add new exclude_min and exclude_max attributes to InRangeValidator for numeric tool parameters.
This commit is contained in:
@@ -5,25 +5,32 @@ Classes related to parameter validation.
|
||||
import os, re, logging
|
||||
from xml.etree.ElementTree import XML
|
||||
from galaxy import model
|
||||
from galaxy import util
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class LateValidationError( Exception ):
|
||||
|
||||
def __init__( self, message ):
|
||||
self.message = message
|
||||
|
||||
|
||||
class Validator( object ):
|
||||
"""
|
||||
A validator checks that a value meets some conditions OR raises ValueError
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
type = elem.get( 'type', None )
|
||||
assert type is not None, "Required 'type' attribute missing from validator"
|
||||
return validator_types[type].from_element( param, elem )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
raise TypeError( "Abstract Method" )
|
||||
|
||||
|
||||
class RegexValidator( Validator ):
|
||||
"""
|
||||
Validator that evaluates a regular expression
|
||||
@@ -41,18 +48,22 @@ class RegexValidator( Validator ):
|
||||
...
|
||||
ValueError: Not gonna happen
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message' ), elem.text )
|
||||
|
||||
def __init__( self, message, expression ):
|
||||
self.message = message
|
||||
# Compile later. RE objects used to not be thread safe. Not sure about
|
||||
# the sre module.
|
||||
self.expression = expression
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if re.match( self.expression, value ) is None:
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class ExpressionValidator( Validator ):
|
||||
"""
|
||||
Validator that evaluates a python expression using the value
|
||||
@@ -70,14 +81,17 @@ class ExpressionValidator( Validator ):
|
||||
...
|
||||
ValueError: Not gonna happen
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message' ), elem.text, elem.get( 'substitute_value_in_message' ) )
|
||||
|
||||
def __init__( self, message, expression, substitute_value_in_message ):
|
||||
self.message = message
|
||||
self.substitute_value_in_message = substitute_value_in_message
|
||||
# Save compiled expression, code objects are thread safe (right?)
|
||||
self.expression = compile( expression, '<string>', 'eval' )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if not( eval( self.expression, dict( value=value ) ) ):
|
||||
message = self.message
|
||||
@@ -85,6 +99,7 @@ class ExpressionValidator( Validator ):
|
||||
message = message % value
|
||||
raise ValueError( message )
|
||||
|
||||
|
||||
class InRangeValidator( Validator ):
|
||||
"""
|
||||
Validator that ensures a number is in a specific range
|
||||
@@ -92,10 +107,13 @@ class InRangeValidator( Validator ):
|
||||
>>> from galaxy.tools.parameters import ToolParameter
|
||||
>>> p = ToolParameter.build( None, XML( '''
|
||||
... <param name="blah" type="integer" size="10" value="10">
|
||||
... <validator type="in_range" message="Not gonna happen" min="10" max="20"/>
|
||||
... <validator type="in_range" message="Not gonna happen" min="10" exclude_min="true" max="20"/>
|
||||
... </param>
|
||||
... ''' ) )
|
||||
>>> t = p.validate( 10 )
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Not gonna happen
|
||||
>>> t = p.validate( 15 )
|
||||
>>> t = p.validate( 20 )
|
||||
>>> t = p.validate( 21 )
|
||||
@@ -103,20 +121,51 @@ class InRangeValidator( Validator ):
|
||||
...
|
||||
ValueError: Not gonna happen
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ), elem.get( 'min' ), elem.get( 'max' ) )
|
||||
def __init__( self, message, range_min, range_max ):
|
||||
return cls( elem.get( 'message', None ), elem.get( 'min' ),
|
||||
elem.get( 'max' ), elem.get( 'exclude_min', 'false' ),
|
||||
elem.get( 'exclude_max', 'false' ) )
|
||||
|
||||
def __init__( self, message, range_min, range_max, exclude_min=False, exclude_max=False ):
|
||||
"""
|
||||
When the optional exclude_min and exclude_max attributes are set
|
||||
to true, the range excludes the end points (i.e., min < value < max),
|
||||
while if set to False ( the default), then range includes the end points
|
||||
(1.e., min <= value <= max). Combinations of exclude_min and exclude_max
|
||||
values are allowed.
|
||||
"""
|
||||
self.min = float( range_min if range_min is not None else '-inf' )
|
||||
self.exclude_min = util.asbool( exclude_min )
|
||||
self.max = float( range_max if range_max is not None else 'inf' )
|
||||
self.exclude_max = util.asbool( exclude_max )
|
||||
assert self.min <= self.max, 'min must be less than or equal to max'
|
||||
# Remove unneeded 0s and decimal from floats to make message pretty.
|
||||
self_min_str = str( self.min ).rstrip( '0' ).rstrip( '.' )
|
||||
self_max_str = str( self.max ).rstrip( '0' ).rstrip( '.' )
|
||||
self.message = message or "Value must be between %s and %s" % ( self_min_str, self_max_str )
|
||||
op1 = '>='
|
||||
op2 = '<='
|
||||
if self.exclude_min:
|
||||
op1 = '>'
|
||||
if self.exclude_max:
|
||||
op2 = '<'
|
||||
self.message = message or "Value must be %s %s and %s %s" % ( op1, self_min_str, op2, self_max_str )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if not( self.min <= float( value ) <= self.max ):
|
||||
raise ValueError( self.message )
|
||||
if self.exclude_min:
|
||||
if not self.min < float( value ):
|
||||
raise ValueError( self.message )
|
||||
else:
|
||||
if not self.min <= float( value ):
|
||||
raise ValueError( self.message )
|
||||
if self.exclude_max:
|
||||
if not float( value ) < self.max:
|
||||
raise ValueError( self.message )
|
||||
else:
|
||||
if not float( value ) <= self.max:
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class LengthValidator( Validator ):
|
||||
"""
|
||||
@@ -139,9 +188,11 @@ class LengthValidator( Validator ):
|
||||
...
|
||||
ValueError: Must have length no more than 8
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ), elem.get( 'min', None ), elem.get( 'max', None ) )
|
||||
|
||||
def __init__( self, message, length_min, length_max ):
|
||||
self.message = message
|
||||
if length_min is not None:
|
||||
@@ -150,38 +201,47 @@ class LengthValidator( Validator ):
|
||||
length_max = int( length_max )
|
||||
self.min = length_min
|
||||
self.max = length_max
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if self.min is not None and len( value ) < self.min:
|
||||
raise ValueError( self.message or ( "Must have length of at least %d" % self.min ) )
|
||||
if self.max is not None and len( value ) > self.max:
|
||||
raise ValueError( self.message or ( "Must have length no more than %d" % self.max ) )
|
||||
|
||||
|
||||
class DatasetOkValidator( Validator ):
|
||||
"""
|
||||
Validator that checks if a dataset is in an 'ok' state
|
||||
"""
|
||||
|
||||
def __init__( self, message=None ):
|
||||
self.message = message
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ) )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if value and value.state != model.Dataset.states.OK:
|
||||
if self.message is None:
|
||||
self.message = "The selected dataset is still being generated, select another dataset or wait until it is completed"
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class MetadataValidator( Validator ):
|
||||
"""
|
||||
Validator that checks for missing metadata
|
||||
"""
|
||||
|
||||
def __init__( self, message = None, check = "", skip = "" ):
|
||||
self.message = message
|
||||
self.check = check.split( "," )
|
||||
self.skip = skip.split( "," )
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( message=elem.get( 'message', None ), check=elem.get( 'check', "" ), skip=elem.get( 'skip', "" ) )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if value:
|
||||
if not isinstance( value, model.DatasetInstance ):
|
||||
@@ -191,18 +251,22 @@ class MetadataValidator( Validator ):
|
||||
self.message = "Metadata missing, click the pencil icon in the history item to edit / save the metadata attributes"
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class UnspecifiedBuildValidator( Validator ):
|
||||
"""
|
||||
Validator that checks for dbkey not equal to '?'
|
||||
"""
|
||||
|
||||
def __init__( self, message=None ):
|
||||
if message is None:
|
||||
self.message = "Unspecified genome build, click the pencil icon in the history item to set the genome build"
|
||||
else:
|
||||
self.message = message
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ) )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
#if value is None, we cannot validate
|
||||
if value:
|
||||
@@ -212,36 +276,46 @@ class UnspecifiedBuildValidator( Validator ):
|
||||
if dbkey == '?':
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class NoOptionsValidator( Validator ):
|
||||
"""Validator that checks for empty select list"""
|
||||
|
||||
def __init__( self, message=None ):
|
||||
self.message = message
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ) )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if value is None:
|
||||
if self.message is None:
|
||||
self.message = "No options available for selection"
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class EmptyTextfieldValidator( Validator ):
|
||||
"""Validator that checks for empty text field"""
|
||||
|
||||
def __init__( self, message=None ):
|
||||
self.message = message
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
return cls( elem.get( 'message', None ) )
|
||||
|
||||
def validate( self, value, history=None ):
|
||||
if value == '':
|
||||
if self.message is None:
|
||||
self.message = "Field requires a value"
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class MetadataInFileColumnValidator( Validator ):
|
||||
"""
|
||||
Validator that checks if the value for a dataset's metadata item exists in a file.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
filename = elem.get( "filename", None )
|
||||
@@ -256,6 +330,7 @@ class MetadataInFileColumnValidator( Validator ):
|
||||
if line_startswith:
|
||||
line_startswith = line_startswith.strip()
|
||||
return cls( filename, metadata_name, metadata_column, message, line_startswith )
|
||||
|
||||
def __init__( self, filename, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None ):
|
||||
self.metadata_name = metadata_name
|
||||
self.message = message
|
||||
@@ -265,6 +340,7 @@ class MetadataInFileColumnValidator( Validator ):
|
||||
fields = line.split( '\t' )
|
||||
if metadata_column < len( fields ):
|
||||
self.valid_values.append( fields[metadata_column].strip() )
|
||||
|
||||
def validate( self, value, history = None ):
|
||||
if not value: return
|
||||
if hasattr( value, "metadata" ):
|
||||
@@ -272,10 +348,12 @@ class MetadataInFileColumnValidator( Validator ):
|
||||
return
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
class MetadataInDataTableColumnValidator( Validator ):
|
||||
"""
|
||||
Validator that checks if the value for a dataset's metadata item exists in a file.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_element( cls, param, elem ):
|
||||
table_name = elem.get( "table_name", None )
|
||||
@@ -323,7 +401,6 @@ class MetadataInDataTableColumnValidator( Validator ):
|
||||
return
|
||||
raise ValueError( self.message )
|
||||
|
||||
|
||||
validator_types = dict( expression=ExpressionValidator,
|
||||
regex=RegexValidator,
|
||||
in_range=InRangeValidator,
|
||||
@@ -340,4 +417,3 @@ def get_suite():
|
||||
"""Get unittest suite for this module"""
|
||||
import doctest, sys
|
||||
return doctest.DocTestSuite( sys.modules[__name__] )
|
||||
|
||||
|
||||
Reference in New Issue
Block a user