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 if self.mustExist and value and not os.path.isdir(value): 332 raise ValueError('Nonexistent directory: '+str(value)+' for key '+str(self.key)) 333 self.value = value 334 return 335 336class ArgDirList(Arg): 337 '''Arguments that represent directory lists''' 338 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 339 self.mustExist = mustExist 340 Arg.__init__(self, key, value, help, isTemporary, deprecated) 341 return 342 343 def getEntryPrompt(self): 344 return 'Please enter directory list for '+str(self.key)+': ' 345 346 def getValue(self): 347 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 348 if not self.isValueSet(): 349 checkInteractive(self.key) 350 try: 351 import GUI.FileBrowser 352 import SIDL.Loader 353 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 354 if self.help: db.setTitle(self.help) 355 else: db.setTitle('Select the directory for '+self.key) 356 db.setMustExist(self.exist) 357 self.value = db.getDirectory() 358 except Exception: 359 return Arg.getValue(self) 360 return self.value 361 362 def setValue(self, value): 363 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 364 import os 365 self.checkKey() 366 if not isinstance(value, list): 367 value = [value] 368 # Should check whether it is a well-formed path 369 nvalue = [] 370 for dir in value: 371 nvalue.append(os.path.expanduser(dir)) 372 value = nvalue 373 for dir in value: 374 if self.mustExist and not os.path.isdir(dir): 375 raise ValueError('Invalid directory: '+str(dir)+' for key '+str(self.key)) 376 self.value = value 377 return 378 379class ArgLibrary(Arg): 380 '''Arguments that represent libraries''' 381 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 382 self.mustExist = mustExist 383 Arg.__init__(self, key, value, help, isTemporary, deprecated) 384 return 385 386 def getEntryPrompt(self): 387 return 'Please enter library for '+str(self.key)+': ' 388 389 def getValue(self): 390 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 391 if not self.isValueSet(): 392 checkInteractive(self.key) 393 try: 394 import GUI.FileBrowser 395 import SIDL.Loader 396 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 397 if self.help: db.setTitle(self.help) 398 else: db.setTitle('Select the library for '+self.key) 399 db.setMustExist(self.exist) 400 self.value = db.getFile() 401 except Exception: 402 return Arg.getValue(self) 403 return self.value 404 405 def setValue(self, value): 406 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 407 import os 408 self.checkKey() 409 # Should check whether it is a well-formed path and an archive or shared object 410 if self.mustExist: 411 if not isinstance(value, list): 412 value = value.split(' ') 413 self.value = value 414 return 415 416class ArgExecutable(Arg): 417 '''Arguments that represent executables''' 418 def __init__(self, key, value = None, help = '', mustExist = 1, isTemporary = 0, deprecated = False): 419 self.mustExist = mustExist 420 Arg.__init__(self, key, value, help, isTemporary, deprecated) 421 return 422 423 def getEntryPrompt(self): 424 return 'Please enter executable for '+str(self.key)+': ' 425 426 def getValue(self): 427 '''Returns the value. SHOULD MAKE THIS A PROPERTY''' 428 if not self.isValueSet(): 429 checkInteractive(self.key) 430 try: 431 import GUI.FileBrowser 432 import SIDL.Loader 433 db = GUI.FileBrowser.FileBrowser(SIDL.Loader.createClass('GUI.Default.DefaultFileBrowser')) 434 if self.help: db.setTitle(self.help) 435 else: db.setTitle('Select the executable for '+self.key) 436 db.setMustExist(self.exist) 437 self.value = db.getFile() 438 except Exception: 439 return Arg.getValue(self) 440 return self.value 441 442 def checkExecutable(self, dir, name): 443 import os 444 prog = os.path.join(dir, name) 445 return os.path.isfile(prog) and os.access(prog, os.X_OK) 446 447 def setValue(self, value): 448 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 449 import os 450 self.checkKey() 451 # Should check whether it is a well-formed path 452 if self.mustExist: 453 index = value.find(' ') 454 if index >= 0: 455 options = value[index:] 456 value = value[:index] 457 else: 458 options = '' 459 found = self.checkExecutable('', value) 460 if not found: 461 for dir in os.environ['PATH'].split(os.path.pathsep): 462 if self.checkExecutable(dir, value): 463 found = 1 464 break 465 if not found: 466 raise ValueError('Invalid executable: '+str(value)+' for key '+str(self.key)) 467 self.value = value+options 468 return 469 470class ArgString(Arg): 471 '''Arguments that represent strings satisfying a given regular expression''' 472 def __init__(self, key, value = None, help = '', regExp = None, isTemporary = 0, deprecated = False): 473 self.regExp = regExp 474 if self.regExp: 475 import re 476 self.re = re.compile(self.regExp) 477 Arg.__init__(self, key, value, help, isTemporary, deprecated) 478 return 479 480 def setValue(self, value): 481 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 482 self.checkKey() 483 if self.regExp and not self.re.match(value): 484 raise ValueError('Invalid string '+str(value)+'. You must give a string satisfying "'+str(self.regExp)+'"'+' for key '+str(self.key)) 485 self.value = value 486 return 487 488class ArgDownload(Arg): 489 '''Arguments that represent software downloads''' 490 def __init__(self, key, value = None, help = '', isTemporary = 0, deprecated = False): 491 Arg.__init__(self, key, value, help, isTemporary, deprecated) 492 return 493 494 def valueName(self, value): 495 if value == 0: 496 return 'no' 497 elif value == 1: 498 return 'yes' 499 return str(value) 500 501 def __str__(self): 502 if not self.isValueSet(): 503 return 'Empty '+str(self.__class__) 504 elif isinstance(self.value, list): 505 return str(map(self.valueName, self.value)) 506 return self.valueName(self.value) 507 508 def getEntryPrompt(self): 509 return 'Please enter download value for '+str(self.key)+': ' 510 511 def setValue(self, value): 512 '''Set the value. SHOULD MAKE THIS A PROPERTY''' 513 import os 514 self.checkKey() 515 try: 516 if value == '0': value = 0 517 elif value == '1': value = 1 518 elif value == 'no': value = 0 519 elif value == 'yes': value = 1 520 elif value == 'false': value = 0 521 elif value == 'true': value = 1 522 elif not isinstance(value, int): 523 value = str(value) 524 except: 525 raise TypeError('Invalid download value: '+str(value)+' for key '+str(self.key)) 526 if isinstance(value, str): 527 import urlparse 528 if not urlparse.urlparse(value)[0]: # how do we check if the URL is invalid? 529 if os.path.isfile(value): 530 value = 'file://'+os.path.abspath(value) 531 else: 532 raise ValueError('Invalid download location: '+str(value)+' for key '+str(self.key)) 533 self.value = value 534 return 535