CMS 3D CMS Logo

betterConfigParser.py
Go to the documentation of this file.
1 from __future__ import print_function
2 from __future__ import absolute_import
3 
4 import configparser as ConfigParser
5 import os
6 import re
7 import copy
8 import collections
9 from .TkAlExceptions import AllInOneError
10 from future.utils import PY3
11 
12 if PY3:
13  unicode = str
14 
15 class AdaptedDict(collections.OrderedDict):
16  """
17  Dictionary which handles updates of values for already existing keys
18  in a modified way.
19  adapteddict[key] returns a list of all values associated with key
20  This dictionary is used in the class `BetterConfigParser` instead of the
21  default `dict_type` of the `ConfigParser` class.
22  """
23 
24  def __init__(self, *args, **kwargs):
25  self.validationslist = []
26  collections.OrderedDict.__init__(self, *args, **kwargs)
27 
28  def __setitem__(self, key, value, dict_setitem=collections.OrderedDict.__setitem__):
29  """
30  od.__setitem__(i, y) <==> od[i]=y
31  Updating an existing key appends the new value to the old value
32  instead of replacing it.
33 
34  Arguments:
35  - `key`: key part of the key-value pair
36  - `value`: value part of the key-value pair
37  - `dict_item`: method which is used for finally setting the item
38  """
39 
40  if key != "__name__" and "__name__" in self and self["__name__"]=="validation":
41  if isinstance(value, (str, unicode)):
42  for index, item in enumerate(self.validationslist[:]):
43  if item == (key, value.split("\n")):
44  self.validationslist[index] = (key, value)
45  return
46  self.validationslist.append((key, value))
47  else:
48  dict_setitem(self, key, value)
49 
50  def __getitem__(self, key):
51  if key != "__name__" and "__name__" in self and self["__name__"]=="validation":
52  return [validation[1] for validation in self.validationslist if validation[0] == key]
53  else:
54  return collections.OrderedDict.__getitem__(self, key)
55 
56  def items(self):
57  if "__name__" in self and self["__name__"]=="validation":
58  return self.validationslist
59  else:
60  return collections.OrderedDict.items(self)
61 
62 class BetterConfigParser(ConfigParser.ConfigParser):
63  def __init__(self):
64  ConfigParser.ConfigParser.__init__(self,dict_type=AdaptedDict)
66 
67  def optionxform(self, optionstr):
68  return optionstr
69 
70  def exists( self, section, option):
71  try:
72  items = self.items(section)
73  except ConfigParser.NoSectionError:
74  return False
75  for item in items:
76  if item[0] == option:
77  return True
78  return False
79 
80  def __updateDict( self, dictionary, section ):
81  result = dictionary
82  try:
83  for option in self.options( section ):
84  result[option] = self.get( section, option )
85  if "local"+section.title() in self.sections():
86  for option in self.options( "local"+section.title() ):
87  result[option] = self.get( "local"+section.title(),
88  option )
89  except ConfigParser.NoSectionError as section:
90  msg = ("%s in configuration files. This section is mandatory."
91  %(str(section).replace(":", "", 1)))
92  raise AllInOneError(msg)
93  return result
94 
95  def getResultingSection( self, section, defaultDict = {}, demandPars = [] ):
96  result = copy.deepcopy(defaultDict)
97  for option in demandPars:
98  try:
99  result[option] = self.get( section, option )
100  except ConfigParser.NoOptionError as globalSectionError:
101  globalSection = str( globalSectionError ).split( "'" )[-2]
102  splittedSectionName = section.split( ":" )
103  if len( splittedSectionName ) > 1:
104  localSection = ("local"+section.split( ":" )[0].title()+":"
105  +section.split(":")[1])
106  else:
107  localSection = ("local"+section.split( ":" )[0].title())
108  if self.has_section( localSection ):
109  try:
110  result[option] = self.get( localSection, option )
111  except ConfigParser.NoOptionError as option:
112  msg = ("%s. This option is mandatory."
113  %(str(option).replace(":", "", 1).replace(
114  "section",
115  "section '"+globalSection+"' or", 1)))
116  raise AllInOneError(msg)
117  else:
118  msg = ("%s. This option is mandatory."
119  %(str(globalSectionError).replace(":", "", 1)))
120  raise AllInOneError(msg)
121  try:
122  result = self.__updateDict( result, section )
123  except AllInOneError: #section doesn't exist
124  if demandPars: #then there's at least one mandatory parameter, which means the section needs to be there
125  raise #otherwise all the parameters are optional, so it's ok
126  return result
127 
128  def getAlignments( self ):
129  alignments = []
130  for section in self.sections():
131  if "alignment:" in section:
132  alignments.append( Alignment( section.split( "alignment:" )[1],
133  self ) )
134  names_after_cleaning = [alignment.name for alignment in alignments]
135  duplicates = [name
136  for name, count
137  in collections.Counter(names_after_cleaning).items()
138  if count > 1]
139  if len(duplicates) > 0:
140  msg = "Duplicate alignment names after removing invalid characters: "
141  msg += ", ".join(duplicates) +"\n"
142  msg += "Please rename the alignments to avoid name clashes."
143  raise AllInOneError(msg)
144  return alignments
145 
146  def getCompares( self ):
147  compares = {}
148  for section in self.sections():
149  if "compare:" in section:
150  self.checkInput(section,
151  knownSimpleOptions = ["levels", "dbOutput","moduleList","modulesToPlot","useDefaultRange","plotOnlyGlobal","plotPng","makeProfilePlots",
152  "dx_min","dx_max","dy_min","dy_max","dz_min","dz_max","dr_min","dr_max","rdphi_min","rdphi_max",
153  "dalpha_min","dalpha_max","dbeta_min","dbeta_max","dgamma_min","dgamma_max",
154  "jobmode", "3DSubdetector1", "3Dubdetector2", "3DTranslationalScaleFactor", "jobid", "multiIOV"])
155  levels = self.get( section, "levels" )
156  dbOutput = self.get( section, "dbOutput" )
157  compares[section.split(":")[1]] = ( levels, dbOutput )
158  return compares
159 
160  def getGeneral( self ):
161  defaults = {
162  "jobmode":"interactive",
163  "datadir":os.getcwd(),
164  "logdir":os.getcwd(),
165  }
166  mandatories = ["eosdir",]
167  self.checkInput("general", knownSimpleOptions = list(defaults.keys()) + mandatories )
168  general = self.getResultingSection( "general", defaultDict = defaults, demandPars = mandatories )
169  internal_section = "internals"
170  if not self.has_section(internal_section):
171  self.add_section(internal_section)
172  if not self.has_option(internal_section, "workdir"):
173  self.set(internal_section, "workdir", "/tmp/$USER")
174  if not self.has_option(internal_section, "scriptsdir"):
175  self.set(internal_section, "scriptsdir", "")
176  #replaceByMap will fail if this is not replaced (which it is in validateAlignments.py)
177 
178  general["workdir"] = self.get(internal_section, "workdir")
179  general["scriptsdir"] = self.get(internal_section, "scriptsdir")
180  for folder in "workdir", "datadir", "logdir", "eosdir":
181  general[folder] = os.path.expandvars(general[folder])
182 
183  return general
184 
185  def checkInput(self, section, knownSimpleOptions=[], knownKeywords=[],
186  ignoreOptions=[]):
187  """
188  Method which checks, if the given options in `section` are in the
189  list of `knownSimpleOptions` or match an item of `knownKeywords`.
190  This is basically a check for typos and wrong parameters.
191 
192  Arguments:
193  - `section`: Section of a configuration file
194  - `knownSimpleOptions`: List of allowed simple options in `section`.
195  - `knownKeywords`: List of allowed keywords in `section`.
196  """
197 
198  try:
199  for option in self.options( section ):
200  if option in knownSimpleOptions:
201  continue
202  elif option.split()[0] in knownKeywords:
203  continue
204  elif option in ignoreOptions:
205  print ("Ignoring option '%s' in section '[%s]'."
206  %(option, section))
207  else:
208  msg = ("Invalid or unknown parameter '%s' in section '%s'!"
209  %(option, section))
210  raise AllInOneError(msg)
211  except ConfigParser.NoSectionError:
212  pass
213 
214  def set(self, section, option, value=None):
215  try:
216  ConfigParser.ConfigParser.set(self, section, option, value)
217  except ConfigParser.NoSectionError:
218  self.add_section(section)
219  ConfigParser.ConfigParser.set(self, section, option, value)
220 
221  def items(self, section, raw=False, vars=None):
222  if section == "validation":
223  if raw or vars:
224  raise NotImplementedError("'raw' and 'vars' do not work for betterConfigParser.items()!")
225  items = self._sections["validation"].items()
226  return items
227  else:
228  return ConfigParser.ConfigParser.items(self, section, raw, vars)
229 
230  def write(self, fp):
231  """Write an .ini-format representation of the configuration state."""
232  for section in self._sections:
233  fp.write("[%s]\n" % section)
234  for (key, value) in self._sections[section].items():
235  if key == "__name__" or not isinstance(value, (str, unicode)):
236  continue
237  if value is not None:
238  key = " = ".join((key, str(value).replace('\n', '\n\t')))
239  fp.write("%s\n" % (key))
240  fp.write("\n")
241 
242 
243  #Preexisting validations in the validation section have syntax:
244  # preexistingoffline myoffline
245  #with no = or :. This regex takes care of that.
246  OPTCRE_VALIDATION = re.compile(
247  r'(?P<option>'
248  r'(?P<preexisting>preexisting)?'
249  r'[^:=\s][^:=]*)' # very permissive!
250  r'\s*(?(preexisting)|' # IF preexisting does not exist:
251  r'(?P<vi>[:=])\s*' # any number of space/tab,
252  # followed by separator
253  # (either : or =), followed
254  # by any # space/tab
255  r'(?P<value>.*))$' # everything up to eol
256  )
def getResultingSection(self, section, defaultDict={}, demandPars=[])
def replace(string, replacements)
def __updateDict(self, dictionary, section)
def checkInput(self, section, knownSimpleOptions=[], knownKeywords=[], ignoreOptions=[])
def __init__(self, args, kwargs)
def items(self, section, raw=False, vars=None)
def __setitem__(self, key, value, dict_setitem=collections.OrderedDict.__setitem__)
static std::string join(char **cmd)
Definition: RemoteFile.cc:21
def set(self, section, option, value=None)
#define str(s)