Package Biskit :: Module SettingsManager
[hide private]
[frames] | no frames]

Source Code for Module Biskit.SettingsManager

  1  ## 
  2  ## Biskit, a toolkit for the manipulation of macromolecular structures 
  3  ## Copyright (C) 2004-2005 Raik Gruenberg & Johan Leckner 
  4  ## 
  5  ## This program is free software; you can redistribute it and/or 
  6  ## modify it under the terms of the GNU General Public License as 
  7  ## published by the Free Software Foundation; either version 2 of the 
  8  ## License, or any later version. 
  9  ## 
 10  ## This program is distributed in the hope that it will be useful, 
 11  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  ## General Public License for more details. 
 14  ## 
 15  ## You find a copy of the GNU General Public License in the file 
 16  ## license.txt along with this program; if not, write to the Free 
 17  ## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 18  ## 
 19  ## 
 20  ## last $Author: leckner $ 
 21  ## last $Date: 2006/12/21 09:33:09 $ 
 22  ## $Revision: 2.10 $ 
 23  """ 
 24  Manage Biskit settings. 
 25  """ 
 26   
 27  import Biskit as B 
 28  import Biskit.tools as T 
 29  import Biskit.SettingsParser as P 
 30   
 31  import user, os 
 32  import ConfigParser 
 33   
