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