Skip to content
Snippets Groups Projects
ConfigHelper.py 4.64 KiB
Newer Older
"""Helper object to identify configuration parameters so we can easily overwrite
them via command line magic or via the steering file

To add additional arguments create either a member variable or a property to a
subclass of the ConfigHelper. To add additional arguments to the add_argument
call for the parser object create an additional member::

  self.member = [1,2]
  self._member_EXTRA = {'help': 'description for parameter',
                        'nargs': '+',
                       }

Marko Petric's avatar
Marko Petric committed

class ConfigHelper(object):
  """Base class for configuration helper"""
Marko Petric's avatar
Marko Petric committed

  # We need a static set of properties, because steeringFile parsing creates new Helper objects!
  _setOfProperties = dict()

Marko Petric's avatar
Marko Petric committed
  def __init__(self):
  def _name(self):
    return self.__class__.__name__

  def _closeProperties(self):
    """Store the list of properties."""
    self._setOfProperties[self._name()] = set(vars(self))

  def _checkProperties(self):
    newProps = set(vars(self))
    if existingProps := self._setOfProperties.get(self._name(), set()):
      if unknownProps := newProps - existingProps:
        raise RuntimeError(f"{self._name()} error: Trying to add unknown propert(y/ies): {unknownProps}!")

  def getOptions(self):
    finalVars = {}

    # get all direct members not starting with underscore
    allVars = vars(self)
    for var, val in allVars.items():
      if not var.startswith('_'):
        extraArgumentsName = "_%s_EXTRA" % var
        options = getattr(self, extraArgumentsName) if hasattr(self, extraArgumentsName) else None
        finalVars[var] = {'default': val}
        if options:
          finalVars[var].update(options)

    # now get things defined with @property
Marko Petric's avatar
Marko Petric committed
    props = [(p, getattr(type(self), p)) for p in dir(type(self)) if isinstance(getattr(type(self), p), property)]
    for propName, prop in props:
Marko Petric's avatar
Marko Petric committed
      optName = "_%s_EXTRA" % propName
      doc = prop.__doc__
      options = getattr(self, optName) if hasattr(self, optName) else None
      finalVars[propName] = {'default': getattr(self, propName)}
      if doc:
        finalVars[propName]['help'] = doc
      if options:
        finalVars[propName].update(options)
Marko Petric's avatar
Marko Petric committed
  def printOptions(self):
    """print all parameters"""
    for opt, val in self.getOptions().items():
      options.append("\n\t'%s': '%s'" % (opt, val['default']))
Marko Petric's avatar
Marko Petric committed
  def setOption(self, name, val):
    """ set the attribute name to val """
    setattr(self, name, val)

  @staticmethod
Marko Petric's avatar
Marko Petric committed
  def makeList(stringVal, sep=" "):
    """returns a list from a string separated by sep"""
    if not stringVal:
Marko Petric's avatar
Marko Petric committed
    if isinstance(stringVal, list):
      return stringVal
    else:
      return stringVal.split(sep)
  @staticmethod
  def makeSet(stringVal, sep=" "):
    """returns a set from a string separated by sep"""
    if not stringVal:
      return set()
    if isinstance(stringVal, (list, set, tuple)):
      return set(stringVal)
    else:
      return set(stringVal.split(sep))

  @staticmethod
  def makeString(container):
    """Return a string that can be parsed by dd4hep into a vector."""
    if not container:
      return ""
    if isinstance(container, set):
      return '{%s}' % ','.join([str(s) for s in container])

Marko Petric's avatar
Marko Petric committed
  def makeTuple(val):
    """ returns a tuple of the string, separators are space or comma """
    myTuple = None
Marko Petric's avatar
Marko Petric committed
    if isinstance(val, tuple):
Marko Petric's avatar
Marko Petric committed
    if isinstance(val, list):
    if isinstance(val, str):
Marko Petric's avatar
Marko Petric committed
      myTuple = tuple([_.strip("(), ") for _ in val.split(sep)])
Marko Petric's avatar
Marko Petric committed
      raise RuntimeError("Cannot parse input value %s" % val)
Marko Petric's avatar
Marko Petric committed
  def makeBool(val):
    """check if val is a bool or a string of true/false, otherwise raise exception"""
    if isinstance(val, bool):
      return val
    elif isinstance(val, str):
      if val.lower() == 'true':
        return True
      elif val.lower() == 'false':
        return False
Marko Petric's avatar
Marko Petric committed
    raise RuntimeError(val)

  @staticmethod
  def addAllHelper(ddsim, parser):
    """all configHelper objects to commandline args"""
    for name, obj in vars(ddsim).items():
        for var, optionsDict in obj.getOptions().items():
          optionsDict['action'] = ('store_true' if var.startswith(("enable", "force"))
                                   else optionsDict.get('action', 'store'))
          parser.add_argument("--%s.%s" % (name, var),
                              dest="%s.%s" % (name, var),
                              **optionsDict
Marko Petric's avatar
Marko Petric committed
                              )