CMS 3D CMS Logo

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