34 -class WriteCfgError( P.SettingsError ):
35 pass
36
37 -class SettingsManager:
38 """ 39 SettingsManager merges the parameters from a default and a user 40 configuration file into a python module where they are published as 41 normal fields. The general flow is like this:: 42 43 default.cfg ---[SettingsParser]---\ 44 [SettingsManager]--->[settings] 45 / 46 user.cfg---[SettingsParser]---/ 47 48 See L{P.SettingsParser} 49 See L{B.settings} 50 51 The default configurations should be located in: 52 53 * C{biskit/external/defaults/settings.cfg} --> L{B.settings} 54 * C{biskit/external/defaults/settings_Mod.cfg} --> L{B.Mod.settings} 55 * C{biskit/external/defaults/settings_Dock.cfg} --> L{B.Dock.settings} 56 57 The user configurations are expected in files of the same name in 58 C{~/.biskit/}. 59 """ 60 61 USER_HEADER = """ 62 ## This is a Biskit user configuration file. The parameters in 63 ## this file are overriding the default parameters given in 64 ## %(fdefault)s. 65 ## If missing, Biskit creates a new user configuration file with 66 ## those parameters for which the default value seems 67 ## invalid. The remaining parameters are commented out. 68 69 ## Parameters in this file will be accessible from within python as 70 ## fields of Biskit.settings. For example:: 71 ## 72 ## leaprc = some/path/to/leaprc # some comment 73 ## 74 ## will lead to a variable in Biskit.settings:: 75 ## 76 ## >>> import Biskit.setting as S 77 ## >>> S.leaprc 78 ## >>> 'some/path/to/leaprc' 79 80 ## ...If, and only if, leaprc also exists in the default settings 81 ## file. Parameters that are not listed in the default settings file 82 ## are ignored. 83 84 ## The default type of parameters is str. A prefix to the name like 85 ## 'int-', 'float-', 'bool-', etc. will be interpreted as 86 ## type-casting. For example:: 87 ## 88 ## float-nice_value = 10 # some comment 89 ## 90 ## will lead to a variable in Biskit.settings:: 91 ## 92 ## >>> S.nice_value 93 ## >>> 10.0 94 95 """ 96
97 - def __init__( self, fdefault, fuser, createmissing=False, verbose=1 ):
98 """ 99 @param fdefault: default configuration file 100 @type fdedault: str 101 @param fuser: user configuration file 102 @type fuser: str 103 @param createmissing: create user config file if missing 104 @type createmissing: bool 105 @param verbose: verbosity level (default: 1) 106 @type verbose: 1|0 107 """ 108 self.verbose = verbose 109 self.fdefault = fdefault 110 self.fuser = fuser 111 self.createmissing = createmissing 112 self.fusermissing = not os.path.exists( T.absfile(fuser) ) 113 114 self.settings = [] #: will hold extracted Setting's
115
116 - def __update( self, cfg_default, cfg_user ):
117 """ 118 Override default settings by valid (or equally invalid) user settings. 119 120 @param cfg_default: settings read in from default file 121 @type cfg_default: dict {'str':SettingsParser.Setting} 122 @param cfg_user : settings read in from user config file 123 @type cfg_user : dict {'str':SettingsParser.Setting} 124 125 @return: configuration with valid user settings overriding default ones 126 @rtype: dict {'str':SettingsParser.Setting} 127 """ 128 r = {} 129 errors = {} 130 131 for name, default in cfg_default.items(): 132 133 next = cfg_user.get( name, default ) 134 135 if next.error > default.error: 136 137 if self.verbose: B.EHandler.warning(\ 138 'User setting %s is reset to default (%r),\n\treason: %s'\ 139 % (name, default.value, next.error)\ 140 + '\n\tPlease check %s!' % self.fuser ) 141 142 next = default 143 144 r[name] = next 145 146 return r
147 148
149 - def collectSettings( self ):
150 """ 151 Parse and combine default and user-defined config files. 152 """ 153 try: 154 pdefault = P.SettingsParser( self.fdefault ) 155 cdefault = pdefault.parse() 156 157 try: 158 puser = P.SettingsParser( self.fuser ) 159 cuser = puser.parse() 160 161 except IOError, e: 162 if self.verbose: B.EHandler.warning( 163 'Could not find file with user-defined settings in %s' \ 164 % self.fuser, trace=0, error=0) 165 166 cuser = {} 167 168 self.settings = self.__update( cdefault, cuser ) 169 170 except P.SettingsError, e: 171 B.EHandler.fatal( str(e) )
172 173
174 - def writeUserSettings( self, errorsonly=False ):
175 """ 176 Create a settings file with all options that are invalid with their 177 default value. 178 """ 179 try: 180 T.backup( self.fuser ) ## create backup if file already exists 181 182 fpath = os.path.dirname(self.fuser) 183 if not os.path.exists( fpath ): 184 if self.verbose: 185 B.EHandler.warning('Creating folder %s for Biskit settings.'\ 186 %fpath ) 187 os.mkdir( fpath ) 188 189 sections = [P.Setting.NORMAL, P.Setting.PATH, P.Setting.BIN] 190 r = {} 191 192 for section in sections: 193 194 r[ section ] = [ s for s in self.settings.values() \ 195 if s.section == section] 196 r[ section ].sort() 197 198 f = open( self.fuser, 'w' ) 199 200 f.write( SettingsManager.USER_HEADER % self.__dict__ ) 201 202 for section in sections: 203 204 f.write( '[%s]\n' % section ) 205 f.write('\n') 206 207 for param in r[section]: 208 209 if (not errorsonly) or param.error: 210 f.write( param.formatted() + '\n') 211 else: 212 f.write( '## ' + param.formatted() + '\n') 213 214 f.write('\n') 215 216 f.close() 217 218 except OSError, e: 219 raise WriteCfgError, e
220
221 - def settings2dict( self ):
222 """ 223 Create dictionary from settings. 224 @return: dictionary of parameter names (keys) and values 225 @rtype: dict {str : any} 226 """ 227 return dict( [ (s.name, s.value) for s in self.settings.values() ] )
228 229
230 - def updateNamespace( self, ns ):
231 """ 232 1. Parse in default configuration and user configuration file 233 2. Merge the two, preferring valid user settings 234 3. Create missing user configuration file if createmissing=True 235 4. Insert parameters into the given namespace 236 237 @param ns: namespace of a module ( obtained with locals() ) 238 @type ns: dict {str:any} 239 """ 240 self.collectSettings() 241 242 if self.fusermissing and self.createmissing: 243 if self.verbose: 244 B.EHandler.warning('Creating new user configuration file %s.' \ 245 % self.fuser, trace=0, error=0) 246 self.writeUserSettings( errorsonly=True ) 247 248 d = self.settings2dict() 249 250 ns.update( d )
251 252 253 ############# 254 ## TESTING 255 ############# 256
257 -class Test:
258 """ 259 Test class 260 """ 261
262 - def run( self, local=0 ):
263 """ 264 run function test 265 @param local: transfer local variables to global and perform 266 other tasks only when run locally 267 @type local: 1|0 268 269 @return: 42 270 @rtype: int 271 """ 272 m = SettingsManager( T.projectRoot()+'/external/defaults/settings.cfg', 273 T.tempDir() + '/settings.cfg', 274 createmissing=True, 275 verbose=local ) 276 277 ns = locals() ## fetch local namespace 278 279 m.updateNamespace( ns ) ## parse and insert options into namespace 280 281 if local: 282 globals().update( locals() ) ## publish namespace for debugging 283 284 T.tryRemove( T.tempDir() + '/settings.cfg' ) ## clean up 285 286 r = m.settings2dict()['testparam'] 287 288 return r ## from 'int-testparam = 42' in settings.cfg
289 290
291 - def expected_result( self ):
292 """ 293 Precalculated result to check for consistent performance. 294 295 @return: 42 296 @rtype: int 297 """ 298 return 42
299 300 301 302 if __name__ == '__main__': 303 304 test = Test() 305 306 assert test.run( local=1 ) == test.expected_result() 307