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