lsst.pex.config  14.0-1-gbcd7061+1
config.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 oldStringType = str # Need to keep hold of original str type
23 from builtins import str
24 from builtins import object
25 from past.builtins import long
26 from past.builtins import basestring
27 from past.builtins import unicode
28 
29 import os
30 import io
31 import sys
32 import math
33 import copy
34 import tempfile
35 import shutil
36 
37 from .comparison import getComparisonName, compareScalars, compareConfigs
38 from .callStack import getStackFrame, getCallStack
39 from future.utils import with_metaclass
40 
41 __all__ = ("Config", "Field", "FieldValidationError")
42 
43 
44 def _joinNamePath(prefix=None, name=None, index=None):
45  """
46  Utility function for generating nested configuration names
47  """
48  if not prefix and not name:
49  raise ValueError("Invalid name: cannot be None")
50  elif not name:
51  name = prefix
52  elif prefix and name:
53  name = prefix + "." + name
54 
55  if index is not None:
56  return "%s[%r]" % (name, index)
57  else:
58  return name
59 
60 
61 def _autocast(x, dtype):
62  """
63  If appropriate perform type casting of value x to type dtype,
64  otherwise return the original value x
65  """
66  if dtype == float and isinstance(x, int):
67  return float(x)
68  if dtype == int and isinstance(x, long):
69  return int(x)
70  if isinstance(x, str):
71  return oldStringType(x)
72  return x
73 
74 
75 def _typeStr(x):
76  """
77  Utility function to generate a fully qualified type name.
78 
79  This is used primarily in writing config files to be
80  executed later upon 'load'.
81  """
82  if hasattr(x, '__module__') and hasattr(x, '__name__'):
83  xtype = x
84  else:
85  xtype = type(x)
86  if (sys.version_info.major <= 2 and xtype.__module__ == '__builtin__') or xtype.__module__ == 'builtins':
87  return xtype.__name__
88  else:
89  return "%s.%s" % (xtype.__module__, xtype.__name__)
90 
91 
92 class ConfigMeta(type):
93  """A metaclass for Config
94 
95  Adds a dictionary containing all Field class attributes
96  as a class attribute called '_fields', and adds the name of each field as
97  an instance variable of the field itself (so you don't have to pass the
98  name of the field to the field constructor).
99  """
100  def __init__(self, name, bases, dict_):
101  type.__init__(self, name, bases, dict_)
102  self._fields = {}
103  self._source = getStackFrame()
104 
105  def getFields(classtype):
106  fields = {}
107  bases = list(classtype.__bases__)
108  bases.reverse()
109  for b in bases:
110  fields.update(getFields(b))
111 
112  for k, v in classtype.__dict__.items():
113  if isinstance(v, Field):
114  fields[k] = v
115  return fields
116 
117  fields = getFields(self)
118  for k, v in fields.items():
119  setattr(self, k, copy.deepcopy(v))
120 
121  def __setattr__(self, name, value):
122  if isinstance(value, Field):
123  value.name = name
124  self._fields[name] = value
125  type.__setattr__(self, name, value)
126 
127 
128 class FieldValidationError(ValueError):
129  """
130  Custom exception class which holds additional information useful to
131  debuggin Config errors:
132  - fieldType: type of the Field which incurred the error
133  - fieldName: name of the Field which incurred the error
134  - fullname: fully qualified name of the Field instance
135  - history: full history of all changes to the Field instance
136  - fieldSource: file and line number of the Field definition
137  """
138  def __init__(self, field, config, msg):
139  self.fieldType = type(field)
140  self.fieldName = field.name
141  self.fullname = _joinNamePath(config._name, field.name)
142  self.history = config.history.setdefault(field.name, [])
143  self.fieldSource = field.source
144  self.configSource = config._source
145  error = "%s '%s' failed validation: %s\n"\
146  "For more information read the Field definition at:\n%s"\
147  "And the Config definition at:\n%s" % \
148  (self.fieldType.__name__, self.fullname, msg,
149  self.fieldSource.format(), self.configSource.format())
150  ValueError.__init__(self, error)
151 
152 
153 class Field(object):
154  """A field in a a Config.
155 
156  Instances of Field should be class attributes of Config subclasses:
157  Field only supports basic data types (int, float, complex, bool, str)
158 
159  class Example(Config):
160  myInt = Field(int, "an integer field!", default=0)
161  """
162  # Must be able to support str and future str as we can not guarantee that
163  # code will pass in a future str type on Python 2
164  supportedTypes = set((str, unicode, basestring, oldStringType, bool, float, int, complex))
165 
166  def __init__(self, doc, dtype, default=None, check=None, optional=False):
167  """Initialize a Field.
168 
169  dtype ------ Data type for the field.
170  doc -------- Documentation for the field.
171  default ---- A default value for the field.
172  check ------ A callable to be called with the field value that returns
173  False if the value is invalid. More complex inter-field
174  validation can be written as part of Config validate()
175  method; this will be ignored if set to None.
176  optional --- When False, Config validate() will fail if value is None
177  """
178  if dtype not in self.supportedTypes:
179  raise ValueError("Unsupported Field dtype %s" % _typeStr(dtype))
180 
181  # Use standard string type if we are given a future str
182  if dtype == str:
183  dtype = oldStringType
184 
185  source = getStackFrame()
186  self._setup(doc=doc, dtype=dtype, default=default, check=check, optional=optional, source=source)
187 
188  def _setup(self, doc, dtype, default, check, optional, source):
189  """
190  Convenience function provided to simplify initialization of derived
191  Field types
192  """
193  self.dtype = dtype
194  self.doc = doc
195  self.__doc__ = doc
196  self.default = default
197  self.check = check
198  self.optional = optional
199  self.source = source
200 
201  def rename(self, instance):
202  """
203  Rename an instance of this field, not the field itself.
204  This is invoked by the owning config object and should not be called
205  directly
206 
207  Only useful for fields which hold sub-configs.
208  Fields which hold subconfigs should rename each sub-config with
209  the full field name as generated by _joinNamePath
210  """
211  pass
212 
213  def validate(self, instance):
214  """
215  Base validation for any field.
216  Ensures that non-optional fields are not None.
217  Ensures type correctness
218  Ensures that user-provided check function is valid
219  Most derived Field types should call Field.validate if they choose
220  to re-implement validate
221  """
222  value = self.__get__(instance)
223  if not self.optional and value is None:
224  raise FieldValidationError(self, instance, "Required value cannot be None")
225 
226  def freeze(self, instance):
227  """
228  Make this field read-only.
229  Only important for fields which hold sub-configs.
230  Fields which hold subconfigs should freeze each sub-config.
231  """
232  pass
233 
234  def _validateValue(self, value):
235  """
236  Validate a value that is not None
237 
238  This is called from __set__
239  This is not part of the Field API. However, simple derived field types
240  may benifit from implementing _validateValue
241  """
242  if value is None:
243  return
244 
245  if not isinstance(value, self.dtype):
246  msg = "Value %s is of incorrect type %s. Expected type %s" % \
247  (value, _typeStr(value), _typeStr(self.dtype))
248  raise TypeError(msg)
249  if self.check is not None and not self.check(value):
250  msg = "Value %s is not a valid value" % str(value)
251  raise ValueError(msg)
252 
253  def save(self, outfile, instance):
254  """
255  Saves an instance of this field to file.
256  This is invoked by the owning config object, and should not be called
257  directly
258 
259  outfile ---- an open output stream.
260  """
261  value = self.__get__(instance)
262  fullname = _joinNamePath(instance._name, self.name)
263 
264  # write full documentation string as comment lines (i.e. first character is #)
265  doc = "# " + str(self.doc).replace("\n", "\n# ")
266  if isinstance(value, float) and (math.isinf(value) or math.isnan(value)):
267  # non-finite numbers need special care
268  outfile.write(u"{}\n{}=float('{!r}')\n\n".format(doc, fullname, value))
269  else:
270  outfile.write(u"{}\n{}={!r}\n\n".format(doc, fullname, value))
271 
272  def toDict(self, instance):
273  """
274  Convert the field value so that it can be set as the value of an item
275  in a dict.
276  This is invoked by the owning config object and should not be called
277  directly
278 
279  Simple values are passed through. Complex data structures must be
280  manipulated. For example, a field holding a sub-config should, instead
281  of the subconfig object, return a dict where the keys are the field
282  names in the subconfig, and the values are the field values in the
283  subconfig.
284  """
285  return self.__get__(instance)
286 
287  def __get__(self, instance, owner=None, at=None, label="default"):
288  """
289  Define how attribute access should occur on the Config instance
290  This is invoked by the owning config object and should not be called
291  directly
292 
293  When the field attribute is accessed on a Config class object, it
294  returns the field object itself in order to allow inspection of
295  Config classes.
296 
297  When the field attribute is access on a config instance, the actual
298  value described by the field (and held by the Config instance) is
299  returned.
300  """
301  if instance is None or not isinstance(instance, Config):
302  return self
303  else:
304  return instance._storage[self.name]
305 
306  def __set__(self, instance, value, at=None, label='assignment'):
307  """
308  Describe how attribute setting should occur on the config instance.
309  This is invoked by the owning config object and should not be called
310  directly
311 
312  Derived Field classes may need to override the behavior. When overriding
313  __set__, Field authors should follow the following rules:
314  * Do not allow modification of frozen configs
315  * Validate the new value *BEFORE* modifying the field. Except if the
316  new value is None. None is special and no attempt should be made to
317  validate it until Config.validate is called.
318  * Do not modify the Config instance to contain invalid values.
319  * If the field is modified, update the history of the field to reflect the
320  changes
321 
322  In order to decrease the need to implement this method in derived Field
323  types, value validation is performed in the method _validateValue. If
324  only the validation step differs in the derived Field, it is simpler to
325  implement _validateValue than to re-implement __set__. More complicated
326  behavior, however, may require a reimplementation.
327  """
328  if instance._frozen:
329  raise FieldValidationError(self, instance, "Cannot modify a frozen Config")
330 
331  history = instance._history.setdefault(self.name, [])
332  if value is not None:
333  value = _autocast(value, self.dtype)
334  try:
335  self._validateValue(value)
336  except BaseException as e:
337  raise FieldValidationError(self, instance, str(e))
338 
339  instance._storage[self.name] = value
340  if at is None:
341  at = getCallStack()
342  history.append((value, at, label))
343 
344  def __delete__(self, instance, at=None, label='deletion'):
345  """
346  Describe how attribute deletion should occur on the Config instance.
347  This is invoked by the owning config object and should not be called
348  directly
349  """
350  if at is None:
351  at = getCallStack()
352  self.__set__(instance, None, at=at, label=label)
353 
354  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
355  """Helper function for Config.compare; used to compare two fields for equality.
356 
357  Must be overridden by more complex field types.
358 
359  @param[in] instance1 LHS Config instance to compare.
360  @param[in] instance2 RHS Config instance to compare.
361  @param[in] shortcut If True, return as soon as an inequality is found.
362  @param[in] rtol Relative tolerance for floating point comparisons.
363  @param[in] atol Absolute tolerance for floating point comparisons.
364  @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
365  to report inequalities.
366 
367  Floating point comparisons are performed by numpy.allclose; refer to that for details.
368  """
369  v1 = getattr(instance1, self.name)
370  v2 = getattr(instance2, self.name)
371  name = getComparisonName(
372  _joinNamePath(instance1._name, self.name),
373  _joinNamePath(instance2._name, self.name)
374  )
375  return compareScalars(name, v1, v2, dtype=self.dtype, rtol=rtol, atol=atol, output=output)
376 
377 
378 class RecordingImporter(object):
379  """An Importer (for sys.meta_path) that records which modules are being imported.
380 
381  Objects also act as Context Managers, so you can:
382  with RecordingImporter() as importer:
383  import stuff
384  print("Imported: " + importer.getModules())
385  This ensures it is properly uninstalled when done.
386 
387  This class makes no effort to do any importing itself.
388  """
389  def __init__(self):
390  """Create and install the Importer"""
391  self._modules = set()
392 
393  def __enter__(self):
394 
395  self.origMetaPath = sys.meta_path
396  sys.meta_path = [self] + sys.meta_path
397  return self
398 
399  def __exit__(self, *args):
400  self.uninstall()
401  return False # Don't suppress exceptions
402 
403  def uninstall(self):
404  """Uninstall the Importer"""
405  sys.meta_path = self.origMetaPath
406 
407  def find_module(self, fullname, path=None):
408  """Called as part of the 'import' chain of events.
409 
410  We return None because we don't do any importing.
411  """
412  self._modules.add(fullname)
413  return None
414 
415  def getModules(self):
416  """Return the set of modules that were imported."""
417  return self._modules
418 
419 
420 class Config(with_metaclass(ConfigMeta, object)):
421  """Base class for control objects.
422 
423  A Config object will usually have several Field instances as class
424  attributes; these are used to define most of the base class behavior.
425  Simple derived class should be able to be defined simply by setting those
426  attributes.
427 
428  Config also emulates a dict of field name: field value
429  """
430 
431  def __iter__(self):
432  """!Iterate over fields
433  """
434  return self._fields.__iter__()
435 
436  def keys(self):
437  """!Return the list of field names
438  """
439  return list(self._storage.keys())
440 
441  def values(self):
442  """!Return the list of field values
443  """
444  return list(self._storage.values())
445 
446  def items(self):
447  """!Return the list of (field name, field value) pairs
448  """
449  return list(self._storage.items())
450 
451  def iteritems(self):
452  """!Iterate over (field name, field value) pairs
453  """
454  return iter(self._storage.items())
455 
456  def itervalues(self):
457  """!Iterate over field values
458  """
459  return iter(self.storage.values())
460 
461  def iterkeys(self):
462  """!Iterate over field names
463  """
464  return iter(self.storage.keys())
465 
466  def __contains__(self, name):
467  """!Return True if the specified field exists in this config
468 
469  @param[in] name field name to test for
470  """
471  return self._storage.__contains__(name)
472 
473  def __new__(cls, *args, **kw):
474  """!Allocate a new Config object.
475 
476  In order to ensure that all Config object are always in a proper
477  state when handed to users or to derived Config classes, some
478  attributes are handled at allocation time rather than at initialization
479 
480  This ensures that even if a derived Config class implements __init__,
481  the author does not need to be concerned about when or even if he
482  should call the base Config.__init__
483  """
484  name = kw.pop("__name", None)
485  at = kw.pop("__at", getCallStack())
486  # remove __label and ignore it
487  kw.pop("__label", "default")
488 
489  instance = object.__new__(cls)
490  instance._frozen = False
491  instance._name = name
492  instance._storage = {}
493  instance._history = {}
494  instance._imports = set()
495  # load up defaults
496  for field in instance._fields.values():
497  instance._history[field.name] = []
498  field.__set__(instance, field.default, at=at + [field.source], label="default")
499  # set custom default-overides
500  instance.setDefaults()
501  # set constructor overides
502  instance.update(__at=at, **kw)
503  return instance
504 
505  def __reduce__(self):
506  """Reduction for pickling (function with arguments to reproduce).
507 
508  We need to condense and reconstitute the Config, since it may contain lambdas
509  (as the 'check' elements) that cannot be pickled.
510  """
511  # The stream must be in characters to match the API but pickle requires bytes
512  stream = io.StringIO()
513  self.saveToStream(stream)
514  return (unreduceConfig, (self.__class__, stream.getvalue().encode()))
515 
516  def setDefaults(self):
517  """
518  Derived config classes that must compute defaults rather than using the
519  Field defaults should do so here.
520  To correctly use inherited defaults, implementations of setDefaults()
521  must call their base class' setDefaults()
522  """
523  pass
524 
525  def update(self, **kw):
526  """!Update values specified by the keyword arguments
527 
528  @warning The '__at' and '__label' keyword arguments are special internal
529  keywords. They are used to strip out any internal steps from the
530  history tracebacks of the config. Modifying these keywords allows users
531  to lie about a Config's history. Please do not do so!
532  """
533  at = kw.pop("__at", getCallStack())
534  label = kw.pop("__label", "update")
535 
536  for name, value in kw.items():
537  try:
538  field = self._fields[name]
539  field.__set__(self, value, at=at, label=label)
540  except KeyError:
541  raise KeyError("No field of name %s exists in config type %s" % (name, _typeStr(self)))
542 
543  def load(self, filename, root="config"):
544  """!Modify this config in place by executing the Python code in the named file.
545 
546  @param[in] filename name of file containing config override code
547  @param[in] root name of variable in file that refers to the config being overridden
548 
549  For example: if the value of root is "config" and the file contains this text:
550  "config.myField = 5" then this config's field "myField" is set to 5.
551 
552  @deprecated For purposes of backwards compatibility, older config files that use
553  root="root" instead of root="config" will be loaded with a warning printed to sys.stderr.
554  This feature will be removed at some point.
555  """
556  with open(filename, "r") as f:
557  code = compile(f.read(), filename=filename, mode="exec")
558  self.loadFromStream(stream=code, root=root)
559 
560  def loadFromStream(self, stream, root="config", filename=None):
561  """!Modify this config in place by executing the python code in the provided stream.
562 
563  @param[in] stream open file object, string or compiled string containing config override code
564  @param[in] root name of variable in stream that refers to the config being overridden
565  @param[in] filename name of config override file, or None if unknown or contained
566  in the stream; used for error reporting
567 
568  For example: if the value of root is "config" and the stream contains this text:
569  "config.myField = 5" then this config's field "myField" is set to 5.
570 
571  @deprecated For purposes of backwards compatibility, older config files that use
572  root="root" instead of root="config" will be loaded with a warning printed to sys.stderr.
573  This feature will be removed at some point.
574  """
575  with RecordingImporter() as importer:
576  try:
577  local = {root: self}
578  exec(stream, {}, local)
579  except NameError as e:
580  if root == "config" and "root" in e.args[0]:
581  if filename is None:
582  # try to determine the file name; a compiled string has attribute "co_filename",
583  # an open file has attribute "name", else give up
584  filename = getattr(stream, "co_filename", None)
585  if filename is None:
586  filename = getattr(stream, "name", "?")
587  sys.stderr.write(u"Config override file %r" % (filename,) +
588  u" appears to use 'root' instead of 'config'; trying with 'root'")
589  local = {"root": self}
590  exec(stream, {}, local)
591  else:
592  raise
593 
594  self._imports.update(importer.getModules())
595 
596  def save(self, filename, root="config"):
597  """!Save a python script to the named file, which, when loaded, reproduces this Config
598 
599  @param[in] filename name of file to which to write the config
600  @param[in] root name to use for the root config variable; the same value must be used when loading
601  """
602  d = os.path.dirname(filename)
603  with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=d) as outfile:
604  self.saveToStream(outfile, root)
605  # tempfile is hardcoded to create files with mode '0600'
606  # for an explantion of these antics see:
607  # https://stackoverflow.com/questions/10291131/how-to-use-os-umask-in-python
608  umask = os.umask(0o077)
609  os.umask(umask)
610  os.chmod(outfile.name, (~umask & 0o666))
611  # chmod before the move so we get quasi-atomic behavior if the
612  # source and dest. are on the same filesystem.
613  # os.rename may not work across filesystems
614  shutil.move(outfile.name, filename)
615 
616  def saveToStream(self, outfile, root="config"):
617  """!Save a python script to a stream, which, when loaded, reproduces this Config
618 
619  @param outfile [inout] open file object to which to write the config. Accepts strings not bytes.
620  @param root [in] name to use for the root config variable; the same value must be used when loading
621  """
622  tmp = self._name
623  self._rename(root)
624  try:
625  configType = type(self)
626  typeString = _typeStr(configType)
627  outfile.write(u"import {}\n".format(configType.__module__))
628  outfile.write(u"assert type({})=={}, 'config is of type %s.%s ".format(root, typeString))
629  outfile.write(u"instead of {}' % (type({}).__module__, type({}).__name__)\n".format(typeString,
630  root,
631  root))
632  self._save(outfile)
633  finally:
634  self._rename(tmp)
635 
636  def freeze(self):
637  """!Make this Config and all sub-configs read-only
638  """
639  self._frozen = True
640  for field in self._fields.values():
641  field.freeze(self)
642 
643  def _save(self, outfile):
644  """!Save this Config to an open stream object
645  """
646  for imp in self._imports:
647  if imp in sys.modules and sys.modules[imp] is not None:
648  outfile.write(u"import {}\n".format(imp))
649  for field in self._fields.values():
650  field.save(outfile, self)
651 
652  def toDict(self):
653  """!Return a dict of field name: value
654 
655  Correct behavior is dependent on proper implementation of Field.toDict. If implementing a new
656  Field type, you may need to implement your own toDict method.
657  """
658  dict_ = {}
659  for name, field in self._fields.items():
660  dict_[name] = field.toDict(self)
661  return dict_
662 
663  def _rename(self, name):
664  """!Rename this Config object in its parent config
665 
666  @param[in] name new name for this config in its parent config
667 
668  Correct behavior is dependent on proper implementation of Field.rename. If implementing a new
669  Field type, you may need to implement your own rename method.
670  """
671  self._name = name
672  for field in self._fields.values():
673  field.rename(self)
674 
675  def validate(self):
676  """!Validate the Config; raise an exception if invalid
677 
678  The base class implementation performs type checks on all fields by
679  calling Field.validate().
680 
681  Complex single-field validation can be defined by deriving new Field
682  types. As syntactic sugar, some derived Field types are defined in
683  this module which handle recursing into sub-configs
684  (ConfigField, ConfigChoiceField)
685 
686  Inter-field relationships should only be checked in derived Config
687  classes after calling this method, and base validation is complete
688  """
689  for field in self._fields.values():
690  field.validate(self)
691 
692  def formatHistory(self, name, **kwargs):
693  """!Format the specified config field's history to a more human-readable format
694 
695  @param[in] name name of field whose history is wanted
696  @param[in] kwargs keyword arguments for lsst.pex.config.history.format
697  @return a string containing the formatted history
698  """
699  import lsst.pex.config.history as pexHist
700  return pexHist.format(self, name, **kwargs)
701 
702  """
703  Read-only history property
704  """
705  history = property(lambda x: x._history)
706 
707  def __setattr__(self, attr, value, at=None, label="assignment"):
708  """!Regulate which attributes can be set
709 
710  Unlike normal python objects, Config objects are locked such
711  that no additional attributes nor properties may be added to them
712  dynamically.
713 
714  Although this is not the standard Python behavior, it helps to
715  protect users from accidentally mispelling a field name, or
716  trying to set a non-existent field.
717  """
718  if attr in self._fields:
719  if at is None:
720  at = getCallStack()
721  # This allows Field descriptors to work.
722  self._fields[attr].__set__(self, value, at=at, label=label)
723  elif hasattr(getattr(self.__class__, attr, None), '__set__'):
724  # This allows properties and other non-Field descriptors to work.
725  return object.__setattr__(self, attr, value)
726  elif attr in self.__dict__ or attr in ("_name", "_history", "_storage", "_frozen", "_imports"):
727  # This allows specific private attributes to work.
728  self.__dict__[attr] = value
729  else:
730  # We throw everything else.
731  raise AttributeError("%s has no attribute %s" % (_typeStr(self), attr))
732 
733  def __delattr__(self, attr, at=None, label="deletion"):
734  if attr in self._fields:
735  if at is None:
736  at = getCallStack()
737  self._fields[attr].__delete__(self, at=at, label=label)
738  else:
739  object.__delattr__(self, attr)
740 
741  def __eq__(self, other):
742  if type(other) == type(self):
743  for name in self._fields:
744  thisValue = getattr(self, name)
745  otherValue = getattr(other, name)
746  if isinstance(thisValue, float) and math.isnan(thisValue):
747  if not math.isnan(otherValue):
748  return False
749  elif thisValue != otherValue:
750  return False
751  return True
752  return False
753 
754  def __ne__(self, other):
755  return not self.__eq__(other)
756 
757  def __str__(self):
758  return str(self.toDict())
759 
760  def __repr__(self):
761  return "%s(%s)" % (
762  _typeStr(self),
763  ", ".join("%s=%r" % (k, v) for k, v in self.toDict().items() if v is not None)
764  )
765 
766  def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None):
767  """!Compare two Configs for equality; return True if equal
768 
769  If the Configs contain RegistryFields or ConfigChoiceFields, unselected Configs
770  will not be compared.
771 
772  @param[in] other Config object to compare with self.
773  @param[in] shortcut If True, return as soon as an inequality is found.
774  @param[in] rtol Relative tolerance for floating point comparisons.
775  @param[in] atol Absolute tolerance for floating point comparisons.
776  @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
777  to report inequalities.
778 
779  Floating point comparisons are performed by numpy.allclose; refer to that for details.
780  """
781  name1 = self._name if self._name is not None else "config"
782  name2 = other._name if other._name is not None else "config"
783  name = getComparisonName(name1, name2)
784  return compareConfigs(name, self, other, shortcut=shortcut,
785  rtol=rtol, atol=atol, output=output)
786 
787 
788 def unreduceConfig(cls, stream):
789  config = cls()
790  config.loadFromStream(stream)
791  return config
def toDict(self, instance)
Definition: config.py:272
def __eq__(self, other)
Definition: config.py:741
def formatHistory(self, name, kwargs)
Format the specified config field&#39;s history to a more human-readable format.
Definition: config.py:692
def load(self, filename, root="config")
Modify this config in place by executing the Python code in the named file.
Definition: config.py:543
def _rename(self, name)
Rename this Config object in its parent config.
Definition: config.py:663
def __init__(self, doc, dtype, default=None, check=None, optional=False)
Definition: config.py:166
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
Definition: comparison.py:67
def unreduceConfig(cls, stream)
Definition: config.py:788
def _save(self, outfile)
Save this Config to an open stream object.
Definition: config.py:643
def __init__(self, field, config, msg)
Definition: config.py:138
def saveToStream(self, outfile, root="config")
Save a python script to a stream, which, when loaded, reproduces this Config.
Definition: config.py:616
def __delattr__(self, attr, at=None, label="deletion")
Definition: config.py:733
def getCallStack(skip=0)
Definition: callStack.py:157
def __get__(self, instance, owner=None, at=None, label="default")
Definition: config.py:287
def save(self, filename, root="config")
Save a python script to the named file, which, when loaded, reproduces this Config.
Definition: config.py:596
def iteritems(self)
Iterate over (field name, field value) pairs.
Definition: config.py:451
def __ne__(self, other)
Definition: config.py:754
def _setup(self, doc, dtype, default, check, optional, source)
Definition: config.py:188
def __iter__(self)
Iterate over fields.
Definition: config.py:431
def values(self)
Return the list of field values.
Definition: config.py:441
def getStackFrame(relative=0)
Definition: callStack.py:54
def __contains__(self, name)
Return True if the specified field exists in this config.
Definition: config.py:466
def save(self, outfile, instance)
Definition: config.py:253
def _validateValue(self, value)
Definition: config.py:234
def validate(self, instance)
Definition: config.py:213
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:134
def __init__(self, name, bases, dict_)
Definition: config.py:100
def iterkeys(self)
Iterate over field names.
Definition: config.py:461
def __new__(cls, args, kw)
Allocate a new Config object.
Definition: config.py:473
def validate(self)
Validate the Config; raise an exception if invalid.
Definition: config.py:675
def __setattr__(self, attr, value, at=None, label="assignment")
Regulate which attributes can be set.
Definition: config.py:707
def keys(self)
Return the list of field names.
Definition: config.py:436
def freeze(self, instance)
Definition: config.py:226
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
Definition: comparison.py:41
def itervalues(self)
Iterate over field values.
Definition: config.py:456
def update(self, kw)
Update values specified by the keyword arguments.
Definition: config.py:525
def __set__(self, instance, value, at=None, label='assignment')
Definition: config.py:306
def items(self)
Return the list of (field name, field value) pairs.
Definition: config.py:446
def loadFromStream(self, stream, root="config", filename=None)
Modify this config in place by executing the python code in the provided stream.
Definition: config.py:560
def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
Compare two Configs for equality; return True if equal.
Definition: config.py:766
def toDict(self)
Return a dict of field name: value.
Definition: config.py:652
def getComparisonName(name1, name2)
Definition: comparison.py:35
def rename(self, instance)
Definition: config.py:201
def __setattr__(self, name, value)
Definition: config.py:121
def __delete__(self, instance, at=None, label='deletion')
Definition: config.py:344
def find_module(self, fullname, path=None)
Definition: config.py:407
def freeze(self)
Make this Config and all sub-configs read-only.
Definition: config.py:636