root/galaxy-central/lib/galaxy/tools/parameters/validation.py

リビジョン 2, 11.3 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1"""
2Classes related to parameter validation.
3"""
4
5import os, re, logging
6from elementtree.ElementTree import XML
7from galaxy import model
8
9log = logging.getLogger( __name__ )
10
11class LateValidationError( Exception ):
12    def __init__( self, message ):
13        self.message = message
14
15class Validator( object ):
16    """
17    A validator checks that a value meets some conditions OR raises ValueError
18    """
19    @classmethod
20    def from_element( cls, param, elem ):
21        type = elem.get( 'type', None )
22        assert type is not None, "Required 'type' attribute missing from validator"
23        return validator_types[type].from_element( param, elem )
24    def validate( self, value, history=None ):
25        raise TypeError( "Abstract Method" )
26       
27class RegexValidator( Validator ):
28    """
29    Validator that evaluates a regular expression
30   
31    >>> from galaxy.tools.parameters import ToolParameter
32    >>> p = ToolParameter.build( None, XML( '''
33    ... <param name="blah" type="text" size="10" value="10">
34    ...     <validator type="regex" message="Not gonna happen">[Ff]oo</validator>
35    ... </param>
36    ... ''' ) )
37    >>> t = p.validate( "Foo" )
38    >>> t = p.validate( "foo" )
39    >>> t = p.validate( "Fop" )
40    Traceback (most recent call last):
41        ...
42    ValueError: Not gonna happen
43    """
44    @classmethod
45    def from_element( cls, param, elem ):
46        return cls( elem.get( 'message' ), elem.text )
47    def __init__( self, message, expression ):
48        self.message = message
49        # Compile later. RE objects used to not be thread safe. Not sure about
50        # the sre module.
51        self.expression = expression 
52    def validate( self, value, history=None ):
53        if re.match( self.expression, value ) is None:
54            raise ValueError( self.message )
55       
56class ExpressionValidator( Validator ):
57    """
58    Validator that evaluates a python expression using the value
59   
60    >>> from galaxy.tools.parameters import ToolParameter
61    >>> p = ToolParameter.build( None, XML( '''
62    ... <param name="blah" type="text" size="10" value="10">
63    ...     <validator type="expression" message="Not gonna happen">value.lower() == "foo"</validator>
64    ... </param>
65    ... ''' ) )
66    >>> t = p.validate( "Foo" )
67    >>> t = p.validate( "foo" )
68    >>> t = p.validate( "Fop" )
69    Traceback (most recent call last):
70        ...
71    ValueError: Not gonna happen
72    """
73    @classmethod
74    def from_element( cls, param, elem ):
75        return cls( elem.get( 'message' ), elem.text, elem.get( 'substitute_value_in_message' ) )
76    def __init__( self, message, expression, substitute_value_in_message ):
77        self.message = message
78        self.substitute_value_in_message = substitute_value_in_message
79        # Save compiled expression, code objects are thread safe (right?)
80        self.expression = compile( expression, '<string>', 'eval' )
81    def validate( self, value, history=None ):
82        if not( eval( self.expression, dict( value=value ) ) ):
83            message = self.message
84            if self.substitute_value_in_message:
85                message = message % value
86            raise ValueError( message )
87       
88class InRangeValidator( Validator ):
89    """
90    Validator that ensures a number is in a specific range
91   
92    >>> from galaxy.tools.parameters import ToolParameter
93    >>> p = ToolParameter.build( None, XML( '''
94    ... <param name="blah" type="integer" size="10" value="10">
95    ...     <validator type="in_range" message="Not gonna happen" min="10" max="20"/>
96    ... </param>
97    ... ''' ) )
98    >>> t = p.validate( 10 )
99    >>> t = p.validate( 15 )
100    >>> t = p.validate( 20 )
101    >>> t = p.validate( 21 )
102    Traceback (most recent call last):
103        ...
104    ValueError: Not gonna happen
105    """
106    @classmethod
107    def from_element( cls, param, elem ):
108        return cls( elem.get( 'message', None ), elem.get( 'min', '-inf' ), elem.get( 'max', '+inf' ) )
109    def __init__( self, message, range_min, range_max ):
110        self.min = float( range_min )
111        self.max = float( range_max ) 
112        self.message = message or ( "Value must be between %f and %f" % ( self.min, self.max ) ) 
113    def validate( self, value, history=None ):
114        if not( self.min <= float( value ) <= self.max ):
115            raise ValueError( self.message )   
116       
117class LengthValidator( Validator ):
118    """
119    Validator that ensures the length of the provided string (value) is in a specific range
120
121    >>> from galaxy.tools.parameters import ToolParameter
122    >>> p = ToolParameter.build( None, XML( '''
123    ... <param name="blah" type="text" size="10" value="foobar">
124    ...     <validator type="length" min="2" max="8"/>
125    ... </param>
126    ... ''' ) )
127    >>> t = p.validate( "foo" )
128    >>> t = p.validate( "bar" )
129    >>> t = p.validate( "f" )
130    Traceback (most recent call last):
131        ...
132    ValueError: Must have length of at least 2
133    >>> t = p.validate( "foobarbaz" )
134    Traceback (most recent call last):
135        ...
136    ValueError: Must have length no more than 8
137    """
138    @classmethod
139    def from_element( cls, param, elem ):
140        return cls( elem.get( 'message', None ), elem.get( 'min', None ), elem.get( 'max', None ) )
141    def __init__( self, message, length_min, length_max ):
142        self.message = message
143        if length_min is not None:
144            length_min = int( length_min )
145        if length_max is not None:
146            length_max = int( length_max )
147        self.min = length_min
148        self.max = length_max
149    def validate( self, value, history=None ):
150        if self.min is not None and len( value ) < self.min:
151            raise ValueError( self.message or ( "Must have length of at least %d" % self.min ) )
152        if self.max is not None and len( value ) > self.max:
153            raise ValueError( self.message or ( "Must have length no more than %d" % self.max ) )
154
155class DatasetOkValidator( Validator ):
156    """
157    Validator that checks if a dataset is in an 'ok' state
158    """
159    def __init__( self, message=None ):
160        self.message = message
161    @classmethod
162    def from_element( cls, param, elem ):
163        return cls( elem.get( 'message', None ) )
164    def validate( self, value, history=None ):
165        if value and value.state != model.Dataset.states.OK:
166            if self.message is None:
167                self.message = "The selected dataset is still being generated, select another dataset or wait until it is completed"
168            raise ValueError( self.message )
169
170class MetadataValidator( Validator ):
171    """
172    Validator that checks for missing metadata
173    """
174    def __init__( self, message = None, check = "", skip = "" ):
175        self.message = message
176        self.check = check.split( "," )
177        self.skip = skip.split( "," )
178    @classmethod
179    def from_element( cls, param, elem ):
180        return cls( message=elem.get( 'message', None ), check=elem.get( 'check', "" ), skip=elem.get( 'skip', "" ) )
181    def validate( self, value, history=None ):
182        if value and value.missing_meta( check = self.check, skip = self.skip ):
183            if self.message is None:
184                self.message = "Metadata missing, click the pencil icon in the history item to edit / save the metadata attributes"
185            raise ValueError( self.message )
186
187class UnspecifiedBuildValidator( Validator ):
188    """
189    Validator that checks for missing metadata
190    """
191    def __init__( self, message=None ):
192        if message is None:
193            self.message = "Unspecified genome build, click the pencil icon in the history item to set the genome build"
194        else:
195            self.message = message
196    @classmethod
197    def from_element( cls, param, elem ):
198        return cls( elem.get( 'message', None ) )
199    def validate( self, value, history=None ):
200        #if value is None, we cannot validate
201        if value:
202            dbkey = value.metadata.dbkey
203            if isinstance( dbkey, list ):
204                dbkey = dbkey[0]
205            if dbkey == '?':
206                raise ValueError( self.message )
207
208class NoOptionsValidator( Validator ):
209    """Validator that checks for empty select list"""
210    def __init__( self, message=None ):
211        self.message = message
212    @classmethod
213    def from_element( cls, param, elem ):
214        return cls( elem.get( 'message', None ) )
215    def validate( self, value, history=None ):
216        if value is None:
217            if self.message is None:
218                self.message = "No options available for selection"
219            raise ValueError( self.message )
220
221class EmptyTextfieldValidator( Validator ):
222    """Validator that checks for empty text field"""
223    def __init__( self, message=None ):
224        self.message = message
225    @classmethod
226    def from_element( cls, param, elem ):
227        return cls( elem.get( 'message', None ) )
228    def validate( self, value, history=None ):
229        if value == '':
230            if self.message is None:
231                self.message = "Field requires a value"
232            raise ValueError( self.message )
233
234class MetadataInFileColumnValidator( Validator ):
235    """
236    Validator that checks if the value for a dataset's metadata item exists in a file.
237    """
238    @classmethod
239    def from_element( cls, param, elem ):
240        filename = elem.get( "filename", None )
241        if filename:
242            filename = "%s/%s" % ( param.tool.app.config.tool_data_path, filename.strip() )
243        metadata_name = elem.get( "metadata_name", None )
244        if metadata_name:
245            metadata_name = metadata_name.strip()
246        metadata_column = int( elem.get( "metadata_column", 0 ) )
247        message = elem.get( "message", "Value for metadata %s was not found in %s." % ( metadata_name, filename ) )
248        line_startswith = elem.get( "line_startswith", None  )
249        if line_startswith:
250            line_startswith = line_startswith.strip()
251        return cls( filename, metadata_name, metadata_column, message, line_startswith )
252    def __init__( self, filename, metadata_name, metadata_column, message="Value for metadata not found.", line_startswith=None ):
253        self.metadata_name = metadata_name
254        self.message = message
255        self.valid_values = []
256        for line in open( filename ):
257            if line_startswith is None or line.startswith( line_startswith ):
258                fields = line.split( '\t' )
259                if metadata_column < len( fields ):
260                    self.valid_values.append( fields[metadata_column].strip() )
261    def validate( self, value, history = None ):
262        if not value: return
263        if hasattr( value, "metadata" ):
264            if value.metadata.spec[self.metadata_name].param.to_string( value.metadata.get( self.metadata_name ) ) in self.valid_values:
265                return
266        raise ValueError( self.message )
267
268validator_types = dict( expression=ExpressionValidator,
269                        regex=RegexValidator,
270                        in_range=InRangeValidator,
271                        length=LengthValidator,
272                        metadata=MetadataValidator,
273                        unspecified_build=UnspecifiedBuildValidator,
274                        no_options=NoOptionsValidator,
275                        empty_field=EmptyTextfieldValidator,
276                        dataset_metadata_in_file=MetadataInFileColumnValidator,
277                        dataset_ok_validator=DatasetOkValidator )
278                       
279def get_suite():
280    """Get unittest suite for this module"""
281    import doctest, sys
282    return doctest.DocTestSuite( sys.modules[__name__] )
283
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。