LSSTApplications  17.0+11,17.0+34,17.0+56,17.0+57,17.0+59,17.0+7,17.0-1-g377950a+33,17.0.1-1-g114240f+2,17.0.1-1-g4d4fbc4+28,17.0.1-1-g55520dc+49,17.0.1-1-g5f4ed7e+52,17.0.1-1-g6dd7d69+17,17.0.1-1-g8de6c91+11,17.0.1-1-gb9095d2+7,17.0.1-1-ge9fec5e+5,17.0.1-1-gf4e0155+55,17.0.1-1-gfc65f5f+50,17.0.1-1-gfc6fb1f+20,17.0.1-10-g87f9f3f+1,17.0.1-11-ge9de802+16,17.0.1-16-ga14f7d5c+4,17.0.1-17-gc79d625+1,17.0.1-17-gdae4c4a+8,17.0.1-2-g26618f5+29,17.0.1-2-g54f2ebc+9,17.0.1-2-gf403422+1,17.0.1-20-g2ca2f74+6,17.0.1-23-gf3eadeb7+1,17.0.1-3-g7e86b59+39,17.0.1-3-gb5ca14a,17.0.1-3-gd08d533+40,17.0.1-30-g596af8797,17.0.1-4-g59d126d+4,17.0.1-4-gc69c472+5,17.0.1-6-g5afd9b9+4,17.0.1-7-g35889ee+1,17.0.1-7-gc7c8782+18,17.0.1-9-gc4bbfb2+3,w.2019.22
LSSTDataManagementBasePackage
configDictField.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 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 
23 from .config import Config, FieldValidationError, _autocast, _typeStr, _joinNamePath
24 from .dictField import Dict, DictField
25 from .comparison import compareConfigs, compareScalars, getComparisonName
26 from .callStack import getCallStack, getStackFrame
27 
28 __all__ = ["ConfigDictField"]
29 
30 
32  """Internal representation of a dictionary of configuration classes.
33 
34  Much like `Dict`, `ConfigDict` is a custom `MutableMapper` which tracks
35  the history of changes to any of its items.
36  """
37 
38  def __init__(self, config, field, value, at, label):
39  Dict.__init__(self, config, field, value, at, label, setHistory=False)
40  self.history.append(("Dict initialized", at, label))
41 
42  def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
43  if self._config._frozen:
44  msg = "Cannot modify a frozen Config. "\
45  "Attempting to set item at key %r to value %s" % (k, x)
46  raise FieldValidationError(self._field, self._config, msg)
47 
48  # validate keytype
49  k = _autocast(k, self._field.keytype)
50  if type(k) != self._field.keytype:
51  msg = "Key %r is of type %s, expected type %s" % \
52  (k, _typeStr(k), _typeStr(self._field.keytype))
53  raise FieldValidationError(self._field, self._config, msg)
54 
55  # validate itemtype
56  dtype = self._field.itemtype
57  if type(x) != self._field.itemtype and x != self._field.itemtype:
58  msg = "Value %s at key %r is of incorrect type %s. Expected type %s" % \
59  (x, k, _typeStr(x), _typeStr(self._field.itemtype))
60  raise FieldValidationError(self._field, self._config, msg)
61 
62  if at is None:
63  at = getCallStack()
64  name = _joinNamePath(self._config._name, self._field.name, k)
65  oldValue = self._dict.get(k, None)
66  if oldValue is None:
67  if x == dtype:
68  self._dict[k] = dtype(__name=name, __at=at, __label=label)
69  else:
70  self._dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
71  if setHistory:
72  self.history.append(("Added item at key %s" % k, at, label))
73  else:
74  if x == dtype:
75  x = dtype()
76  oldValue.update(__at=at, __label=label, **x._storage)
77  if setHistory:
78  self.history.append(("Modified item at key %s" % k, at, label))
79 
80  def __delitem__(self, k, at=None, label="delitem"):
81  if at is None:
82  at = getCallStack()
83  Dict.__delitem__(self, k, at, label, False)
84  self.history.append(("Removed item at key %s" % k, at, label))
85 
86 
88  """A configuration field (`~lsst.pex.config.Field` subclass) that is a
89  mapping of keys to `~lsst.pex.config.Config` instances.
90 
91  ``ConfigDictField`` behaves like `DictField` except that the
92  ``itemtype`` must be a `~lsst.pex.config.Config` subclass.
93 
94  Parameters
95  ----------
96  doc : `str`
97  A description of the configuration field.
98  keytype : {`int`, `float`, `complex`, `bool`, `str`}
99  The type of the mapping keys. All keys must have this type.
100  itemtype : `lsst.pex.config.Config`-type
101  The type of the values in the mapping. This must be
102  `~lsst.pex.config.Config` or a subclass.
103  default : optional
104  Unknown.
105  default : ``itemtype``-dtype, optional
106  Default value of this field.
107  optional : `bool`, optional
108  If `True`, this configuration `~lsst.pex.config.Field` is *optional*.
109  Default is `True`.
110 
111  Raises
112  ------
113  ValueError
114  Raised if the inputs are invalid:
115 
116  - ``keytype`` or ``itemtype`` arguments are not supported types
117  (members of `ConfigDictField.supportedTypes`.
118  - ``dictCheck`` or ``itemCheck`` is not a callable function.
119 
120  See also
121  --------
122  ChoiceField
123  ConfigChoiceField
124  ConfigField
125  ConfigurableField
126  DictField
127  Field
128  ListField
129  RangeField
130  RegistryField
131 
132  Notes
133  -----
134  You can use ``ConfigDictField`` to create name-to-config mappings. One use
135  case is for configuring mappings for dataset types in a Butler. In this
136  case, the dataset type names are arbitrary and user-selected while the
137  mapping configurations are known and fixed.
138  """
139 
140  DictClass = ConfigDict
141 
142  def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
143  source = getStackFrame()
144  self._setup(doc=doc, dtype=ConfigDict, default=default, check=None,
145  optional=optional, source=source)
146  if keytype not in self.supportedTypes:
147  raise ValueError("'keytype' %s is not a supported type" %
148  _typeStr(keytype))
149  elif not issubclass(itemtype, Config):
150  raise ValueError("'itemtype' %s is not a supported type" %
151  _typeStr(itemtype))
152  if dictCheck is not None and not hasattr(dictCheck, "__call__"):
153  raise ValueError("'dictCheck' must be callable")
154  if itemCheck is not None and not hasattr(itemCheck, "__call__"):
155  raise ValueError("'itemCheck' must be callable")
156 
157  self.keytype = keytype
158  self.itemtype = itemtype
159  self.dictCheck = dictCheck
160  self.itemCheck = itemCheck
161 
162  def rename(self, instance):
163  configDict = self.__get__(instance)
164  if configDict is not None:
165  for k in configDict:
166  fullname = _joinNamePath(instance._name, self.name, k)
167  configDict[k]._rename(fullname)
168 
169  def validate(self, instance):
170  value = self.__get__(instance)
171  if value is not None:
172  for k in value:
173  item = value[k]
174  item.validate()
175  if self.itemCheck is not None and not self.itemCheck(item):
176  msg = "Item at key %r is not a valid value: %s" % (k, item)
177  raise FieldValidationError(self, instance, msg)
178  DictField.validate(self, instance)
179 
180  def toDict(self, instance):
181  configDict = self.__get__(instance)
182  if configDict is None:
183  return None
184 
185  dict_ = {}
186  for k in configDict:
187  dict_[k] = configDict[k].toDict()
188 
189  return dict_
190 
191  def save(self, outfile, instance):
192  configDict = self.__get__(instance)
193  fullname = _joinNamePath(instance._name, self.name)
194  if configDict is None:
195  outfile.write(u"{}={!r}\n".format(fullname, configDict))
196  return
197 
198  outfile.write(u"{}={!r}\n".format(fullname, {}))
199  for v in configDict.values():
200  outfile.write(u"{}={}()\n".format(v._name, _typeStr(v)))
201  v._save(outfile)
202 
203  def freeze(self, instance):
204  configDict = self.__get__(instance)
205  if configDict is not None:
206  for k in configDict:
207  configDict[k].freeze()
208 
209  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
210  """Compare two fields for equality.
211 
212  Used by `lsst.pex.ConfigDictField.compare`.
213 
214  Parameters
215  ----------
216  instance1 : `lsst.pex.config.Config`
217  Left-hand side config instance to compare.
218  instance2 : `lsst.pex.config.Config`
219  Right-hand side config instance to compare.
220  shortcut : `bool`
221  If `True`, this function returns as soon as an inequality if found.
222  rtol : `float`
223  Relative tolerance for floating point comparisons.
224  atol : `float`
225  Absolute tolerance for floating point comparisons.
226  output : callable
227  A callable that takes a string, used (possibly repeatedly) to report inequalities.
228 
229  Returns
230  -------
231  isEqual : bool
232  `True` if the fields are equal, `False` otherwise.
233 
234  Notes
235  -----
236  Floating point comparisons are performed by `numpy.allclose`.
237  """
238  d1 = getattr(instance1, self.name)
239  d2 = getattr(instance2, self.name)
240  name = getComparisonName(
241  _joinNamePath(instance1._name, self.name),
242  _joinNamePath(instance2._name, self.name)
243  )
244  if not compareScalars("keys for %s" % name, set(d1.keys()), set(d2.keys()), output=output):
245  return False
246  equal = True
247  for k, v1 in d1.items():
248  v2 = d2[k]
249  result = compareConfigs("%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
250  rtol=rtol, atol=atol, output=output)
251  if not result and shortcut:
252  return False
253  equal = equal and result
254  return equal
def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None)
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
Definition: comparison.py:105
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
daf::base::PropertySet * set
Definition: fits.cc:884
def getCallStack(skip=0)
Definition: callStack.py:169
def __get__(self, instance, owner=None, at=None, label="default")
Definition: config.py:476
def _setup(self, doc, dtype, default, check, optional, source)
Definition: config.py:273
def getStackFrame(relative=0)
Definition: callStack.py:52
table::Key< int > type
Definition: Detector.cc:167
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
def __delitem__(self, k, at=None, label="delitem")
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
Definition: comparison.py:56
def getComparisonName(name1, name2)
Definition: comparison.py:34
def __init__(self, config, field, value, at, label)