CMS 3D CMS Logo

VarParsing.py
Go to the documentation of this file.
1 import sys
2 import os
3 import re
4 from pprint import pprint
5 from FWCore.Utilities.Enumerate import Enumerate
6 from FWCore.Utilities.FileUtils import sectionNofTotal
7 
8 class VarParsing (object):
9  """Infrastructure to parse variable definitions passed to cmsRun
10  configuration scripts"""
11 
12 
13  multiplicity = Enumerate ("singleton list", "multiplicity")
14  varType = Enumerate ("bool int float string tagString")
15  commaRE = re.compile (r',')
16  trueRE = re.compile (r'^true$', re.IGNORECASE)
17  falseRE = re.compile (r'^false$', re.IGNORECASE)
18 
19 
20  def __init__ (self, *args):
21  """Class initializer"""
22  # Set everything up first
23  self._singletons = {}
24  self._lists = {}
25  self._register = {}
26  self._beenSet = {}
27  self._info = {}
28  self._types = {}
29  self._maxLength = 0
30  self._tags = {}
31  self._tagOrder = []
32  self._noCommaSplit = {}
33  self._noDefaultClear = {}
35  self._currentlyParsing = False
36  # now play with the rest
37  for arg in args:
38  lower = arg.lower()
39  if lower == 'python':
40  self.register ('storePrepend',
41  '',
42  VarParsing.multiplicity.singleton,
43  VarParsing.varType.string,
44  "Prepend location of files starting "
45  "with '/store'")
46  # Don't continue because we want to process the next
47  # piece, too.
48  if lower == 'analysis' or lower == 'python':
49  # Optionos for cmsRun or FWLite.Python
50  self.register ('maxEvents',
51  -1,
52  VarParsing.multiplicity.singleton,
53  VarParsing.varType.int,
54  "Number of events to process (-1 for all)")
55  self.register ('totalSections',
56  0,
57  VarParsing.multiplicity.singleton,
58  VarParsing.varType.int,
59  "Total number of sections")
60  self.register ('section',
61  0,
62  VarParsing.multiplicity.singleton,
63  VarParsing.varType.int,
64  "This section (from 1..totalSections inclusive)")
65  self.register ('inputFiles',
66  '',
67  VarParsing.multiplicity.list,
68  VarParsing.varType.string,
69  "Files to process")
70  self.register ('secondaryInputFiles',
71  '',
72  VarParsing.multiplicity.list,
73  VarParsing.varType.string,
74  "Second group of files to process (if needed)")
75  self.register ('filePrepend',
76  '',
77  VarParsing.multiplicity.singleton,
78  VarParsing.varType.string,
79  "String to prepend location of all files")
80  self.register ('outputFile',
81  'output.root',
82  VarParsing.multiplicity.singleton,
83  VarParsing.varType.tagString,
84  "Name of output file (if needed)")
85  self.register ('secondaryOutputFile',
86  '',
87  VarParsing.multiplicity.singleton,
88  VarParsing.varType.tagString,
89  "Name of second output file (if needed)")
90  self.register ('tag',
91  '',
92  VarParsing.multiplicity.singleton,
93  VarParsing.varType.string,
94  "tag to add to output filename")
95  self.setupTags (tag = 'numEvent%d',
96  ifCond = 'maxEvents > 0',
97  tagArg = 'maxEvents')
98  self.setupTags (tag = '%s',
99  ifCond = 'tag',
100  tagArg = 'tag')
101  continue
102  # old, depricated, but here for compatibility of older code
103  if lower == "standard":
104  # load in old standard arguments and defaults
105  self.register ('maxEvents',
106  -1,
107  VarParsing.multiplicity.singleton,
108  VarParsing.varType.int,
109  "Number of events to process (-1 for all)")
110  self.register ('files',
111  '',
112  VarParsing.multiplicity.list,
113  VarParsing.varType.string,
114  "Files to process")
115  self.register ('secondaryFiles',
116  '',
117  VarParsing.multiplicity.list,
118  VarParsing.varType.string,
119  "Second group of files to process (if needed)")
120  self.register ('output',
121  'output.root',
122  VarParsing.multiplicity.singleton,
123  VarParsing.varType.tagString,
124  "Name of output file (if needed)")
125  self.register ('secondaryOutput',
126  '',
127  VarParsing.multiplicity.singleton,
128  VarParsing.varType.tagString,
129  "Name of second output file (if needed)")
130  self.setupTags (tag = 'numEvent%d',
131  ifCond = 'maxEvents > 0',
132  tagArg = 'maxEvents')
133  continue
134  # if we're still here, then we've got a rogue arument
135  print "Error: VarParsing.__init__ doesn't understand '%s'" \
136  % arg
137  raise RuntimeError("Failed to create VarParsing object")
138 
139 
140  def setupTags (self, **kwargs):
141  """Sets up information for tags for output names"""
142  necessaryKeys = set (['ifCond', 'tag'])
143  allowedKeys = set (['tagArg'])
144  for key in kwargs.keys():
145  if key in allowedKeys:
146  continue
147  if key in necessaryKeys:
148  necessaryKeys.remove (key)
149  continue
150  # if we're here, then we have a key that's not understood
151  print "Unknown option '%s'" % key
152  raise RuntimeError("Unknown option")
153  if necessaryKeys:
154  # if this is not empty, then we didn't have a key that was
155  # necessary.
156  print "Missing keys: %s" % necessaryKeys
157  raise runtimeError("Missing keys")
158  tag = kwargs.get('tag')
159  del kwargs['tag']
160  self._tags[tag] = kwargs
161  self._tagOrder.append (tag)
162 
163 
164  def parseArguments (self):
165  """Parses command line arguments. Parsing starts just after
166  the name of the configuration script. Parsing will fail if
167  there is not 'xxxx.py'"""
168  self._currentlyParsing = True
169  foundPy = False
170  printStatus = False
171  help = False
172  singleAssign = True
173  for arg in sys.argv:
174  if not foundPy and arg.endswith ('.py'):
175  foundPy = True
176  continue
177  if not foundPy:
178  continue
179  # If we're here, then we can parse to our hearts content.
180  # So, is this a command or a declaration?
181  if arg.count('='):
182  # declaration
183  name, value = arg.split ('=', 1)
184  if name.count('_'):
185  # name with command
186  name, command = name.split ('_', 1)
187  command = command.lower()
188  if command == 'load':
189  self.loadFromFile (name, value)
190  continue
191  if command == 'clear':
192  self.clearList (name)
193  continue
194  # If we're here, then I don't recognize this command
195  print "Unknown command '%s' in '%s_%s" % \
196  (command, name, command)
197  raise RuntimeError("Illegal parsing command")
198  else:
199  # just a name and value
200  if name not in self._register:
201  print "Error: '%s' not registered." \
202  % name
203  raise RuntimeError("Unknown variable")
204  if VarParsing.multiplicity.singleton == \
205  self._register[name]:
206  # singleton
207  if self._beenSet.get (name) and singleAssign:
208  print "Variable '%s' assigned multiple times. Use" \
209  , "'multipleAssign' command to avoid"
210  raise RuntimeError("Multiple assignment")
211  self._beenSet[name] = True
212  self.setDefault (name, value)
213  else:
214  # list
215  self.setDefault (name, value)
216  else:
217  # commands
218  if arg.count('_'):
219  # name modifier
220  name, command = arg.split ('_', 1)
221  command = command.lower()
222  if name not in self._register:
223  print "Error: '%s' not registered." \
224  % name
225  raise RuntimeError("Unknown variable")
226  if command == 'clear':
227  self.clearList (name)
228  continue
229  # if we're still here, complain that we don't
230  # understand this command:
231  print "Do not understand '%s' in '%s'" % (command, arg)
232  raise RuntimeError("Unknown command")
233  else:
234  # simple command
235  command = arg.lower()
236  if command == 'help' or command == '--help':
237  help = True
238  elif command == 'print' or command == '--print':
239  printStatus = True
240  elif command == 'noprint' or command == '--noprint':
241  printStatus = False
242  else:
243  # We don't understand this command
244  print "Do not understand command '%s'" % (arg)
245  raise RuntimeError("Unknown command")
246  # else if declaration
247  ###########################
248  # Post-loading processing #
249  ###########################
250  # sections
251  if 'totalSections' in self._register and \
252  'section' in self._register and \
253  'inputFiles' in self._register and \
254  self.totalSections and self.section:
255  # copy list
256  oldInputFiles = self.inputFiles
257  # clear list
258  self.clearList ('inputFiles')
259  # used old list to make list
260  self.inputFiles = sectionNofTotal (oldInputFiles,
261  self.section,
262  self.totalSections)
263  # storePrepend
264  if 'storePrepend' in self._register and \
265  'inputFiles' in self._register and \
266  self.storePrepend:
267  storeRE = re.compile (r'^/store/')
268  newFileList = []
269  for filename in self.inputFiles:
270  if storeRE.match (filename):
271  filename = self.storePrepend + filename
272  newFileList.append (filename)
273  # clear old list
274  self.clearList ('inputFiles')
275  # set new list as list
276  self.inputFiles = newFileList
277  # filePrepend
278  if 'filePrepend' in self._register and \
279  'inputFiles' in self._register and \
280  self.filePrepend:
281  newFileList = []
282  for filename in self.inputFiles:
283  filename = self.filePrepend + filename
284  newFileList.append (filename)
285  # clear old list
286  self.clearList ('inputFiles')
287  # set new list as list
288  self.inputFiles = newFileList
289  # make sure found the py file
290  if not foundPy:
291  print "VarParsing.parseArguments() Failure: No configuration " + \
292  "file found ending in .py."
293  raise RuntimeError("Invalid configuration ending")
294  if help:
295  self.help()
296  if printStatus:
297  print self
298  self._currentlyParsing = False
299 
300 
301  def clearList (self, name):
302  """Empties all entries from list"""
303  if name not in self._register:
304  print "Error: '%s' not registered." \
305  % name
306  raise RuntimeError("Unknown variable")
307  if self._register[name] != VarParsing.multiplicity.list:
308  print "Error: '%s' is not a list" % name
309  raise RuntimeError("Faulty 'clear' command")
310  # if we're still here, do what we came to do
311  self._lists[name] = []
312 
313 
314  def setNoDefaultClear (self, name, value=True):
315  """Tells lists to not clear default list values when set from
316  command line."""
317  if name not in self._register:
318  print "Error: '%s' not registered." \
319  % name
320  raise RuntimeError("Unknown variable")
321  if self._register[name] != VarParsing.multiplicity.list:
322  print "Error: '%s' is not a list" % name
323  raise RuntimeError("Faulty 'setNoDefaultClear' command")
324  self._noDefaultClear[name] = bool (value)
325 
326 
327  def setNoCommaSplit (self, name, value=True):
328  """Tells lists to not split up values by commas."""
329  if name not in self._register:
330  print "Error: '%s' not registered." \
331  % name
332  raise RuntimeError("Unknown variable")
333  if self._register[name] != VarParsing.multiplicity.list:
334  print "Error: '%s' is not a list" % name
335  raise RuntimeError("Faulty 'setNoCommaSplit' command")
336  self._noCommaSplit[name] = bool (value)
337 
338 
339  def loadFromFile (self, name, filename):
340  """Loads a list from file"""
341  if name not in self._register:
342  print "Error: '%s' not registered." \
343  % name
344  raise RuntimeError("Unknown variable")
345  if self._register[name] != VarParsing.multiplicity.list:
346  print "Error: '%s' is not a list" % name
347  raise RuntimeError("'load' only works for lists")
348  filename = os.path.expanduser (filename)
349  if not os.path.exists (filename):
350  print "Error: '%s' file does not exist."
351  raise RuntimeError("Bad filename")
352  source = open (filename, 'r')
353  for line in source.readlines():
354  line = re.sub (r'#.+$', '', line) # remove comment characters
355  line = line.strip()
356  if len (line):
357  self._lists[name].append( self._convert (name, line ) )
358  source.close()
359 
360 
361  def help (self):
362  """Prints out help information and exits"""
363  print self
364  print """Options:
365  help : This screen
366  multipleAssign : Allows singletons to have multiple assigments
367  print : Prints out current values
368  XXX_clear : Clears list named 'XXX'
369  """
370  sys.exit (0)
371 
372 
373  def register (self, name,
374  default = "",
375  mult = multiplicity.singleton,
376  mytype = varType.int,
377  info = "",
378  **kwargs):
379  """Register a variable"""
380  # is type ok?
381  if not VarParsing.multiplicity.isValidValue (mult):
382  print "Error: VarParsing.register() must use ",\
383  "VarParsing.multiplicity."
384  raise RuntimeError("Improper 'mult' value")
385  if not VarParsing.varType.isValidValue (mytype):
386  print "Error: VarParsing.register() must use ",\
387  "VarParsing.varType."
388  raise RuntimeError("Improper 'type' value %s" % mytype)
389  if VarParsing.multiplicity.list == mult and \
390  VarParsing.varType.tagString == mytype:
391  print "Error: 'tagString' can only be used with 'singleton'"
392  raise RuntimeError("Improper registration")
393  # is the name ok
394  if name.count ("_"):
395  print "Error: Name can not contain '_': %s" % name
396  raise RuntimeError("Improper 'name'")
397  # has this been registered before?
398  if name in self._register:
399  # Uh oh
400  print "Error: You can not register a name twice, '%s'" \
401  % name
402  raise RuntimeError("Attempt to re-register variable")
403  self._register[name] = mult
404  self._beenSet[name] = False
405  self._info[name] = info
406  self._types[name] = mytype
407  if len (name) > self._maxLength:
408  self._maxLength = len (name)
409  if VarParsing.multiplicity.singleton == mult:
410  self._singletons[name] = default
411  else:
412  self._lists[name] = []
413  # if it's a list, we only want to use the default if it
414  # does exist.
415  if len (default):
416  self._lists[name].append (default)
417  #######################################
418  ## Process any additional directives ##
419  #######################################
420  # do we want to tell the list to not split command line
421  # arguments by commas?
422  if kwargs.get ('noCommaSplit'):
423  self._noCommaSplit[name] = bool( kwargs['noCommaSplit'] )
424  del kwargs['noCommaSplit']
425  if kwargs.get ('noDefaultClear'):
426  self._noDefaultClear[name] = bool( kwargs['noDefaultClear'] )
427  del kwargs['noDefaultClear']
428  if len (kwargs):
429  raise RuntimeError("register() Unknown arguments %s" % kwargs)
430 
431 
432  def has_key (self, key):
433  """Returns true if a key is registered"""
434  return key in self._register
435 
436 
437  def setType (self, name, mytype):
438  """Change the type of 'name' to 'mytype'"""
439  if not VarParsing.varType.isValidValue (mytype):
440  print "Error: VarParsing.setType() must use ",\
441  "VarParsing.varType."
442  raise RuntimeError("Improper 'type' value")
443  oldVal = self.__getattr__ (name, noTags = True)
444  self._types[name] = mytype
445  self.setDefault (name, oldVal)
446 
447 
448  def setDefault (self, name, *args):
449  """Used to set or change the default of an already registered
450  name"""
451  # has this been registered?
452  if name not in self._register:
453  print "Error: VarParsing.setDefault '%s' not already registered." \
454  % name
455  raise RuntimeError("setDefault without registration")
456  if VarParsing.multiplicity.singleton == self._register[name]:
457  # make sure we only have one value
458  if len (args) != 1:
459  print "Error: VarParsing.setDefault needs exactly 1 ",\
460  "value for '%s'" % name
461  raise RuntimeError("setDefault args problem")
462  self._singletons[name] = self._convert (name, args[0])
463  else:
464  # If:
465  # - We have a list (which we do)
466  # - We are now processing command line parsing
467  # - It has not yet been set after parsing
468  # - We have not explicitly asked for it to be NOT cleared
469  # Then:
470  # - We clear the list
471  if self._currentlyParsing and \
472  not self._setDuringParsing.get(name) and \
473  not self._noDefaultClear.get(name):
474  # All four conditions have been satisfied, so let's go
475  # ahead and clear this.
476  self.clearList (name)
477  # IF we are currently parsing, then tell people I've set
478  # this:
479  if self._currentlyParsing:
480  self._setDuringParsing[name] = True
481  # if args is a tuple and it only has one entry, get rid of
482  # the first level of tupleness:
483  if isinstance (args, tuple) and len (args) == 1:
484  args = args[0]
485  # is this still a tuple
486  if isinstance (args, tuple):
487  mylist = list (args)
488  elif isinstance (args, list):
489  mylist = args
490  else:
491  mylist = []
492  mylist.append (args)
493  if not self._noCommaSplit.get (name):
494  oldList = mylist
495  mylist = []
496  for item in oldList:
497  mylist.extend( VarParsing.commaRE.split( item ) )
498  for item in mylist:
499  self._lists[name].append( self._convert (name, item ) )
500 
501 
502  def _convert (self, name, inputVal):
503  """Converts inputVal to the type required by name"""
504  inputVal = str (inputVal)
505  if self._types[name] == VarParsing.varType.bool:
506  if VarParsing.trueRE.match (inputVal) or '1' == inputVal:
507  return True
508  elif VarParsing.falseRE.match (inputVal) or '0' == inputVal:
509  return False
510  # if we're still here, then we don't have 'true' or 'false'
511  raise RuntimeError("Unknown bool value '%s'. Must be 'true' or 'false'" % inputVal)
512  if self._types[name] == VarParsing.varType.string or \
513  self._types[name] == VarParsing.varType.tagString:
514  return inputVal
515  elif self._types[name] == VarParsing.varType.int:
516  return int (inputVal, 0)
517  elif self._types[name] == VarParsing.varType.float:
518  return float (inputVal)
519  else:
520  raise RuntimeError("Unknown varType")
521 
522 
523  def _withTags (self, name):
524  if name not in self._register:
525  print "Error: '%s' not registered." \
526  % name
527  raise RuntimeError("Unknown variable")
528  if self._register[name] == VarParsing.multiplicity.list:
529  print "Error: '%s' is a list" % name
530  raise RuntimeError("withTags() only works on singletons")
531  retval = self._singletons[name]
532  if retval.endswith ('.root'):
533  retval, garbage = os.path.splitext (retval)
534  reverseOrder = self._tagOrder
535  reverseOrder.reverse()
536  for tag in reverseOrder:
537  tagDict = self._tags[tag]
538  ifCond = tagDict['ifCond']
539  if ifCond.count('%'):
540  pass
541  else:
542  ifCond = "self." + ifCond
543  boolValue = eval (ifCond)
544  tagArg = tagDict.get ('tagArg')
545  if tagArg:
546  evalString = "'%s' %% self.%s" % (tag, tagArg)
547  tag = eval (evalString)
548  if boolValue:
549  retval = retval + "_" + tag
550  return retval + ".root"
551 
552 
553  def __str__ (self):
554  """String form of self"""
555  maxLen = min (self._maxLength, 20)
556  form = " %%-%ds: %%s" % maxLen
557  formInfo = " %%%ds - %%s" % (maxLen - 2)
558  formItem = " %%%ds %%s" % (maxLen - 1)
559  retval = ""
560  if len (self._singletons.keys()):
561  retval = retval + "Singletons:\n"
562  for varName, value in sorted (self._singletons.iteritems()):
563  retval = retval + form % (varName, value) + "\n";
564  if self._info.get(varName):
565  retval = retval + formInfo % ('', self._info[varName]) + "\n"
566  if len (self._singletons.keys()):
567  retval = retval + "Lists:\n"
568  for varName, value in sorted (self._lists.iteritems()):
569  stringValue = "%s" % value
570  if len (stringValue) < 76 - maxLen:
571  retval = retval + form % (varName, value) + "\n"
572  else:
573  varLength = len (value)
574  for index, item in enumerate (value):
575  if index == 0:
576  retval = retval + form % (varName, "['" + item)
577  else:
578  retval = retval + formItem % ('',"'" + item)
579  if index == varLength - 1:
580  retval = retval + "' ]\n"
581  else:
582  retval = retval + "',\n"
583  if self._info.get(varName):
584  retval = retval + formInfo % ('', self._info[varName]) + "\n"
585  return retval
586 
587 
588  def __setattr__ (self, name, value, *extras):
589  """Lets me set internal values, or uses setDefault"""
590  if not name.startswith ("_"):
591  mylist = list (extras)
592  mylist.insert (0, value)
593  self.setDefault (name, *mylist)
594  else:
595  object.__setattr__ (self, name, value)
596 
597 
598  def __getattr__ (self, name, noTags = False):
599  """Lets user get the info they want with obj.name"""
600  if name.startswith ("_"):
601  # internal use
602  return object.__getattribute__ (self, name)
603  else:
604  # user variable
605  if name not in self._register:
606  print "Error: '%s' not already registered." \
607  % name
608  raise RuntimeError("Unknown variable")
609  if VarParsing.multiplicity.singleton == self._register[name]:
610  if VarParsing.varType.tagString == self._types[name] \
611  and not noTags:
612  return self._withTags (name)
613  else:
614  return self._singletons[name]
615  else:
616  return self._lists[name]
617 
618 
619 ##############################################################################
620 ## ######################################################################## ##
621 ## ## ## ##
622 ## ######################################################################## ##
623 ##############################################################################
624 
625 
626 if __name__ == "__main__":
627  #############################################
628  ## Load and save command line history when ##
629  ## running interactively. ##
630  #############################################
631  import os, readline
632  import atexit
633  historyPath = os.path.expanduser("~/.pyhistory")
634 
635 
636  def save_history(historyPath=historyPath):
637  import readline
638  readline.write_history_file(historyPath)
639  if os.path.exists(historyPath):
640  readline.read_history_file(historyPath)
641 
642 
643  atexit.register(save_history)
644  readline.parse_and_bind("set show-all-if-ambiguous on")
645  readline.parse_and_bind("tab: complete")
646  if os.path.exists (historyPath) :
647  readline.read_history_file(historyPath)
648  readline.set_history_length(-1)
649 
650 
651  ############################
652  # Example code starts here #
653  ############################
654 
655  obj = VarParsing ('standard')
656  obj.register ('someVar',
657  mult=VarParsing.multiplicity.singleton,
658  info="for testing")
659  obj.setupTags (tag = "someCondition",
660  ifCond = "someVar")
661  ## obj.register ('numbers',
662  ## mult=VarParsing.multiplicity.list,
663  ## info="Numbers")
664  ## obj.register ('singleNumber',
665  ## mult=VarParsing.multiplicity.singleton,
666  ## info="A single number")
667  obj.parseArguments()
668 
def setupTags(self, kwargs)
Definition: VarParsing.py:140
inputFiles
Post-loading processing #.
Definition: VarParsing.py:260
def _withTags(self, name)
Definition: VarParsing.py:523
def setNoCommaSplit(self, name, value=True)
Definition: VarParsing.py:327
def setType(self, name, mytype)
Definition: VarParsing.py:437
def has_key(self, key)
Definition: VarParsing.py:432
def parseArguments(self)
Definition: VarParsing.py:164
def setNoDefaultClear(self, name, value=True)
Definition: VarParsing.py:314
def save_history(historyPath=historyPath)
Definition: VarParsing.py:636
def register(self, name, default="", mult=multiplicity.singleton, mytype=varType.int, info="", kwargs)
Definition: VarParsing.py:378
def __getattr__(self, name, noTags=False)
Definition: VarParsing.py:598
def loadFromFile(self, name, filename)
Definition: VarParsing.py:339
def __init__(self, args)
Definition: VarParsing.py:20
def setDefault(self, name, args)
Definition: VarParsing.py:448
def __setattr__(self, name, value, extras)
Definition: VarParsing.py:588
def _convert(self, name, inputVal)
Definition: VarParsing.py:502
def clearList(self, name)
Definition: VarParsing.py:301