OpenMPCD
Configuration.py
1 class Configuration:
2  """
3  Represents an OpenMPCD configuration.
4 
5  Configurations are assumed to be in the libconfig++ format.
6  """
7 
8  def __init__(self, source = None, canonicalize = True):
9  """
10  The constructor.
11 
12  @param[in] source
13  If `None`, constructs an empty configuration.
14  If the parameter is a string that corresponds to a file, it
15  is loaded as the configuration file.
16  If the string corresponds to a directory, it is assumed that
17  this directory contains a file `config.txt`, which is loaded.
18  Otherwise, it is assumed that the parameter contains a string
19  representation of a configuration.
20  @param[in] canonicalize
21  Set to true to canonicalize the input configuration, i.e.
22  transform it such that deprecated configuration settings are
23  translated into their new equivalents.
24  """
25 
26  import os.path
27  import pylibconfig2
28 
29  self._config = pylibconfig2.Config()
30 
31  if source is not None:
32  if os.path.isfile(source):
33  self._config.read_file(source)
34  elif os.path.isdir(source):
35  self._config.read_file(source + "/config.txt")
36  else:
37  self._config.read_string(source)
38 
39  if canonicalize:
40  self._canonicalize()
41 
42 
43  def createGroup(self, name):
44  """
45  Creates a group of the given name, if it does not exist, and returns it.
46 
47  This will create groups recursively, if necessary.
48  """
49 
50  import pylibconfig2
51 
52  if self.__contains__(name):
53  ret = self._config.get(name)
54  if not isinstance(ret, pylibconfig2.ConfGroup):
55  raise Exception()
56 
57  return ret
58 
59  components = name.split(".")
60  current = self._config
61  for component in components:
62  tmp = Configuration()
63  tmp._config = current
64  if component not in tmp:
65  current.set(component, pylibconfig2.ConfGroup())
66  current = current.get(component)
67 
68  if not isinstance(current, pylibconfig2.ConfGroup):
69  raise Exception
70 
71  return self.__getitem__(name)
72 
73 
74  def isEquivalent(self, rhs, pathTranslators = []):
75  """
76  Returns whether this configuration is equivalent to `rhs`, i.e. if all
77  settings in this configuration exist and have the same values in `rhs`
78  and vice versa.
79 
80  This method does not discriminate what `libconfig` calls arrays and
81  lists.
82 
83  @throw TypeError
84  Throws if `rhs` is not of type `Configuration`.
85  @throw TypeError
86  Throws if `pathTranslators` is not a list of elements that
87  `callable()` returns `True` for.
88 
89  @param[in] rhs
90  The instance to compare to.
91  @param[in] pathTranslators
92  Any configuration value string (on both this instance and
93  `rhs`) will be successively replaced by the results of
94  calling the functions in `pathTranslators` on that string.
95  """
96 
97  def applyTanslators(s, translators):
98  for translator in translators:
99  s = translator(s)
100  return s
101 
102  if not isinstance(rhs, Configuration):
103  raise TypeError()
104  if not isinstance(pathTranslators, list):
105  raise TypeError()
106  for translator in pathTranslators:
107  if not callable(translator):
108  raise TypeError()
109 
110  import pylibconfig2
111  listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
112  if isinstance(self._config, listTypes):
113  if not isinstance(rhs._config, listTypes):
114  return False
115  if len(self._config) != len(rhs._config):
116  return False
117 
118  for i in range(0, len(self._config)):
119  key = "[" + str(i) + "]"
120  if key not in rhs:
121  return False
122  if isinstance(self[key], self.__class__):
123  if not isinstance(rhs[key], self.__class__):
124  return False
125  if not self[key].isEquivalent(rhs[key], pathTranslators):
126  return False
127  else:
128  lhsValue = self[key]
129  rhsValue = rhs[key]
130 
131  if type(lhsValue) != type(rhsValue):
132  return False
133 
134  if isinstance(lhsValue, str):
135  lhsString = applyTanslators(lhsValue, pathTranslators)
136  rhsString = applyTanslators(rhsValue, pathTranslators)
137  if lhsString != rhsString:
138  return False
139  else:
140  if lhsValue != rhsValue:
141  return False
142 
143  return True
144 
145  if isinstance(rhs._config, listTypes):
146  return False
147 
148  for key in self._config.__dict__:
149  if key not in rhs:
150  return False
151 
152  if isinstance(self[key], Configuration):
153  if not isinstance(rhs[key], Configuration):
154  return False
155  if not self[key].isEquivalent(rhs[key], pathTranslators):
156  return False
157  elif isinstance(self[key], str):
158  if not isinstance(rhs[key], str):
159  return False
160  lhsString = applyTanslators(self[key], pathTranslators)
161  rhsString = applyTanslators(rhs[key], pathTranslators)
162  if lhsString != rhsString:
163  return False
164  else:
165  if self[key] != rhs[key]:
166  return False
167 
168  for key in rhs._config.__dict__:
169  if key not in self:
170  return False
171 
172  return True
173 
174 
175  def get(self, key):
176  """
177  Alias for `__getitem__`, which makes the interface more compatible to
178  `pylibconfig2.Config`.
179  """
180 
181  return self[key]
182 
183 
185  """
186  Returns this configuration as a dictionary, with each key being a
187  setting name (including the names of the parent setting groups, lists,
188  etc.), and the corresponding dictionary value being the setting's value.
189 
190  Empty groups, lists, and arrays will have `None` as their value.
191  """
192 
193  import pylibconfig2
194 
195  if isinstance(self._config, pylibconfig2.ConfArray):
196  ret = {}
197  for key, value in enumerate(self._config):
198  ret["[" + str(key) + "]"] = value
199  return ret
200 
201  ret = {}
202  for key in self._config.__dict__:
203  if isinstance(self[key], Configuration):
204  subdict = self[key].getAsFlatDictionary()
205  for subkey, subvalue in subdict.items():
206  ret[key + "." + subkey] = subvalue
207  if len(subdict) == 0:
208  ret[key] = None
209  else:
210  ret[key] = self[key]
211 
212  if isinstance(self._config, pylibconfig2.ConfList):
213  for i in range(0, len(self._config)):
214  key = "[" + str(i) + "]"
215  if isinstance(self[key], Configuration):
216  subdict = self[key].getAsFlatDictionary()
217  for subkey, subvalue in subdict.items():
218  ret[key + "." + subkey] = subvalue
219  if len(subdict) == 0:
220  ret[key] = None
221  else:
222  ret[key] = self[key]
223 
224  return ret
225 
226 
227  def getDifferencesAsFlatDictionary(self, rhs):
228  """
229  Returns a dictionary, the keys of which are exactly the setting names
230  that appear in either exactly one of `self` and `rhs`, or occur in both,
231  but with different values.
232 
233  Each key's value is a list of two lists; the first corresponding to
234  `self`, the second to `rhs`. These lists are empty if the setting is not
235  set in the corresponding configuration, and otherwise contain the value
236  that is set in the corresponding configuration.
237 
238  Setting names and values are understood as in `getAsFlatDictionary`.
239 
240  @param[in] rhs
241  The right-hand-side instance of this class.
242  """
243 
244  if not isinstance(rhs, self.__class__):
245  raise TypeError()
246 
247  myDict = self.getAsFlatDictionary()
248  rhsDict = rhs.getAsFlatDictionary()
249 
250  ret = {}
251  for myKey in myDict:
252  if myKey not in rhsDict:
253  ret[myKey] = [[myDict[myKey]], []]
254  elif myDict[myKey] != rhsDict[myKey]:
255  ret[myKey] = [[myDict[myKey]], [rhsDict[myKey]]]
256 
257  for rhsKey in rhsDict:
258  if rhsKey not in myDict:
259  ret[rhsKey] = [[], [rhsDict[rhsKey]]]
260 
261 
262  return ret
263 
264 
265  def saveToParameterFile(self, path, additionalParameters = {}):
266  """
267  Saves the configuration settings in a file with the given `path`.
268 
269  The first line contains the setting names, ordered lexicographically and
270  separated by tab characters;
271  the second line contains the corresponding values, with string values
272  enclosed in curly braces, empty groups being represented by the string
273  (not enclosed in curly braces) `None`,
274  and with boolean values being represented by the strings `true` and
275  `false`.
276 
277  This format is supposed to be readable by the `LaTeX` package
278  `pgfplotstable`.
279 
280  @throw TypeError
281  Throws if any argument has an invalid type.
282 
283  @param[in] path
284  The file path to save the output to, as a string.
285  @param[in] additionalParameters
286  A dictionary containing, the keys of which are strings that
287  describe addition parameters to write to the file, while the
288  repsective values describe the parameter values.
289  """
290 
291  if not isinstance(path, str):
292  raise TypeError()
293 
294  if not isinstance(additionalParameters, dict):
295  raise TypeError()
296  for key in additionalParameters:
297  if not isinstance(key, str):
298  raise TypeError()
299 
300 
301  flatDict = self.getAsFlatDictionary()
302  for key, value in additionalParameters.items():
303  assert key not in flatDict
304  flatDict[key] = value
305 
306  headerLine = ""
307  valueLine = ""
308  for key in sorted(flatDict):
309  if headerLine:
310  headerLine += "\t"
311  valueLine += "\t"
312 
313  headerLine += key
314 
315  value = flatDict[key]
316  if isinstance(value, str):
317  valueLine += "{" + value + "}"
318  elif value is None:
319  valueLine += "None"
320  elif isinstance(value, bool):
321  if value:
322  valueLine += "true"
323  else:
324  valueLine += "false"
325  else:
326  valueLine += str(value)
327 
328  with open(path, "w") as f:
329  f.write(headerLine + "\n" + valueLine)
330 
331 
332  def __getitem__(self, key):
333  """
334  Returns the value of the given key.
335 
336  @throw TypeError
337  Throws if `key` is not of type `str`.
338  @throw KeyError
339  Throws if `key` is malformed or does not exist.
340 
341  @param[in] key
342  The key to retrieve.
343  """
344 
345  import pylibconfig2
346 
347  if not isinstance(key, str):
348  raise TypeError
349 
350  listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
351  pylibconfigBaseTypes = listTypes + (pylibconfig2.ConfGroup,)
352 
353  if isinstance(self._config, pylibconfig2.Config):
354  ret = self._config.lookup(key)
355  elif isinstance(self._config, pylibconfig2.ConfGroup):
356  components = key.split(".")
357  ret = self._config
358  for i, component in enumerate(components):
359  if component not in ret.keys():
360  raise KeyError(key)
361  ret = ret.get(component)
362  if isinstance(ret, listTypes):
363  tmp = Configuration()
364  tmp._config = ret
365  if i + 1 == len(components):
366  return tmp
367 
368  newKey = ".".join(components[i + 1:])
369  return tmp[newKey]
370  elif isinstance(self._config, listTypes):
371  components = key.split(".", 1)
372  numeral = components[0]
373  if numeral[0] != "[" or numeral[-1] != "]":
374  raise KeyError(key)
375  number = int(numeral[1:-1])
376  if str(number) != numeral[1:-1]:
377  raise KeyError(key)
378 
379  ret = self._config[number]
380 
381  if isinstance(ret, pylibconfigBaseTypes):
382  tmp = Configuration()
383  tmp._config = ret
384 
385  if len(components) == 1:
386  return tmp
387 
388  return tmp[".".join(components[1:])]
389 
390  return ret
391  else:
392  raise Exception
393 
394  if ret is None:
395  raise KeyError(key)
396 
397  if isinstance(ret, pylibconfigBaseTypes):
398  ret2 = Configuration()
399  ret2._config = ret
400  return ret2
401 
402  return ret
403 
404 
405  def __setitem__(self, key, value):
406  """
407  Sets the given configuration key to the given value.
408 
409  Groups will be created as needed, if they do not exist already.
410 
411  @throw TypeError
412  Throws if `key` is not of type `str`.
413  @throw KeyError
414  Throws if `key` is malformed.
415 
416  @param[in] key
417  The configuration key.
418  @param[in] value
419  The configuration value.
420  """
421 
422  if not isinstance(key, str):
423  raise TypeError()
424  if len(key) == 0:
425  raise KeyError()
426 
427  components = key.split(".")
428 
429  for component in components:
430  if len(component) == 0:
431  raise KeyError()
432 
433  if isinstance(value, Configuration):
434  value = value._config
435 
436  if len(components) == 1:
437  self._config.set(key, value)
438  else:
439  group = self
440  for component in components[:-1]:
441  if not component in group:
442  if component[0] == "[":
443  if not component[-1] == "]":
444  raise KeyError(key)
445  raise Exception("not implemented")
446  group.createGroup(component)
447  group = group[component]
448 
449  import pylibconfig2
450  if isinstance(group._config, pylibconfig2.ConfArray):
451  component = components[-1]
452  if component[0] != "[" or component[-1] != "]":
453  raise KeyError(key)
454  number = int(component[1:-1])
455  group._config[number] = value
456  else:
457  group._config.set(components[-1], value)
458 
459 
460  def __delitem__(self, key):
461  """
462  Deletes the given setting recursively, if it exists.
463 
464  @throw TypeError
465  Throws if `key` is not an instance of `str`.
466 
467  @param[in] key
468  The setting to delete, as a string.
469  """
470 
471  if not isinstance(key, str):
472  raise TypeError()
473 
474  if key not in self:
475  return
476 
477  components = key.split(".")
478 
479  if len(components) == 1:
480  del self._config.__dict__[key]
481  return
482 
483  parent = self[".".join(components[:-1])]
484  del parent[components[-1]]
485 
486 
487  def __contains__(self, key):
488  """
489  Returns whether the given configuration `key` has been set.
490 
491  @throw TypeError
492  Throws if `key` is not an instance of `str`.
493 
494  @param[in] key
495  The setting to look up, as a string.
496  """
497 
498  import pylibconfig2
499 
500  if not isinstance(key, str):
501  raise KeyError
502 
503  if isinstance(self._config, pylibconfig2.Config):
504  try:
505  return self._config.lookup(key) is not None
506  except pylibconfig2.ParseException:
507  return False
508 
509  if isinstance(self._config, pylibconfig2.ConfGroup):
510  components = key.split(".")
511  ret = self._config
512  for component in components:
513  if component not in ret.keys():
514  return False
515  ret = ret.get(component)
516 
517  return True
518 
519  listTypes = (pylibconfig2.ConfList, pylibconfig2.ConfArray)
520  if isinstance(self._config, listTypes):
521  components = key.split(".", 1)
522  numeral = components[0]
523  if numeral[0] != "[" or numeral[-1] != "]":
524  raise KeyError(key)
525  number = int(numeral[1:-1])
526  if str(number) != numeral[1:-1]:
527  raise KeyError(key)
528 
529  try:
530  self._config[number]
531  except IndexError:
532  return False
533  return True
534 
535  raise Exception()
536 
537 
538  def __repr__(self):
539  """
540  Returns a `str` representation of this instance.
541 
542  This representation can be used to construct another instance of this
543  class that is equivalent to this instance.
544  """
545 
546  return self._config.__repr__()
547 
548 
549  def _canonicalize(self):
550  """
551  Transforms this instance's configuration data such that deprecated
552  settings are translated into their replacements.
553  """
554 
555  if "boundaryConditions.type" in self:
556  if self["boundaryConditions.type"] == "Lees-Edwards":
557  shearRate = self["boundaryConditions.shearRate"]
558  del self["boundaryConditions.type"]
559  del self["boundaryConditions.shearRate"]
560  self["boundaryConditions.LeesEdwards.shearRate"] = shearRate
561  else:
562  raise RuntimeError("Unknown boundary condition")
563 
564  if "instrumentation.cellSubdivision" in self:
565  assert "instrumentation.flowProfile" not in self
566  assert "instrumentation.densityProfile" not in self
567 
568  cellSubdivision = self["instrumentation.cellSubdivision"]
569  del self["instrumentation.cellSubdivision"]
570 
571  self["instrumentation.flowProfile.cellSubdivision"] = \
572  cellSubdivision
573  self["instrumentation.densityProfile.cellSubdivision"] = \
574  cellSubdivision
MPCDAnalysis.Configuration.Configuration.__repr__
def __repr__(self)
Definition: Configuration.py:557
MPCDAnalysis.Configuration.Configuration.createGroup
def createGroup(self, name)
Definition: Configuration.py:51
MPCDAnalysis.Configuration.Configuration.__setitem__
def __setitem__(self, key, value)
Definition: Configuration.py:430
MPCDAnalysis.Configuration.Configuration.__delitem__
def __delitem__(self, key)
Definition: Configuration.py:480
MPCDAnalysis.Configuration.Configuration.getDifferencesAsFlatDictionary
def getDifferencesAsFlatDictionary(self, rhs)
Definition: Configuration.py:249
MPCDAnalysis.Configuration.Configuration._config
_config
Definition: Configuration.py:31
MPCDAnalysis.Configuration.Configuration.__contains__
def __contains__(self, key)
Definition: Configuration.py:508
MPCDAnalysis.Configuration.Configuration.isEquivalent
def isEquivalent(self, rhs, pathTranslators=[])
Definition: Configuration.py:99
MPCDAnalysis.Configuration.Configuration.saveToParameterFile
def saveToParameterFile(self, path, additionalParameters={})
Definition: Configuration.py:297
MPCDAnalysis.Configuration.Configuration.__getitem__
def __getitem__(self, key)
Definition: Configuration.py:352
MPCDAnalysis.Configuration.Configuration
Definition: Configuration.py:7
MPCDAnalysis.Configuration.Configuration.__init__
def __init__(self, source=None, canonicalize=True)
Definition: Configuration.py:26
MPCDAnalysis.Configuration.Configuration.getAsFlatDictionary
def getAsFlatDictionary(self)
Definition: Configuration.py:197
MPCDAnalysis.Configuration.Configuration.get
def get(self, key)
Definition: Configuration.py:184
MPCDAnalysis.Configuration.Configuration._canonicalize
def _canonicalize(self)
Definition: Configuration.py:567