1try: 2 import readline 3except ImportError: pass 4 5def getInteractive(): 6 return isInteractive 7 8def setInteractive(interactive): 9 global isInteractive 10 isInteractive = interactive 11 return 12 13def checkInteractive(key): 14 if not isInteractive: 15 raise ValueError('Value not set for key '+str(key)) 16 return 17setInteractive(1) 18 19class Arg(object): 20 '''This is the base class for all objects contained in RDict. Access to the raw argument values is 21provided by getValue() and setValue(). These objects can be thought of as type objects for the 22values themselves. It is possible to set an Arg in the RDict which has not yet been assigned a value 23in order to declare the type of that option. 24 25Inputs which cannot be converted to the correct type will cause TypeError, those failing validation 26tests will cause ValueError. 27''' 28 def __init__(self, key, value = None, help = '', isTemporary = False, deprecated = False): 29 self.key = key 30 self.help = help 31 self.isTemporary = isTemporary 32 self.deprecated = False 33 if not value is None: 34 self.setValue(value) 35 self.deprecated = deprecated 36 return 37 38 def isValueSet(self): 39 '''Determines whether the value of this argument has been set''' 40 return hasattr(self, 'value') 41 42 def getTemporary(self): 43 '''Retrieve the flag indicating whether the item should be persistent''' 44 return self.isTemporary 45 46 def setTemporary(self, isTemporary): 47 '''Set the flag indicating whether the item should be persistent''' 48 self.isTemporary = isTemporary 49 return 50 51 def parseValue(arg): 52 '''Return the object represented by the value portion of a string argument''' 53 # Should I replace this with a lexer? 54 if arg: arg = arg.strip() 55 if arg and arg[0] == '[' and arg[-1] == ']': 56 if len(arg) > 2: value = arg[1:-1].split(',') 57 else: value = [] 58 elif arg and arg[0] == '{' and arg[-1] == '}': 59 value = {} 60 idx = 1 61 oldIdx = idx 62 while idx < len(arg)-1: 63 if arg[oldIdx] == ',': 64 oldIdx += 1 65 while not arg[idx] == ':': idx += 1 66 key = arg[oldIdx:idx] 67 idx += 1 68 oldIdx = idx 69 nesting = 0 70 while not (arg[idx] == ',' or arg[idx] == '}') or nesting: 71 if arg[idx] == '[': 72 nesting += 1 73 elif arg[idx] == ']': 74 nesting -= 1 75 idx += 1 76 value[key] = Arg.parseValue(arg[oldIdx:idx]) 77 oldIdx = idx 78 else: 79 value = arg 80 return value 81 parseValue = staticmethod(parseValue) 82 83 def parseArgument(arg, ignoreDouble = 0): 84 '''Split an argument into a (key, value) tuple, stripping off the leading dashes. Return (None, None) on failure.''' 85 start = 0 86 if arg and arg[0] == '-': 87 start = 1 88 if arg[1] == '-' and not ignoreDouble: 89 start = 2 90 if arg.find('=') >= 0: 91 (key, value) = arg[start:].split('=', 1) 92 else: 93 if start == 0: 94 (key, value) = (None, arg) 95 else: 96 (key, value) = (arg[start:], '1') 97 return (key, Arg.parseValue(value)) 98 99 parseArgument = staticmethod(parseArgument) 100 101 def findArgument(key, argList): 102 '''Locate an argument with the given key in argList, returning the value or None on failure 103 - This is generally used to process arguments which must take effect before canonical argument parsing''' 104 if not isinstance(argList, list): return None 105 # Reverse the list so that we preserve the semantics which state that the last 106 # argument with a given key takes effect 107 l = argList[:] 108 l.reverse() 109 for arg in l: 110 (k, value) = Arg.parseArgument(arg) 111 if k == key: 112 return value 113 return None 114 findArgument = staticmethod(findArgument) 115 116 def processAlternatePrefixes(argList): 117 '''Convert alternate prefixes to our normal form''' 118 for l in range(0, len(argList)): 119 name = argList[l] 120 if name.find('enable-') >= 0: 121 argList[l] = name.replace('enable-','with-') 122 if name.find('=') == -1: argList[l] = argList[l]+'=1' 123 if name.find('disable-') >= 0: 124 argList[l] = name.replace('disable-','with-') 125 if name.find('=') == -1: argList[l] = argList[l]+'=0' 126 elif name.endswith('=1'): argList[l].replace('=1','=0') 127 if name.find('without-') >= 0: 128 argList[l] = name.replace('without-','with-') 129 if name.find('=') == -1: argList[l] = argList[l]+'=0' 130 elif name.endswith('=1'): argList[l].replace('=1','=0') 131 return 132 processAlternatePrefixes = staticmethod(processAlternatePrefixes) 133 134 def __str__(self): 135 if not self.isValueSet(): 136 return 'Empty '+str(self.__class__) 137 elif isinstance(self.value, list): 138 return str(map(str, self.value)) 139 return str(self.value) 140 141 def getEntryPrompt(self): 142 return 'Please enter value for '+str(self.key)+': ' 143 144 def getKey(self): 145 '''Returns the key. SHOULD MAKE THIS A PROPERTY''' 146 return self.key 147 148 def setKey(self, key): 149 '''Set the key. SHOULD MAKE THIS A PROPERTY''' 150 self.key = key 151 return 152 153 def getValue(self): 154 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 155 if not self.isValueSet(): 156 checkInteractive(self.key) 157 if self.help: print self.help 158 while 1: 159 try: 160 self.setValue(Arg.parseValue(raw_input(self.getEntryPrompt()))) 161 break 162 except KeyboardInterrupt: 163 raise KeyError('Could not find value for key '+str(self.key)) 164 except TypeError, e: 165 print str(e) 166 return self.value 167 168 def checkKey(self): 169 if self.deprecated: 170 if isinstance(self.deprecated, str): 171 raise KeyError('Deprecated option '+self.key+' should be '+self.deprecated) 172 raise KeyError('Deprecated option '+self.key) 173 return 174 175 def setValue(self, value): 176 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 177 self.checkKey() 178 self.value = value 179 return 180 181class ArgBool(Arg): 182 '''Arguments that represent boolean values''' 183 def __init__(self, key, value = None, help = '', isTemporary = 0, deprecated = False): 184 Arg.__init__(self, key, value, help, isTemporary, deprecated) 185 return 186 187 def getEntryPrompt(self): 188 return 'Please enter boolean value for '+str(self.key)+': ' 189 190 def setValue(self, value): 191 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 192 self.checkKey() 193 try: 194 if value == 'no': value = 0 195 elif value == 'yes': value = 1 196 elif value == 'true': value = 1 197 elif value == 'false': value = 0 198 elif value == 'True': value = 1 199 elif value == 'False': value = 0 200 else: value = int(value) 201 except: 202 raise TypeError('Invalid boolean value: '+str(value)+' for key '+str(self.key)) 203 self.value = value 204 return 205 206class ArgFuzzyBool(Arg): 207 '''Arguments that represent boolean values of an extended set''' 208 def __init__(self, key, value = None, help = '', isTemporary = 0, deprecated = False): 209 Arg.__init__(self, key, value, help, isTemporary, deprecated) 210 return 211 212 def valueName(self, value): 213 if value == 0: 214 return 'no' 215 elif value == 1: 216 return 'yes' 217 elif value == 2: 218 return 'ifneeded' 219 return str(value) 220 221 def __str__(self): 222 if not self.isValueSet(): 223 return 'Empty '+str(self.__class__) 224 elif isinstance(self.value, list): 225 return str(map(self.valueName, self.value)) 226 return self.valueName(self.value) 227 228 def getEntryPrompt(self): 229 return 'Please enter fuzzy boolean value for '+str(self.key)+': ' 230 231 def setValue(self, value): 232 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 233 self.checkKey() 234 try: 235 if value == '0': value = 0 236 elif value == '1': value = 1 237 elif value == 'no': value = 0 238 elif value == 'yes': value = 1 239 elif value == 'false': value = 0 240 elif value == 'true': value = 1 241 elif value == 'maybe': value = 2 242 elif value == 'ifneeded': value = 2 243 elif value == 'client': value = 2 244 elif value == 'server': value = 3 245 else: value = int(value) 246 except: 247 raise TypeError('Invalid fuzzy boolean value: '+str(value)+' for key '+str(self.key)) 248 self.value = value 249 return 250 251class ArgInt(Arg): 252 '''Arguments that represent integer numbers''' 253 def __init__(self, key, value = None, help = '', min = -2147483647L, max = 2147483648L, isTemporary = 0, deprecated = False): 254 self.min = min 255 self.max = max 256 Arg.__init__(self, key, value, help, isTemporary, deprecated) 257 return 258 259 def getEntryPrompt(self): 260 return 'Please enter integer value for '+str(self.key)+': ' 261 262 def setValue(self, value): 263 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 264 self.checkKey() 265 try: 266 value = int(value) 267 except: 268 raise TypeError('Invalid integer number: '+str(value)+' for key '+str(self.key)) 269 if value < self.min or value >= self.max: 270 raise ValueError('Number out of range: '+str(value)+' not in ['+str(self.min)+','+str(self.max)+')'+' for key '+str(self.key)) 271 self.value = value 272 return 273 274class ArgReal(Arg): 275 '''Arguments that represent floating point numbers''' 276 def __init__(self, key, value = None, help = '', min = -1.7976931348623157e308, max = 1.7976931348623157e308, isTemporary = 0, deprecated = False): 277 self.min = min 278 self.max = max 279 Arg.__init__(self, key, value, help, isTemporary, deprecated) 280 return 281 282 def getEntryPrompt(self): 283 return 'Please enter floating point value for '+str(self.key)+': ' 284 285 def setValue(self, value): 286 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 287 self.checkKey() 288 try: 289 value = float(value) 290 except: 291 raise TypeError('Invalid floating point number: '+str(value)+' for key '+str(self.key)) 292 if value < self.min or value >= self.max: 293 raise ValueError('Number out of range: '+str(value)+' not in ['+str(self.min)+','+str(self.max)+')'+' for key '+str(self.key)) 294 self.value = value 295 return 296 297class ArgDir(Arg): 298 '''Arguments that represent directories''' 299 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 300 self.mustExist = mustExist 301 Arg.__init__(self, key, value, help, isTemporary, deprecated) 302 return 303 304 def getEntryPrompt(self): 305 return 'Please enter directory for '+str(self.key)+': ' 306 307 def getValue(self): 308 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 309 if not self.isValueSet(): 310 checkInteractive(self.key) 311 try: 312 import GUI.FileBrowser 313 import SIDL.Loader 314 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 315 if self.help: db.setTitle(self.help) 316 else: db.setTitle('Select the directory for '+self.key) 317 db.setMustExist(self.exist) 318 self.value = db.getDirectory() 319 except Exception: 320 return Arg.getValue(self) 321 return self.value 322 323 def setValue(self, value): 324 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 325 import os 326 self.checkKey() 327 # Should check whether it is a well-formed path 328 if not isinstance(value, str): 329 raise TypeError('Invalid directory: '+str(value)+' for key '+str(self.key)) 330 value = os.path.expanduser(value) 331 value = os.path.abspath(value) 332 if self.mustExist and value and not os.path.isdir(value): 333 raise ValueError('Nonexistent directory: '+str(value)+' for key '+str(self.key)) 334 self.value = value 335 return 336 337class ArgDirList(Arg): 338 '''Arguments that represent directory lists''' 339 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 340 self.mustExist = mustExist 341 Arg.__init__(self, key, value, help, isTemporary, deprecated) 342 return 343 344 def getEntryPrompt(self): 345 return 'Please enter directory list for '+str(self.key)+': ' 346 347 def getValue(self): 348 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 349 if not self.isValueSet(): 350 checkInteractive(self.key) 351 try: 352 import GUI.FileBrowser 353 import SIDL.Loader 354 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 355 if self.help: db.setTitle(self.help) 356 else: db.setTitle('Select the directory for '+self.key) 357 db.setMustExist(self.exist) 358 self.value = db.getDirectory() 359 except Exception: 360 return Arg.getValue(self) 361 return self.value 362 363 def setValue(self, value): 364 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 365 import os 366 self.checkKey() 367 if not isinstance(value, list): 368 value = [value] 369 # Should check whether it is a well-formed path 370 nvalue = [] 371 for dir in value: 372 nvalue.append(os.path.expanduser(dir)) 373 value = nvalue 374 for dir in value: 375 if self.mustExist and not os.path.isdir(dir): 376 raise ValueError('Invalid directory: '+str(dir)+' for key '+str(self.key)) 377 self.value = value 378 return 379 380class ArgLibrary(Arg): 381 '''Arguments that represent libraries''' 382 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 383 self.mustExist = mustExist 384 Arg.__init__(self, key, value, help, isTemporary, deprecated) 385 return 386 387 def getEntryPrompt(self): 388 return 'Please enter library for '+str(self.key)+': ' 389 390 def getValue(self): 391 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 392 if not self.isValueSet(): 393 checkInteractive(self.key) 394 try: 395 import GUI.FileBrowser 396 import SIDL.Loader 397 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 398 if self.help: db.setTitle(self.help) 399 else: db.setTitle('Select the library for '+self.key) 400 db.setMustExist(self.exist) 401 self.value = db.getFile() 402 except Exception: 403 return Arg.getValue(self) 404 return self.value 405 406 def setValue(self, value): 407 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 408 import os 409 self.checkKey() 410 # Should check whether it is a well-formed path and an archive or shared object 411 if self.mustExist: 412 if not isinstance(value, list): 413 value = value.split(' ') 414 self.value = value 415 return 416 417class ArgExecutable(Arg): 418 '''Arguments that represent executables''' 419 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 420 self.mustExist = mustExist 421 Arg.__init__(self, key, value, help, isTemporary, deprecated) 422 return 423 424 def getEntryPrompt(self): 425 return 'Please enter executable for '+str(self.key)+': ' 426 427 def getValue(self): 428 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 429 if not self.isValueSet(): 430 checkInteractive(self.key) 431 try: 432 import GUI.FileBrowser 433 import SIDL.Loader 434 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 435 if self.help: db.setTitle(self.help) 436 else: db.setTitle('Select the executable for '+self.key) 437 db.setMustExist(self.exist) 438 self.value = db.getFile() 439 except Exception: 440 return Arg.getValue(self) 441 return self.value 442 443 def checkExecutable(self, dir, name): 444 import os 445 prog = os.path.join(dir, name) 446 return os.path.isfile(prog) and os.access(prog, os.X_OK) 447 448 def setValue(self, value): 449 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 450 import os 451 self.checkKey() 452 # Should check whether it is a well-formed path 453 if self.mustExist: 454 index = value.find(' ') 455 if index >= 0: 456 options = value[index:] 457 value = value[:index] 458 else: 459 options = '' 460 found = self.checkExecutable('', value) 461 if not found: 462 for dir in os.environ['PATH'].split(os.path.pathsep): 463 if self.checkExecutable(dir, value): 464 found = 1 465 break 466 if not found: 467 raise ValueError('Invalid executable: '+str(value)+' for key '+str(self.key)) 468 self.value = value+options 469 return 470 471class ArgString(Arg): 472 '''Arguments that represent strings satisfying a given regular expression''' 473 def __init__(self, key, value = None, help = '', regExp = None, isTemporary = 0, deprecated = False): 474 self.regExp = regExp 475 if self.regExp: 476 import re 477 self.re = re.compile(self.regExp) 478 Arg.__init__(self, key, value, help, isTemporary, deprecated) 479 return 480 481 def setValue(self, value): 482 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 483 self.checkKey() 484 if self.regExp and not self.re.match(value): 485 raise ValueError('Invalid string '+str(value)+'. You must give a string satisfying "'+str(self.regExp)+'"'+' for key '+str(self.key)) 486 self.value = value 487 return 488 489class ArgDownload(Arg): 490 '''Arguments that represent software downloads''' 491 def __init__(self, key, value = None, help = '', isTemporary = 0, deprecated = False): 492 Arg.__init__(self, key, value, help, isTemporary, deprecated) 493 return 494 495 def valueName(self, value): 496 if value == 0: 497 return 'no' 498 elif value == 1: 499 return 'yes' 500 return str(value) 501 502 def __str__(self): 503 if not self.isValueSet(): 504 return 'Empty '+str(self.__class__) 505 elif isinstance(self.value, list): 506 return str(map(self.valueName, self.value)) 507 return self.valueName(self.value) 508 509 def getEntryPrompt(self): 510 return 'Please enter download value for '+str(self.key)+': ' 511 512 def setValue(self, value): 513 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 514 import os 515 self.checkKey() 516 try: 517 if value == '0': value = 0 518 elif value == '1': value = 1 519 elif value == 'no': value = 0 520 elif value == 'yes': value = 1 521 elif value == 'false': value = 0 522 elif value == 'true': value = 1 523 elif not isinstance(value, int): 524 value = str(value) 525 except: 526 raise TypeError('Invalid download value: '+str(value)+' for key '+str(self.key)) 527 if isinstance(value, str): 528 import urlparse 529 if not urlparse.urlparse(value)[0]: # how do we check if the URL is invalid? 530 if os.path.isfile(value): 531 value = 'file://'+os.path.abspath(value) 532 else: 533 raise ValueError('Invalid download location: '+str(value)+' for key '+str(self.key)) 534 self.value = value 535 return 536