LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
configField.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2013 LSST Corporation.
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 <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import traceback
23 
24 from .config import Config, Field, FieldValidationError, _joinNamePath, _typeStr
25 from .comparison import compareConfigs, getComparisonName
26 
27 __all__ = ["ConfigField"]
28 
29 class ConfigField(Field):
30  """
31  Defines a field which is itself a Config.
32 
33  The behavior of this type of field is much like that of the base Field type.
34 
35  Note that dtype must be a subclass of Config.
36 
37  If default=None, the field will default to a default-constructed
38  instance of dtype.
39 
40  Additionally, to allow for fewer deep-copies, assigning an instance of
41  ConfigField to dtype itself, is considered equivalent to assigning a
42  default-constructed sub-config. This means that the argument default can be
43  dtype, as well as an instance of dtype.
44 
45  Assigning to ConfigField will update all of the fields in the config.
46  """
47 
48  def __init__(self, doc, dtype, default=None, check=None):
49  if not issubclass(dtype, Config):
50  raise ValueError("dtype=%s is not a subclass of Config" % \
51  _typeStr(dtype))
52  if default is None:
53  default = dtype
54  source = traceback.extract_stack(limit=2)[0]
55  self._setup( doc=doc, dtype=dtype, default=default, check=check,
56  optional=False, source=source)
57 
58  def __get__(self, instance, owner=None):
59  if instance is None or not isinstance(instance, Config):
60  return self
61  else:
62  value = instance._storage.get(self.name, None)
63  if value is None:
64  at = traceback.extract_stack()[:-1]+[self.source]
65  self.__set__(instance, self.default, at=at, label="default")
66  return value
67 
68  def __set__(self, instance, value, at=None, label="assignment"):
69  if instance._frozen:
70  raise FieldValidationError(self, instance, \
71  "Cannot modify a frozen Config")
72  name = _joinNamePath(prefix=instance._name, name=self.name)
73 
74  if value != self.dtype and type(value) != self.dtype:
75  msg = "Value %s is of incorrect type %s. Expected %s" %\
76  (value, _typeStr(value), _typeStr(self.dtype))
77  raise FieldValidationError(self, instance, msg)
78 
79  if at is None:
80  at = traceback.extract_stack()[:-1]
81 
82  oldValue = instance._storage.get(self.name, None)
83  if oldValue is None:
84  if value == self.dtype:
85  instance._storage[self.name] = self.dtype(
86  __name=name, __at=at, __label=label)
87  else:
88  instance._storage[self.name] = self.dtype(
89  __name=name, __at=at, __label=label, **value._storage)
90  else:
91  if value == self.dtype:
92  value = value()
93  oldValue.update(__at=at, __label=label, **value._storage)
94  history = instance._history.setdefault(self.name, [])
95  history.append(("config value set", at, label))
96 
97  def rename(self, instance):
98  value = self.__get__(instance)
99  value._rename(_joinNamePath(instance._name, self.name))
100 
101  def save(self, outfile, instance):
102  value = self.__get__(instance)
103  value._save(outfile)
104 
105  def freeze(self, instance):
106  value = self.__get__(instance)
107  value.freeze()
108 
109  def toDict(self, instance):
110  value = self.__get__(instance)
111  return value.toDict()
112 
113  def validate(self, instance):
114  value = self.__get__(instance)
115  value.validate()
116 
117  if self.check is not None and not self.check(value):
118  msg = "%s is not a valid value"%str(value)
119  raise FieldValidationError(self, instance, msg)
120 
121  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
122  """Helper function for Config.compare; used to compare two fields for equality.
123 
124  @param[in] instance1 LHS Config instance to compare.
125  @param[in] instance2 RHS Config instance to compare.
126  @param[in] shortcut If True, return as soon as an inequality is found.
127  @param[in] rtol Relative tolerance for floating point comparisons.
128  @param[in] atol Absolute tolerance for floating point comparisons.
129  @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
130  to report inequalities.
131 
132  Floating point comparisons are performed by numpy.allclose; refer to that for details.
133  """
134  c1 = getattr(instance1, self.name)
135  c2 = getattr(instance2, self.name)
136  name = getComparisonName(
137  _joinNamePath(instance1._name, self.name),
138  _joinNamePath(instance2._name, self.name)
139  )
140  return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)