00001 import sys
00002 import os
00003 import re
00004 from pprint import pprint
00005 from FWCore.Utilities.Enumerate import Enumerate
00006 from FWCore.Utilities.FileUtils import sectionNofTotal
00007
00008 class VarParsing (object):
00009 """Infrastructure to parse variable definitions passed to cmsRun
00010 configuration scripts"""
00011
00012
00013 multiplicity = Enumerate ("singleton list", "multiplicity")
00014 varType = Enumerate ("bool int float string tagString")
00015 commaRE = re.compile (r',')
00016 trueRE = re.compile (r'^true$', re.IGNORECASE)
00017 falseRE = re.compile (r'^false$', re.IGNORECASE)
00018
00019
00020 def __init__ (self, *args):
00021 """Class initializer"""
00022
00023 self._singletons = {}
00024 self._lists = {}
00025 self._register = {}
00026 self._beenSet = {}
00027 self._info = {}
00028 self._types = {}
00029 self._maxLength = 0
00030 self._tags = {}
00031 self._tagOrder = []
00032 self._noCommaSplit = {}
00033 self._noDefaultClear = {}
00034 self._setDuringParsing = {}
00035 self._currentlyParsing = False
00036
00037 for arg in args:
00038 lower = arg.lower()
00039 if lower == 'python':
00040 self.register ('storePrepend',
00041 '',
00042 VarParsing.multiplicity.singleton,
00043 VarParsing.varType.string,
00044 "Prepend location of files starting "
00045 "with '/store'")
00046
00047
00048 if lower == 'analysis' or lower == 'python':
00049
00050 self.register ('maxEvents',
00051 -1,
00052 VarParsing.multiplicity.singleton,
00053 VarParsing.varType.int,
00054 "Number of events to process (-1 for all)")
00055 self.register ('totalSections',
00056 0,
00057 VarParsing.multiplicity.singleton,
00058 VarParsing.varType.int,
00059 "Total number of sections")
00060 self.register ('section',
00061 0,
00062 VarParsing.multiplicity.singleton,
00063 VarParsing.varType.int,
00064 "This section (from 1..totalSections inclusive)")
00065 self.register ('inputFiles',
00066 '',
00067 VarParsing.multiplicity.list,
00068 VarParsing.varType.string,
00069 "Files to process")
00070 self.register ('secondaryInputFiles',
00071 '',
00072 VarParsing.multiplicity.list,
00073 VarParsing.varType.string,
00074 "Second group of files to process (if needed)")
00075 self.register ('filePrepend',
00076 '',
00077 VarParsing.multiplicity.singleton,
00078 VarParsing.varType.string,
00079 "String to prepend location of all files")
00080 self.register ('outputFile',
00081 'output.root',
00082 VarParsing.multiplicity.singleton,
00083 VarParsing.varType.tagString,
00084 "Name of output file (if needed)")
00085 self.register ('secondaryOutputFile',
00086 '',
00087 VarParsing.multiplicity.singleton,
00088 VarParsing.varType.tagString,
00089 "Name of second output file (if needed)")
00090 self.register ('tag',
00091 '',
00092 VarParsing.multiplicity.singleton,
00093 VarParsing.varType.string,
00094 "tag to add to output filename")
00095 self.setupTags (tag = 'numEvent%d',
00096 ifCond = 'maxEvents > 0',
00097 tagArg = 'maxEvents')
00098 self.setupTags (tag = '%s',
00099 ifCond = 'tag',
00100 tagArg = 'tag')
00101 continue
00102
00103 if lower == "standard":
00104
00105 self.register ('maxEvents',
00106 -1,
00107 VarParsing.multiplicity.singleton,
00108 VarParsing.varType.int,
00109 "Number of events to process (-1 for all)")
00110 self.register ('files',
00111 '',
00112 VarParsing.multiplicity.list,
00113 VarParsing.varType.string,
00114 "Files to process")
00115 self.register ('secondaryFiles',
00116 '',
00117 VarParsing.multiplicity.list,
00118 VarParsing.varType.string,
00119 "Second group of files to process (if needed)")
00120 self.register ('output',
00121 'output.root',
00122 VarParsing.multiplicity.singleton,
00123 VarParsing.varType.tagString,
00124 "Name of output file (if needed)")
00125 self.register ('secondaryOutput',
00126 '',
00127 VarParsing.multiplicity.singleton,
00128 VarParsing.varType.tagString,
00129 "Name of second output file (if needed)")
00130 self.setupTags (tag = 'numEvent%d',
00131 ifCond = 'maxEvents > 0',
00132 tagArg = 'maxEvents')
00133 continue
00134
00135 print "Error: VarParsing.__init__ doesn't understand '%s'" \
00136 % arg
00137 raise RuntimeError, "Failed to create VarParsing object"
00138
00139
00140 def setupTags (self, **kwargs):
00141 """Sets up information for tags for output names"""
00142 necessaryKeys = set (['ifCond', 'tag'])
00143 allowedKeys = set (['tagArg'])
00144 for key in kwargs.keys():
00145 if key in allowedKeys:
00146 continue
00147 if key in necessaryKeys:
00148 necessaryKeys.remove (key)
00149 continue
00150
00151 print "Unknown option '%s'" % key
00152 raise RuntimeError, "Unknown option"
00153 if necessaryKeys:
00154
00155
00156 print "Missing keys: %s" % necessaryKeys
00157 raise runtimeError, "Missing keys"
00158 tag = kwargs.get('tag')
00159 del kwargs['tag']
00160 self._tags[tag] = kwargs
00161 self._tagOrder.append (tag)
00162
00163
00164 def parseArguments (self):
00165 """Parses command line arguments. Parsing starts just after
00166 the name of the configuration script. Parsing will fail if
00167 there is not 'xxxx.py'"""
00168 self._currentlyParsing = True
00169 foundPy = False
00170 printStatus = False
00171 help = False
00172 singleAssign = True
00173 for arg in sys.argv:
00174 if not foundPy and arg.endswith ('.py'):
00175 foundPy = True
00176 continue
00177 if not foundPy:
00178 continue
00179
00180
00181 if arg.count('='):
00182
00183 name, value = arg.split ('=', 1)
00184 if name.count('_'):
00185
00186 name, command = name.split ('_', 1)
00187 command = command.lower()
00188 if command == 'load':
00189 self.loadFromFile (name, value)
00190 continue
00191 if command == 'clear':
00192 self.clearList (name)
00193 continue
00194
00195 print "Unknown command '%s' in '%s_%s" % \
00196 (command, name, command)
00197 raise RuntimeError, "Illegal parsing command"
00198 else:
00199
00200 if not self._register.has_key (name):
00201 print "Error: '%s' not registered." \
00202 % name
00203 raise RuntimeError, "Unknown variable"
00204 if VarParsing.multiplicity.singleton == \
00205 self._register[name]:
00206
00207 if self._beenSet.get (name) and singleAssign:
00208 print "Variable '%s' assigned multiple times. Use" \
00209 , "'multipleAssign' command to avoid"
00210 raise RuntimeError, "Multiple assignment"
00211 self._beenSet[name] = True
00212 self.setDefault (name, value)
00213 else:
00214
00215 self.setDefault (name, value)
00216 else:
00217
00218 if arg.count('_'):
00219
00220 name, command = arg.split ('_', 1)
00221 command = command.lower()
00222 if not self._register.has_key (name):
00223 print "Error: '%s' not registered." \
00224 % name
00225 raise RuntimeError, "Unknown variable"
00226 if command == 'clear':
00227 self.clearList (name)
00228 continue
00229
00230
00231 print "Do not understand '%s' in '%s'" % (command, arg)
00232 raise RuntimeError, "Unknown command"
00233 else:
00234
00235 command = arg.lower()
00236 if command == 'help' or command == '--help':
00237 help = True
00238 elif command == 'print' or command == '--print':
00239 printStatus = True
00240 elif command == 'noprint' or command == '--noprint':
00241 printStatus = False
00242 else:
00243
00244 print "Do not understand command '%s'" % (arg)
00245 raise RuntimeError, "Unknown command"
00246
00247
00248
00249
00250
00251 if self._register.has_key ('totalSections') and \
00252 self._register.has_key ('section') and \
00253 self._register.has_key ('inputFiles') and \
00254 self.totalSections and self.section:
00255
00256 oldInputFiles = self.inputFiles
00257
00258 self.clearList ('inputFiles')
00259
00260 self.inputFiles = sectionNofTotal (oldInputFiles,
00261 self.section,
00262 self.totalSections)
00263
00264 if self._register.has_key ('storePrepend') and \
00265 self._register.has_key ('inputFiles') and \
00266 self.storePrepend:
00267 storeRE = re.compile (r'^/store/')
00268 newFileList = []
00269 for filename in self.inputFiles:
00270 if storeRE.match (filename):
00271 filename = self.storePrepend + filename
00272 newFileList.append (filename)
00273
00274 self.clearList ('inputFiles')
00275
00276 self.inputFiles = newFileList
00277
00278 if self._register.has_key ('filePrepend') and \
00279 self._register.has_key ('inputFiles') and \
00280 self.filePrepend:
00281 newFileList = []
00282 for filename in self.inputFiles:
00283 filename = self.filePrepend + filename
00284 newFileList.append (filename)
00285
00286 self.clearList ('inputFiles')
00287
00288 self.inputFiles = newFileList
00289
00290 if not foundPy:
00291 print "VarParsing.parseArguments() Failure: No configuration " + \
00292 "file found ending in .py."
00293 raise RuntimeError, "Invalid configuration ending"
00294 if help:
00295 self.help()
00296 if printStatus:
00297 print self
00298 self._currentlyParsing = False
00299
00300
00301 def clearList (self, name):
00302 """Empties all entries from list"""
00303 if not self._register.has_key (name):
00304 print "Error: '%s' not registered." \
00305 % name
00306 raise RuntimeError, "Unknown variable"
00307 if self._register[name] != VarParsing.multiplicity.list:
00308 print "Error: '%s' is not a list" % name
00309 raise RuntimeError, "Faulty 'clear' command"
00310
00311 self._lists[name] = []
00312
00313
00314 def setNoDefaultClear (self, name, value=True):
00315 """Tells lists to not clear default list values when set from
00316 command line."""
00317 if not self._register.has_key (name):
00318 print "Error: '%s' not registered." \
00319 % name
00320 raise RuntimeError, "Unknown variable"
00321 if self._register[name] != VarParsing.multiplicity.list:
00322 print "Error: '%s' is not a list" % name
00323 raise RuntimeError, "Faulty 'setNoDefaultClear' command"
00324 self._noDefaultClear[name] = bool (value)
00325
00326
00327 def setNoCommaSplit (self, name, value=True):
00328 """Tells lists to not split up values by commas."""
00329 if not self._register.has_key (name):
00330 print "Error: '%s' not registered." \
00331 % name
00332 raise RuntimeError, "Unknown variable"
00333 if self._register[name] != VarParsing.multiplicity.list:
00334 print "Error: '%s' is not a list" % name
00335 raise RuntimeError, "Faulty 'setNoCommaSplit' command"
00336 self._noCommaSplit[name] = bool (value)
00337
00338
00339 def loadFromFile (self, name, filename):
00340 """Loads a list from file"""
00341 if not self._register.has_key (name):
00342 print "Error: '%s' not registered." \
00343 % name
00344 raise RuntimeError, "Unknown variable"
00345 if self._register[name] != VarParsing.multiplicity.list:
00346 print "Error: '%s' is not a list" % name
00347 raise RuntimeError, "'load' only works for lists"
00348 filename = os.path.expanduser (filename)
00349 if not os.path.exists (filename):
00350 print "Error: '%s' file does not exist."
00351 raise RuntimeError, "Bad filename"
00352 source = open (filename, 'r')
00353 for line in source.readlines():
00354 line = re.sub (r'#.+$', '', line)
00355 line = line.strip()
00356 if len (line):
00357 self._lists[name].append( self._convert (name, line ) )
00358 source.close()
00359
00360
00361 def help (self):
00362 """Prints out help information and exits"""
00363 print self
00364 print """Options:
00365 help : This screen
00366 multipleAssign : Allows singletons to have multiple assigments
00367 print : Prints out current values
00368 XXX_clear : Clears list named 'XXX'
00369 """
00370 sys.exit (0)
00371
00372
00373 def register (self, name,
00374 default = "",
00375 mult = multiplicity.singleton,
00376 mytype = varType.int,
00377 info = "",
00378 **kwargs):
00379 """Register a variable"""
00380
00381 if not VarParsing.multiplicity.isValidValue (mult):
00382 print "Error: VarParsing.register() must use ",\
00383 "VarParsing.multiplicity."
00384 raise RuntimeError, "Improper 'mult' value"
00385 if not VarParsing.varType.isValidValue (mytype):
00386 print "Error: VarParsing.register() must use ",\
00387 "VarParsing.varType."
00388 raise RuntimeError, "Improper 'type' value %s" % mytype
00389 if VarParsing.multiplicity.list == mult and \
00390 VarParsing.varType.tagString == mytype:
00391 print "Error: 'tagString' can only be used with 'singleton'"
00392 raise RuntimeError, "Improper registration"
00393
00394 if name.count ("_"):
00395 print "Error: Name can not contain '_': %s" % name
00396 raise RuntimeError, "Improper 'name'"
00397
00398 if self._register.has_key (name):
00399
00400 print "Error: You can not register a name twice, '%s'" \
00401 % name
00402 raise RuntimeError, "Attempt to re-register variable"
00403 self._register[name] = mult
00404 self._beenSet[name] = False
00405 self._info[name] = info
00406 self._types[name] = mytype
00407 if len (name) > self._maxLength:
00408 self._maxLength = len (name)
00409 if VarParsing.multiplicity.singleton == mult:
00410 self._singletons[name] = default
00411 else:
00412 self._lists[name] = []
00413
00414
00415 if len (default):
00416 self._lists[name].append (default)
00417
00418
00419
00420
00421
00422 if kwargs.get ('noCommaSplit'):
00423 self._noCommaSplit[name] = bool( kwargs['noCommaSplit'] )
00424 del kwargs['noCommaSplit']
00425 if kwargs.get ('noDefaultClear'):
00426 self._noDefaultClear[name] = bool( kwargs['noDefaultClear'] )
00427 del kwargs['noDefaultClear']
00428 if len (kwargs):
00429 raise RuntimeError, "register() Unknown arguments %s" % kwargs
00430
00431
00432 def has_key (self, key):
00433 """Returns true if a key is registered"""
00434 return self._register.has_key (key)
00435
00436
00437 def setType (self, name, mytype):
00438 """Change the type of 'name' to 'mytype'"""
00439 if not VarParsing.varType.isValidValue (mytype):
00440 print "Error: VarParsing.setType() must use ",\
00441 "VarParsing.varType."
00442 raise RuntimeError, "Improper 'type' value"
00443 oldVal = self.__getattr__ (name, noTags = True)
00444 self._types[name] = mytype
00445 self.setDefault (name, oldVal)
00446
00447
00448 def setDefault (self, name, *args):
00449 """Used to set or change the default of an already registered
00450 name"""
00451
00452 if not self._register.has_key (name):
00453 print "Error: VarParsing.setDefault '%s' not already registered." \
00454 % name
00455 raise RuntimeError, "setDefault without registration"
00456 if VarParsing.multiplicity.singleton == self._register[name]:
00457
00458 if len (args) != 1:
00459 print "Error: VarParsing.setDefault needs exactly 1 ",\
00460 "value for '%s'" % name
00461 raise RuntimeError, "setDefault args problem"
00462 self._singletons[name] = self._convert (name, args[0])
00463 else:
00464
00465
00466
00467
00468
00469
00470
00471 if self._currentlyParsing and \
00472 not self._setDuringParsing.get(name) and \
00473 not self._noDefaultClear.get(name):
00474
00475
00476 self.clearList (name)
00477
00478
00479 if self._currentlyParsing:
00480 self._setDuringParsing[name] = True
00481
00482
00483 if isinstance (args, tuple) and len (args) == 1:
00484 args = args[0]
00485
00486 if isinstance (args, tuple):
00487 mylist = list (args)
00488 elif isinstance (args, list):
00489 mylist = args
00490 else:
00491 mylist = []
00492 mylist.append (args)
00493 if not self._noCommaSplit.get (name):
00494 oldList = mylist
00495 mylist = []
00496 for item in oldList:
00497 mylist.extend( VarParsing.commaRE.split( item ) )
00498 for item in mylist:
00499 self._lists[name].append( self._convert (name, item ) )
00500
00501
00502 def _convert (self, name, inputVal):
00503 """Converts inputVal to the type required by name"""
00504 inputVal = str (inputVal)
00505 if self._types[name] == VarParsing.varType.bool:
00506 if VarParsing.trueRE.match (inputVal) or '1' == inputVal:
00507 return True
00508 elif VarParsing.falseRE.match (inputVal) or '0' == inputVal:
00509 return False
00510
00511 raise RuntimeError, "Unknown bool value '%s'. Must be 'true' or 'false'" % inputVal
00512 if self._types[name] == VarParsing.varType.string or \
00513 self._types[name] == VarParsing.varType.tagString:
00514 return inputVal
00515 elif self._types[name] == VarParsing.varType.int:
00516 return int (inputVal, 0)
00517 elif self._types[name] == VarParsing.varType.float:
00518 return float (inputVal)
00519 else:
00520 raise RuntimeError, "Unknown varType"
00521
00522
00523 def _withTags (self, name):
00524 if not self._register.has_key (name):
00525 print "Error: '%s' not registered." \
00526 % name
00527 raise RuntimeError, "Unknown variable"
00528 if self._register[name] == VarParsing.multiplicity.list:
00529 print "Error: '%s' is a list" % name
00530 raise RuntimeError, "withTags() only works on singletons"
00531 retval = self._singletons[name]
00532 if retval.endswith ('.root'):
00533 retval, garbage = os.path.splitext (retval)
00534 reverseOrder = self._tagOrder
00535 reverseOrder.reverse()
00536 for tag in reverseOrder:
00537 tagDict = self._tags[tag]
00538 ifCond = tagDict['ifCond']
00539 if ifCond.count('%'):
00540 pass
00541 else:
00542 ifCond = "self." + ifCond
00543 boolValue = eval (ifCond)
00544 tagArg = tagDict.get ('tagArg')
00545 if tagArg:
00546 evalString = "'%s' %% self.%s" % (tag, tagArg)
00547 tag = eval (evalString)
00548 if boolValue:
00549 retval = retval + "_" + tag
00550 return retval + ".root"
00551
00552
00553 def __str__ (self):
00554 """String form of self"""
00555 maxLen = min (self._maxLength, 20)
00556 form = " %%-%ds: %%s" % maxLen
00557 formInfo = " %%%ds - %%s" % (maxLen - 2)
00558 formItem = " %%%ds %%s" % (maxLen - 1)
00559 retval = ""
00560 if len (self._singletons.keys()):
00561 retval = retval + "Singletons:\n"
00562 for varName, value in sorted (self._singletons.iteritems()):
00563 retval = retval + form % (varName, value) + "\n";
00564 if self._info.get(varName):
00565 retval = retval + formInfo % ('', self._info[varName]) + "\n"
00566 if len (self._singletons.keys()):
00567 retval = retval + "Lists:\n"
00568 for varName, value in sorted (self._lists.iteritems()):
00569 stringValue = "%s" % value
00570 if len (stringValue) < 76 - maxLen:
00571 retval = retval + form % (varName, value) + "\n"
00572 else:
00573 varLength = len (value)
00574 for index, item in enumerate (value):
00575 if index == 0:
00576 retval = retval + form % (varName, "['" + item)
00577 else:
00578 retval = retval + formItem % ('',"'" + item)
00579 if index == varLength - 1:
00580 retval = retval + "' ]\n"
00581 else:
00582 retval = retval + "',\n"
00583 if self._info.get(varName):
00584 retval = retval + formInfo % ('', self._info[varName]) + "\n"
00585 return retval
00586
00587
00588 def __setattr__ (self, name, value, *extras):
00589 """Lets me set internal values, or uses setDefault"""
00590 if not name.startswith ("_"):
00591 mylist = list (extras)
00592 mylist.insert (0, value)
00593 self.setDefault (name, *mylist)
00594 else:
00595 object.__setattr__ (self, name, value)
00596
00597
00598 def __getattr__ (self, name, noTags = False):
00599 """Lets user get the info they want with obj.name"""
00600 if name.startswith ("_"):
00601
00602 return object.__getattribute__ (self, name)
00603 else:
00604
00605 if not self._register.has_key (name):
00606 print "Error: '%s' not already registered." \
00607 % name
00608 raise RuntimeError, "Unknown variable"
00609 if VarParsing.multiplicity.singleton == self._register[name]:
00610 if VarParsing.varType.tagString == self._types[name] \
00611 and not noTags:
00612 return self._withTags (name)
00613 else:
00614 return self._singletons[name]
00615 else:
00616 return self._lists[name]
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626 if __name__ == "__main__":
00627
00628
00629
00630
00631 import os, readline
00632 import atexit
00633 historyPath = os.path.expanduser("~/.pyhistory")
00634
00635
00636 def save_history(historyPath=historyPath):
00637 import readline
00638 readline.write_history_file(historyPath)
00639 if os.path.exists(historyPath):
00640 readline.read_history_file(historyPath)
00641
00642
00643 atexit.register(save_history)
00644 readline.parse_and_bind("set show-all-if-ambiguous on")
00645 readline.parse_and_bind("tab: complete")
00646 if os.path.exists (historyPath) :
00647 readline.read_history_file(historyPath)
00648 readline.set_history_length(-1)
00649
00650
00651
00652
00653
00654
00655 obj = VarParsing ('standard')
00656 obj.register ('someVar',
00657 mult=VarParsing.multiplicity.singleton,
00658 info="for testing")
00659 obj.setupTags (tag = "someCondition",
00660 ifCond = "someVar")
00661
00662
00663
00664
00665
00666
00667 obj.parseArguments()
00668