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