1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """
24 Parse a Biskit settings file.
25 """
26
27 import os
28 import user
29 import sys
30 import ConfigParser
31
32 import Biskit as B
33 import Biskit.tools as T
34
36 pass
37
39 pass
40
42 pass
43
45 pass
46
48 pass
49
51 pass
52
54 pass
55
56
58 """
59 Change ConfigParser so that it doesn't convert option names to lower case.
60 """
62 return optionstr
63
65 """
66 Simple container for a single parameter
67 """
68
69 NORMAL = 'NORMAL'
70 PATH = 'PATHS'
71 BIN = 'BINARIES'
72
73 - def __init__( self, name=None, value=None, vtype=str, comment=None,
74 error=None, section=NORMAL ):
75 self.name = name
76 self.value = value
77 self.vtype = vtype
78 self.comment = comment
79 self.error = error
80 self.section = section
81
83 """
84 Recast value to a new type. Value None remains unchanged.
85 @param vtype: new type for value
86 @type vtype: type
87 @raise InvalidValue: if current value is incompatible with vtype
88 """
89 try:
90 if not self.value is None:
91 self.value = vtype( self.value )
92 self.vtype = vtype
93 except ValueError, e:
94 raise InvalidValue, '%s: cannot convert "%s" to %r.' %\
95 (self.name,self.value,vtype)
96
98 error = ''
99 if self.error: error = '(!)'
100
101 comment = self.comment or ''
102 if comment: comment = ' # '+comment
103
104 return '%s%s = %s%s(%s)%s%s' %\
105 (error, self.name, tab, self.vtype.__name__, str(self.value),\
106 tab, comment)
107
110
112 """
113 Compare Setting instances by their name.
114 @return: 1,0,-1
115 @rtype: int
116 """
117 if isinstance( other, self.__class__ ):
118 return cmp( self.name, other.name )
119
120 return cmp( self, other )
121
122
143
144
146 """
147 A config file parser on steroids -- performs the following tasks:
148
149 1. read a ini-style settings file
150 2. type-cast options (e.g. of the form int-some_name into int(some_name))
151 3. validate that all entries of section [PATHS] point to existing paths
152 4. absolutize all valid paths
153 5. validate that all entries of section [BINARIES] point to binaries
154 """
155
157
158 self.f_ini = T.absfile( ini )
159 self.result = {}
160
161
163 """
164 @param v: potential path name
165 @type v: str
166
167 @return: validated absolute Path
168 @rtype : str
169
170 @raise InvalidPath: if path is not found
171 """
172 try:
173 v = T.absfile( v )
174 if not v or not os.path.exists( v ):
175 raise InvalidPath, 'invalid path %r' % v
176
177 return v
178
179 except InvalidPath, e:
180 raise
181 except Exception, e:
182 raise InvalidPath, 'error during path validation: %r' % str(e)
183
184
186 """
187 @param v: potential binary path
188 @type v: str
189
190 @return: validated absolute path to existing binary
191 @rtype : str
192
193 @raise InvalidBinary: if path is not found
194 """
195 try:
196 if not v:
197 raise IOError, 'empty path'
198
199 return T.absbinary( v )
200
201 except IOError, msg:
202 raise InvalidBinary( str(msg) )
203
204
205 - def __type( self, option, default=str ):
206 """
207 Extract type from option name.
208
209 @param option: name of parameter
210 @type option: str
211 @param default: default type [str]
212 @type default: type
213
214 @return: type, stripped option name (e.g. 'int_var1' -> int, 'var1')
215 @rtype: type, str
216
217 @raise TypeError: if type cannot be interpreted
218 """
219 t = default
220 o = option
221
222 if option.count('-') > 0:
223
224 try:
225
226 splt = option.split('-')
227
228 s = splt[0]
229 o = ''.join( splt[1:] )
230
231 t = eval( s )
232
233 if not type(t) is type:
234 raise TypeError, '%s is not a valid type' % s
235
236 except Exception, e:
237 raise TypeError, 'Cannot extract type from %s: %r'\
238 % option, e
239
240 return t, o
241
242
244 """
245 @param option: option name
246 @type option: str
247
248 @param value: option value
249 @type value: str
250
251 @param section: which section are we working on
252 @type section: str
253
254 @return: new setting
255 @rtype: Setting
256
257 @raise SettingsError: InvalidType or Value
258 """
259 r = Setting( section=section )
260
261 try:
262
263 x = value.split('#')
264 r.value = x[0].strip() or None
265
266 if len(x) > 1:
267 r.comment = ''.join( x[1:] )
268
269 vtype, r.name = self.__type( option )
270 r.typeCast( vtype )
271
272 if section == Setting.PATH:
273 r.value = self.__validPath( r.value )
274
275 if section == Setting.BIN:
276 r.value = self.__validBinary( r.value )
277
278 except SettingsWarning, e:
279 r.error = str(e)
280
281 return r
282
283
285 """
286 @param items: section comming from ConfigParser
287 @type items: [ ( str, str ) ]
288
289 @param section: which config section are we working on?
290 @type section: str
291
292 @return: validated path values
293 @rtype : dict, {str: Setting}
294 """
295 r = {}
296
297 for name, value in items:
298
299 s = self.__process( name, value, section )
300
301 r[ s.name ] = s
302
303 if verbose and s.error:
304 B.EHandler.warning( s.error, trace=0, error=0 )
305
306 return r
307
308
309 - def parse( self, verbose=False ):
310 """
311 @param verbose: print warning messages via Biskit.EHandler
312 @type verbose: bool
313
314 @return: dict of type-cast params contained in fini
315 @rtype: dict, {str: Setting}
316
317 @raise IOError: if the settings file does not exist
318 @raise SettingsError: (InvalidFile, InvalidValue, InvalidType)
319 """
320 try:
321
322 c = CaseSensitiveConfigParser()
323
324 if c.read( self.f_ini ) != [ self.f_ini ]:
325 raise IOError, 'Settings file %s not found.' % self.f_ini
326
327 for section in c.sections():
328
329 self.result.update(
330 self.__processSection( c.items(section), section) )
331
332 except ConfigParser.Error, e:
333 raise InvalidFile, 'Error parsing settings file %s: ' %\
334 self.f_ini + str(e)
335
336 return self.result
337
350
351
352
353
354
356 """
357 Test class
358 """
359
361 """
362 run function test
363
364 @return: 1
365 @rtype: int
366 """
367 p = SettingsParser( T.projectRoot()+'/external/defaults/settings.cfg')
368
369 p.parse()
370
371 t = p.result.get('testparam', Setting())
372
373 globals().update( locals() )
374
375 return t.name, t.value
376
377
379 """
380 Precalculated result to check for consistent performance.
381
382 @return: 1
383 @rtype: int
384 """
385 return 'testparam', 42
386
387
388
389 if __name__ == '__main__':
390
391 test = Test()
392
393 assert test.run( ) == test.expected_result()
394