CMS 3D CMS Logo

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