LSSTApplications  17.0+11,17.0+36,17.0+64,17.0+65,17.0+67,17.0+7,17.0-1-g377950a+36,17.0.1-1-g114240f+2,17.0.1-1-g4d4fbc4+31,17.0.1-1-g5f4ed7e+65,17.0.1-1-g6dd7d69+26,17.0.1-1-g8de6c91+11,17.0.1-1-gb9095d2+7,17.0.1-1-ge9fec5e+5,17.0.1-1-gf4e0155+69,17.0.1-1-gfc65f5f+61,17.0.1-1-gfc6fb1f+20,17.0.1-10-g87f9f3f+15,17.0.1-10-gd2c662c+4,17.0.1-12-g112a4bc+9,17.0.1-17-gdae4c4a+23,17.0.1-19-ga0476e3a+4,17.0.1-2-g26618f5+40,17.0.1-2-g54f2ebc+9,17.0.1-2-g57d9af3+3,17.0.1-2-gf403422+1,17.0.1-21-ga02730c,17.0.1-24-g44c3907+1,17.0.1-27-g9f381ddd+4,17.0.1-3-g7e86b59+50,17.0.1-3-gb5ca14a,17.0.1-32-gd980f9f48,17.0.1-4-g27bbc53+3,17.0.1-4-g59d126d+15,17.0.1-5-g3877d06+7,17.0.1-7-g35889ee+12,17.0.1-7-gc7c8782+22,17.0.1-9-gb4677f3+2,w.2019.26
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 __all__ = ("Config", "Field", "FieldValidationError")
23 
24 import io
25 import os
26 import re
27 import sys
28 import math
29 import copy
30 import tempfile
31 import shutil
32 
33 from .comparison import getComparisonName, compareScalars, compareConfigs
34 from .callStack import getStackFrame, getCallStack
35 
36 
37 def _joinNamePath(prefix=None, name=None, index=None):
38  """Generate nested configuration names.
39  """
40  if not prefix and not name:
41  raise ValueError("Invalid name: cannot be None")
42  elif not name:
43  name = prefix
44  elif prefix and name:
45  name = prefix + "." + name
46 
47  if index is not None:
48  return "%s[%r]" % (name, index)
49  else:
50  return name
51 
52 
53 def _autocast(x, dtype):
54  """Cast a value to a type, if appropriate.
55 
56  Parameters
57  ----------
58  x : object
59  A value.
60  dtype : tpye
61  Data type, such as `float`, `int`, or `str`.
62 
63  Returns
64  -------
65  values : object
66  If appropriate, the returned value is ``x`` cast to the given type
67  ``dtype``. If the cast cannot be performed the original value of
68  ``x`` is returned.
69  """
70  if dtype == float and isinstance(x, int):
71  return float(x)
72  return x
73 
74 
75 def _typeStr(x):
76  """Generate a fully-qualified type name.
77 
78  Returns
79  -------
80  `str`
81  Fully-qualified type name.
82 
83  Notes
84  -----
85  This function is used primarily for writing config files to be executed
86  later upon with the 'load' function.
87  """
88  if hasattr(x, '__module__') and hasattr(x, '__name__'):
89  xtype = x
90  else:
91  xtype = type(x)
92  if (sys.version_info.major <= 2 and xtype.__module__ == '__builtin__') or xtype.__module__ == 'builtins':
93  return xtype.__name__
94  else:
95  return "%s.%s" % (xtype.__module__, xtype.__name__)
96 
97 
99  """A metaclass for `lsst.pex.config.Config`.
100 
101  Notes
102  -----
103  ``ConfigMeta`` adds a dictionary containing all `~lsst.pex.config.Field`
104  class attributes as a class attribute called ``_fields``, and adds
105  the name of each field as an instance variable of the field itself (so you
106  don't have to pass the name of the field to the field constructor).
107  """
108 
109  def __init__(cls, name, bases, dict_):
110  type.__init__(cls, name, bases, dict_)
111  cls._fields = {}
112  cls._source = getStackFrame()
113 
114  def getFields(classtype):
115  fields = {}
116  bases = list(classtype.__bases__)
117  bases.reverse()
118  for b in bases:
119  fields.update(getFields(b))
120 
121  for k, v in classtype.__dict__.items():
122  if isinstance(v, Field):
123  fields[k] = v
124  return fields
125 
126  fields = getFields(cls)
127  for k, v in fields.items():
128  setattr(cls, k, copy.deepcopy(v))
129 
130  def __setattr__(cls, name, value):
131  if isinstance(value, Field):
132  value.name = name
133  cls._fields[name] = value
134  type.__setattr__(cls, name, value)
135 
136 
137 class FieldValidationError(ValueError):
138  """Raised when a ``~lsst.pex.config.Field`` is not valid in a
139  particular ``~lsst.pex.config.Config``.
140 
141  Parameters
142  ----------
143  field : `lsst.pex.config.Field`
144  The field that was not valid.
145  config : `lsst.pex.config.Config`
146  The config containing the invalid field.
147  msg : `str`
148  Text describing why the field was not valid.
149  """
150 
151  def __init__(self, field, config, msg):
152  self.fieldType = type(field)
153  """Type of the `~lsst.pex.config.Field` that incurred the error.
154  """
155 
156  self.fieldName = field.name
157  """Name of the `~lsst.pex.config.Field` instance that incurred the
158  error (`str`).
159 
160  See also
161  --------
162  lsst.pex.config.Field.name
163  """
164 
165  self.fullname = _joinNamePath(config._name, field.name)
166  """Fully-qualified name of the `~lsst.pex.config.Field` instance
167  (`str`).
168  """
169 
170  self.history = config.history.setdefault(field.name, [])
171  """Full history of all changes to the `~lsst.pex.config.Field`
172  instance.
173  """
174 
175  self.fieldSource = field.source
176  """File and line number of the `~lsst.pex.config.Field` definition.
177  """
178 
179  self.configSource = config._source
180  error = "%s '%s' failed validation: %s\n"\
181  "For more information see the Field definition at:\n%s"\
182  " and the Config definition at:\n%s" % \
183  (self.fieldType.__name__, self.fullname, msg,
184  self.fieldSource.format(), self.configSource.format())
185  super().__init__(error)
186 
187 
188 class Field:
189  """A field in a `~lsst.pex.config.Config` that supports `int`, `float`,
190  `complex`, `bool`, and `str` data types.
191 
192  Parameters
193  ----------
194  doc : `str`
195  A description of the field for users.
196  dtype : type
197  The field's data type. ``Field`` only supports basic data types:
198  `int`, `float`, `complex`, `bool`, and `str`. See
199  `Field.supportedTypes`.
200  default : object, optional
201  The field's default value.
202  check : callable, optional
203  A callable that is called with the field's value. This callable should
204  return `False` if the value is invalid. More complex inter-field
205  validation can be written as part of the
206  `lsst.pex.config.Config.validate` method.
207  optional : `bool`, optional
208  This sets whether the field is considered optional, and therefore
209  doesn't need to be set by the user. When `False`,
210  `lsst.pex.config.Config.validate` fails if the field's value is `None`.
211 
212  Raises
213  ------
214  ValueError
215  Raised when the ``dtype`` parameter is not one of the supported types
216  (see `Field.supportedTypes`).
217 
218  See also
219  --------
220  ChoiceField
221  ConfigChoiceField
222  ConfigDictField
223  ConfigField
224  ConfigurableField
225  DictField
226  ListField
227  RangeField
228  RegistryField
229 
230  Notes
231  -----
232  ``Field`` instances (including those of any subclass of ``Field``) are used
233  as class attributes of `~lsst.pex.config.Config` subclasses (see the
234  example, below). ``Field`` attributes work like the `property` attributes
235  of classes that implement custom setters and getters. `Field` attributes
236  belong to the class, but operate on the instance. Formally speaking,
237  `Field` attributes are `descriptors
238  <https://docs.python.org/3/howto/descriptor.html>`_.
239 
240  When you access a `Field` attribute on a `Config` instance, you don't
241  get the `Field` instance itself. Instead, you get the value of that field,
242  which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
243  container type (like a `lsst.pex.config.List`) depending on the field's
244  type. See the example, below.
245 
246  Examples
247  --------
248  Instances of ``Field`` should be used as class attributes of
249  `lsst.pex.config.Config` subclasses:
250 
251  >>> from lsst.pex.config import Config, Field
252  >>> class Example(Config):
253  ... myInt = Field("An integer field.", int, default=0)
254  ...
255  >>> print(config.myInt)
256  0
257  >>> config.myInt = 5
258  >>> print(config.myInt)
259  5
260  """
261 
262  supportedTypes = set((str, bool, float, int, complex))
263  """Supported data types for field values (`set` of types).
264  """
265 
266  def __init__(self, doc, dtype, default=None, check=None, optional=False):
267  if dtype not in self.supportedTypes:
268  raise ValueError("Unsupported Field dtype %s" % _typeStr(dtype))
269 
270  source = getStackFrame()
271  self._setup(doc=doc, dtype=dtype, default=default, check=check, optional=optional, source=source)
272 
273  def _setup(self, doc, dtype, default, check, optional, source):
274  """Set attributes, usually during initialization.
275  """
276  self.dtype = dtype
277  """Data type for the field.
278  """
279 
280  self.doc = doc
281  """A description of the field (`str`).
282  """
283 
284  self.__doc__ = f"{doc} (`{dtype.__name__}`"
285  if optional or default is not None:
286  self.__doc__ += f", default ``{default!r}``"
287  self.__doc__ += ")"
288 
289  self.default = default
290  """Default value for this field.
291  """
292 
293  self.check = check
294  """A user-defined function that validates the value of the field.
295  """
296 
297  self.optional = optional
298  """Flag that determines if the field is required to be set (`bool`).
299 
300  When `False`, `lsst.pex.config.Config.validate` will fail if the
301  field's value is `None`.
302  """
303 
304  self.source = source
305  """The stack frame where this field is defined (`list` of
306  `lsst.pex.config.callStack.StackFrame`).
307  """
308 
309  def rename(self, instance):
310  """Rename the field in a `~lsst.pex.config.Config` (for internal use
311  only).
312 
313  Parameters
314  ----------
315  instance : `lsst.pex.config.Config`
316  The config instance that contains this field.
317 
318  Notes
319  -----
320  This method is invoked by the `lsst.pex.config.Config` object that
321  contains this field and should not be called directly.
322 
323  Renaming is only relevant for `~lsst.pex.config.Field` instances that
324  hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should
325  rename each subconfig with the full field name as generated by
326  `lsst.pex.config.config._joinNamePath`.
327  """
328  pass
329 
330  def validate(self, instance):
331  """Validate the field (for internal use only).
332 
333  Parameters
334  ----------
335  instance : `lsst.pex.config.Config`
336  The config instance that contains this field.
337 
338  Raises
339  ------
340  lsst.pex.config.FieldValidationError
341  Raised if verification fails.
342 
343  Notes
344  -----
345  This method provides basic validation:
346 
347  - Ensures that the value is not `None` if the field is not optional.
348  - Ensures type correctness.
349  - Ensures that the user-provided ``check`` function is valid.
350 
351  Most `~lsst.pex.config.Field` subclasses should call
352  `lsst.pex.config.field.Field.validate` if they re-implement
353  `~lsst.pex.config.field.Field.validate`.
354  """
355  value = self.__get__(instance)
356  if not self.optional and value is None:
357  raise FieldValidationError(self, instance, "Required value cannot be None")
358 
359  def freeze(self, instance):
360  """Make this field read-only (for internal use only).
361 
362  Parameters
363  ----------
364  instance : `lsst.pex.config.Config`
365  The config instance that contains this field.
366 
367  Notes
368  -----
369  Freezing is only relevant for fields that hold subconfigs. Fields which
370  hold subconfigs should freeze each subconfig.
371 
372  **Subclasses should implement this method.**
373  """
374  pass
375 
376  def _validateValue(self, value):
377  """Validate a value.
378 
379  Parameters
380  ----------
381  value : object
382  The value being validated.
383 
384  Raises
385  ------
386  TypeError
387  Raised if the value's type is incompatible with the field's
388  ``dtype``.
389  ValueError
390  Raised if the value is rejected by the ``check`` method.
391  """
392  if value is None:
393  return
394 
395  if not isinstance(value, self.dtype):
396  msg = "Value %s is of incorrect type %s. Expected type %s" % \
397  (value, _typeStr(value), _typeStr(self.dtype))
398  raise TypeError(msg)
399  if self.check is not None and not self.check(value):
400  msg = "Value %s is not a valid value" % str(value)
401  raise ValueError(msg)
402 
403  def _collectImports(self, instance, imports):
404  """This function should call the _collectImports method on all config
405  objects the field may own, and union them with the supplied imports
406  set.
407 
408  Parameters
409  ----------
410  instance : instance or subclass of `lsst.pex.config.Config`
411  A config object that has this field defined on it
412  imports : `set`
413  Set of python modules that need imported after persistence
414  """
415  pass
416 
417  def save(self, outfile, instance):
418  """Save this field to a file (for internal use only).
419 
420  Parameters
421  ----------
422  outfile : file-like object
423  A writeable field handle.
424  instance : `Config`
425  The `Config` instance that contains this field.
426 
427  Notes
428  -----
429  This method is invoked by the `~lsst.pex.config.Config` object that
430  contains this field and should not be called directly.
431 
432  The output consists of the documentation string
433  (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second
434  line is formatted as an assignment: ``{fullname}={value}``.
435 
436  This output can be executed with Python.
437  """
438  value = self.__get__(instance)
439  fullname = _joinNamePath(instance._name, self.name)
440 
441  # write full documentation string as comment lines (i.e. first character is #)
442  doc = "# " + str(self.doc).replace("\n", "\n# ")
443  if isinstance(value, float) and (math.isinf(value) or math.isnan(value)):
444  # non-finite numbers need special care
445  outfile.write(u"{}\n{}=float('{!r}')\n\n".format(doc, fullname, value))
446  else:
447  outfile.write(u"{}\n{}={!r}\n\n".format(doc, fullname, value))
448 
449  def toDict(self, instance):
450  """Convert the field value so that it can be set as the value of an
451  item in a `dict` (for internal use only).
452 
453  Parameters
454  ----------
455  instance : `Config`
456  The `Config` that contains this field.
457 
458  Returns
459  -------
460  value : object
461  The field's value. See *Notes*.
462 
463  Notes
464  -----
465  This method invoked by the owning `~lsst.pex.config.Config` object and
466  should not be called directly.
467 
468  Simple values are passed through. Complex data structures must be
469  manipulated. For example, a `~lsst.pex.config.Field` holding a
470  subconfig should, instead of the subconfig object, return a `dict`
471  where the keys are the field names in the subconfig, and the values are
472  the field values in the subconfig.
473  """
474  return self.__get__(instance)
475 
476  def __get__(self, instance, owner=None, at=None, label="default"):
477  """Define how attribute access should occur on the Config instance
478  This is invoked by the owning config object and should not be called
479  directly
480 
481  When the field attribute is accessed on a Config class object, it
482  returns the field object itself in order to allow inspection of
483  Config classes.
484 
485  When the field attribute is access on a config instance, the actual
486  value described by the field (and held by the Config instance) is
487  returned.
488  """
489  if instance is None or not isinstance(instance, Config):
490  return self
491  else:
492  return instance._storage[self.name]
493 
494  def __set__(self, instance, value, at=None, label='assignment'):
495  """Set an attribute on the config instance.
496 
497  Parameters
498  ----------
499  instance : `lsst.pex.config.Config`
500  The config instance that contains this field.
501  value : obj
502  Value to set on this field.
503  at : `list` of `lsst.pex.config.callStack.StackFrame`
504  The call stack (created by
505  `lsst.pex.config.callStack.getCallStack`).
506  label : `str`, optional
507  Event label for the history.
508 
509  Notes
510  -----
511  This method is invoked by the owning `lsst.pex.config.Config` object
512  and should not be called directly.
513 
514  Derived `~lsst.pex.config.Field` classes may need to override the
515  behavior. When overriding ``__set__``, `~lsst.pex.config.Field` authors
516  should follow the following rules:
517 
518  - Do not allow modification of frozen configs.
519  - Validate the new value **before** modifying the field. Except if the
520  new value is `None`. `None` is special and no attempt should be made
521  to validate it until `lsst.pex.config.Config.validate` is called.
522  - Do not modify the `~lsst.pex.config.Config` instance to contain
523  invalid values.
524  - If the field is modified, update the history of the
525  `lsst.pex.config.field.Field` to reflect the changes.
526 
527  In order to decrease the need to implement this method in derived
528  `~lsst.pex.config.Field` types, value validation is performed in the
529  `lsst.pex.config.Field._validateValue`. If only the validation step
530  differs in the derived `~lsst.pex.config.Field`, it is simpler to
531  implement `lsst.pex.config.Field._validateValue` than to reimplement
532  ``__set__``. More complicated behavior, however, may require
533  reimplementation.
534  """
535  if instance._frozen:
536  raise FieldValidationError(self, instance, "Cannot modify a frozen Config")
537 
538  history = instance._history.setdefault(self.name, [])
539  if value is not None:
540  value = _autocast(value, self.dtype)
541  try:
542  self._validateValue(value)
543  except BaseException as e:
544  raise FieldValidationError(self, instance, str(e))
545 
546  instance._storage[self.name] = value
547  if at is None:
548  at = getCallStack()
549  history.append((value, at, label))
550 
551  def __delete__(self, instance, at=None, label='deletion'):
552  """Delete an attribute from a `lsst.pex.config.Config` instance.
553 
554  Parameters
555  ----------
556  instance : `lsst.pex.config.Config`
557  The config instance that contains this field.
558  at : `list` of `lsst.pex.config.callStack.StackFrame`
559  The call stack (created by
560  `lsst.pex.config.callStack.getCallStack`).
561  label : `str`, optional
562  Event label for the history.
563 
564  Notes
565  -----
566  This is invoked by the owning `~lsst.pex.config.Config` object and
567  should not be called directly.
568  """
569  if at is None:
570  at = getCallStack()
571  self.__set__(instance, None, at=at, label=label)
572 
573  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
574  """Compare a field (named `Field.name`) in two
575  `~lsst.pex.config.Config` instances for equality.
576 
577  Parameters
578  ----------
579  instance1 : `lsst.pex.config.Config`
580  Left-hand side `Config` instance to compare.
581  instance2 : `lsst.pex.config.Config`
582  Right-hand side `Config` instance to compare.
583  shortcut : `bool`, optional
584  **Unused.**
585  rtol : `float`, optional
586  Relative tolerance for floating point comparisons.
587  atol : `float`, optional
588  Absolute tolerance for floating point comparisons.
589  output : callable, optional
590  A callable that takes a string, used (possibly repeatedly) to
591  report inequalities.
592 
593  Notes
594  -----
595  This method must be overridden by more complex `Field` subclasses.
596 
597  See also
598  --------
599  lsst.pex.config.compareScalars
600  """
601  v1 = getattr(instance1, self.name)
602  v2 = getattr(instance2, self.name)
603  name = getComparisonName(
604  _joinNamePath(instance1._name, self.name),
605  _joinNamePath(instance2._name, self.name)
606  )
607  return compareScalars(name, v1, v2, dtype=self.dtype, rtol=rtol, atol=atol, output=output)
608 
609 
611  """Importer (for `sys.meta_path`) that records which modules are being
612  imported.
613 
614  *This class does not do any importing itself.*
615 
616  Examples
617  --------
618  Use this class as a context manager to ensure it is properly uninstalled
619  when done:
620 
621  >>> with RecordingImporter() as importer:
622  ... # import stuff
623  ... import numpy as np
624  ... print("Imported: " + importer.getModules())
625  """
626 
627  def __init__(self):
628  self._modules = set()
629 
630  def __enter__(self):
631  self.origMetaPath = sys.meta_path
632  sys.meta_path = [self] + sys.meta_path
633  return self
634 
635  def __exit__(self, *args):
636  self.uninstall()
637  return False # Don't suppress exceptions
638 
639  def uninstall(self):
640  """Uninstall the importer.
641  """
642  sys.meta_path = self.origMetaPath
643 
644  def find_module(self, fullname, path=None):
645  """Called as part of the ``import`` chain of events.
646  """
647  self._modules.add(fullname)
648  # Return None because we don't do any importing.
649  return None
650 
651  def getModules(self):
652  """Get the set of modules that were imported.
653 
654  Returns
655  -------
656  modules : `set` of `str`
657  Set of imported module names.
658  """
659  return self._modules
660 
661 
662 class Config(metaclass=ConfigMeta):
663  """Base class for configuration (*config*) objects.
664 
665  Notes
666  -----
667  A ``Config`` object will usually have several `~lsst.pex.config.Field`
668  instances as class attributes. These are used to define most of the base
669  class behavior.
670 
671  ``Config`` implements a mapping API that provides many `dict`-like methods,
672  such as `keys`, `values`, `items`, `iteritems`, `iterkeys`, and
673  `itervalues`. ``Config`` instances also support the ``in`` operator to
674  test if a field is in the config. Unlike a `dict`, ``Config`` classes are
675  not subscriptable. Instead, access individual fields as attributes of the
676  configuration instance.
677 
678  Examples
679  --------
680  Config classes are subclasses of ``Config`` that have
681  `~lsst.pex.config.Field` instances (or instances of
682  `~lsst.pex.config.Field` subclasses) as class attributes:
683 
684  >>> from lsst.pex.config import Config, Field, ListField
685  >>> class DemoConfig(Config):
686  ... intField = Field(doc="An integer field", dtype=int, default=42)
687  ... listField = ListField(doc="List of favorite beverages.", dtype=str,
688  ... default=['coffee', 'green tea', 'water'])
689  ...
690  >>> config = DemoConfig()
691 
692  Configs support many `dict`-like APIs:
693 
694  >>> config.keys()
695  ['intField', 'listField']
696  >>> 'intField' in config
697  True
698 
699  Individual fields can be accessed as attributes of the configuration:
700 
701  >>> config.intField
702  42
703  >>> config.listField.append('earl grey tea')
704  >>> print(config.listField)
705  ['coffee', 'green tea', 'water', 'earl grey tea']
706  """
707 
708  def __iter__(self):
709  """Iterate over fields.
710  """
711  return self._fields.__iter__()
712 
713  def keys(self):
714  """Get field names.
715 
716  Returns
717  -------
718  names : `list`
719  List of `lsst.pex.config.Field` names.
720 
721  See also
722  --------
723  lsst.pex.config.Config.iterkeys
724  """
725  return list(self._storage.keys())
726 
727  def values(self):
728  """Get field values.
729 
730  Returns
731  -------
732  values : `list`
733  List of field values.
734 
735  See also
736  --------
737  lsst.pex.config.Config.itervalues
738  """
739  return list(self._storage.values())
740 
741  def items(self):
742  """Get configurations as ``(field name, field value)`` pairs.
743 
744  Returns
745  -------
746  items : `list`
747  List of tuples for each configuration. Tuple items are:
748 
749  0. Field name.
750  1. Field value.
751 
752  See also
753  --------
754  lsst.pex.config.Config.iteritems
755  """
756  return list(self._storage.items())
757 
758  def iteritems(self):
759  """Iterate over (field name, field value) pairs.
760 
761  Yields
762  ------
763  item : `tuple`
764  Tuple items are:
765 
766  0. Field name.
767  1. Field value.
768 
769  See also
770  --------
771  lsst.pex.config.Config.items
772  """
773  return iter(self._storage.items())
774 
775  def itervalues(self):
776  """Iterate over field values.
777 
778  Yields
779  ------
780  value : obj
781  A field value.
782 
783  See also
784  --------
785  lsst.pex.config.Config.values
786  """
787  return iter(self.storage.values())
788 
789  def iterkeys(self):
790  """Iterate over field names
791 
792  Yields
793  ------
794  key : `str`
795  A field's key (attribute name).
796 
797  See also
798  --------
799  lsst.pex.config.Config.values
800  """
801  return iter(self.storage.keys())
802 
803  def __contains__(self, name):
804  """!Return True if the specified field exists in this config
805 
806  @param[in] name field name to test for
807  """
808  return self._storage.__contains__(name)
809 
810  def __new__(cls, *args, **kw):
811  """Allocate a new `lsst.pex.config.Config` object.
812 
813  In order to ensure that all Config object are always in a proper state
814  when handed to users or to derived `~lsst.pex.config.Config` classes,
815  some attributes are handled at allocation time rather than at
816  initialization.
817 
818  This ensures that even if a derived `~lsst.pex.config.Config` class
819  implements ``__init__``, its author does not need to be concerned about
820  when or even the base ``Config.__init__`` should be called.
821  """
822  name = kw.pop("__name", None)
823  at = kw.pop("__at", getCallStack())
824  # remove __label and ignore it
825  kw.pop("__label", "default")
826 
827  instance = object.__new__(cls)
828  instance._frozen = False
829  instance._name = name
830  instance._storage = {}
831  instance._history = {}
832  instance._imports = set()
833  # load up defaults
834  for field in instance._fields.values():
835  instance._history[field.name] = []
836  field.__set__(instance, field.default, at=at + [field.source], label="default")
837  # set custom default-overides
838  instance.setDefaults()
839  # set constructor overides
840  instance.update(__at=at, **kw)
841  return instance
842 
843  def __reduce__(self):
844  """Reduction for pickling (function with arguments to reproduce).
845 
846  We need to condense and reconstitute the `~lsst.pex.config.Config`,
847  since it may contain lambdas (as the ``check`` elements) that cannot
848  be pickled.
849  """
850  # The stream must be in characters to match the API but pickle requires bytes
851  stream = io.StringIO()
852  self.saveToStream(stream)
853  return (unreduceConfig, (self.__class__, stream.getvalue().encode()))
854 
855  def setDefaults(self):
856  """Subclass hook for computing defaults.
857 
858  Notes
859  -----
860  Derived `~lsst.pex.config.Config` classes that must compute defaults
861  rather than using the `~lsst.pex.config.Field` instances's defaults
862  should do so here. To correctly use inherited defaults,
863  implementations of ``setDefaults`` must call their base class's
864  ``setDefaults``.
865  """
866  pass
867 
868  def update(self, **kw):
869  """Update values of fields specified by the keyword arguments.
870 
871  Parameters
872  ----------
873  kw
874  Keywords are configuration field names. Values are configuration
875  field values.
876 
877  Notes
878  -----
879  The ``__at`` and ``__label`` keyword arguments are special internal
880  keywords. They are used to strip out any internal steps from the
881  history tracebacks of the config. Do not modify these keywords to
882  subvert a `~lsst.pex.config.Config` instance's history.
883 
884  Examples
885  --------
886  This is a config with three fields:
887 
888  >>> from lsst.pex.config import Config, Field
889  >>> class DemoConfig(Config):
890  ... fieldA = Field(doc='Field A', dtype=int, default=42)
891  ... fieldB = Field(doc='Field B', dtype=bool, default=True)
892  ... fieldC = Field(doc='Field C', dtype=str, default='Hello world')
893  ...
894  >>> config = DemoConfig()
895 
896  These are the default values of each field:
897 
898  >>> for name, value in config.iteritems():
899  ... print(f"{name}: {value}")
900  ...
901  fieldA: 42
902  fieldB: True
903  fieldC: 'Hello world'
904 
905  Using this method to update ``fieldA`` and ``fieldC``:
906 
907  >>> config.update(fieldA=13, fieldC='Updated!')
908 
909  Now the values of each field are:
910 
911  >>> for name, value in config.iteritems():
912  ... print(f"{name}: {value}")
913  ...
914  fieldA: 13
915  fieldB: True
916  fieldC: 'Updated!'
917  """
918  at = kw.pop("__at", getCallStack())
919  label = kw.pop("__label", "update")
920 
921  for name, value in kw.items():
922  try:
923  field = self._fields[name]
924  field.__set__(self, value, at=at, label=label)
925  except KeyError:
926  raise KeyError("No field of name %s exists in config type %s" % (name, _typeStr(self)))
927 
928  def load(self, filename, root="config"):
929  """Modify this config in place by executing the Python code in a
930  configuration file.
931 
932  Parameters
933  ----------
934  filename : `str`
935  Name of the configuration file. A configuration file is Python
936  module.
937  root : `str`, optional
938  Name of the variable in file that refers to the config being
939  overridden.
940 
941  For example, the value of root is ``"config"`` and the file
942  contains::
943 
944  config.myField = 5
945 
946  Then this config's field ``myField`` is set to ``5``.
947 
948  **Deprecated:** For backwards compatibility, older config files
949  that use ``root="root"`` instead of ``root="config"`` will be
950  loaded with a warning printed to `sys.stderr`. This feature will be
951  removed at some point.
952 
953  See also
954  --------
955  lsst.pex.config.Config.loadFromStream
956  lsst.pex.config.Config.save
957  lsst.pex.config.Config.saveFromStream
958  """
959  with open(filename, "r") as f:
960  code = compile(f.read(), filename=filename, mode="exec")
961  self.loadFromStream(stream=code, root=root)
962 
963  def loadFromStream(self, stream, root="config", filename=None):
964  """Modify this Config in place by executing the Python code in the
965  provided stream.
966 
967  Parameters
968  ----------
969  stream : file-like object, `str`, or compiled string
970  Stream containing configuration override code.
971  root : `str`, optional
972  Name of the variable in file that refers to the config being
973  overridden.
974 
975  For example, the value of root is ``"config"`` and the file
976  contains::
977 
978  config.myField = 5
979 
980  Then this config's field ``myField`` is set to ``5``.
981 
982  **Deprecated:** For backwards compatibility, older config files
983  that use ``root="root"`` instead of ``root="config"`` will be
984  loaded with a warning printed to `sys.stderr`. This feature will be
985  removed at some point.
986  filename : `str`, optional
987  Name of the configuration file, or `None` if unknown or contained
988  in the stream. Used for error reporting.
989 
990  See also
991  --------
992  lsst.pex.config.Config.load
993  lsst.pex.config.Config.save
994  lsst.pex.config.Config.saveFromStream
995  """
996  with RecordingImporter() as importer:
997  try:
998  local = {root: self}
999  exec(stream, {}, local)
1000  except NameError as e:
1001  if root == "config" and "root" in e.args[0]:
1002  if filename is None:
1003  # try to determine the file name; a compiled string has attribute "co_filename",
1004  # an open file has attribute "name", else give up
1005  filename = getattr(stream, "co_filename", None)
1006  if filename is None:
1007  filename = getattr(stream, "name", "?")
1008  print(f"Config override file {filename!r}"
1009  " appears to use 'root' instead of 'config'; trying with 'root'", file=sys.stderr)
1010  local = {"root": self}
1011  exec(stream, {}, local)
1012  else:
1013  raise
1014 
1015  self._imports.update(importer.getModules())
1016 
1017  def save(self, filename, root="config"):
1018  """Save a Python script to the named file, which, when loaded,
1019  reproduces this config.
1020 
1021  Parameters
1022  ----------
1023  filename : `str`
1024  Desination filename of this configuration.
1025  root : `str`, optional
1026  Name to use for the root config variable. The same value must be
1027  used when loading (see `lsst.pex.config.Config.load`).
1028 
1029  See also
1030  --------
1031  lsst.pex.config.Config.saveToStream
1032  lsst.pex.config.Config.load
1033  lsst.pex.config.Config.loadFromStream
1034  """
1035  d = os.path.dirname(filename)
1036  with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=d) as outfile:
1037  self.saveToStream(outfile, root)
1038  # tempfile is hardcoded to create files with mode '0600'
1039  # for an explantion of these antics see:
1040  # https://stackoverflow.com/questions/10291131/how-to-use-os-umask-in-python
1041  umask = os.umask(0o077)
1042  os.umask(umask)
1043  os.chmod(outfile.name, (~umask & 0o666))
1044  # chmod before the move so we get quasi-atomic behavior if the
1045  # source and dest. are on the same filesystem.
1046  # os.rename may not work across filesystems
1047  shutil.move(outfile.name, filename)
1048 
1049  def saveToStream(self, outfile, root="config"):
1050  """Save a configuration file to a stream, which, when loaded,
1051  reproduces this config.
1052 
1053  Parameters
1054  ----------
1055  outfile : file-like object
1056  Destination file object write the config into. Accepts strings not
1057  bytes.
1058  root
1059  Name to use for the root config variable. The same value must be
1060  used when loading (see `lsst.pex.config.Config.load`).
1061 
1062  See also
1063  --------
1064  lsst.pex.config.Config.save
1065  lsst.pex.config.Config.load
1066  lsst.pex.config.Config.loadFromStream
1067  """
1068  tmp = self._name
1069  self._rename(root)
1070  try:
1071  self._collectImports()
1072  # Remove self from the set, as it is handled explicitly below
1073  self._imports.remove(self.__module__)
1074  configType = type(self)
1075  typeString = _typeStr(configType)
1076  outfile.write(u"import {}\n".format(configType.__module__))
1077  outfile.write(u"assert type({})=={}, 'config is of type %s.%s ".format(root, typeString))
1078  outfile.write(u"instead of {}' % (type({}).__module__, type({}).__name__)\n".format(typeString,
1079  root,
1080  root))
1081  for imp in self._imports:
1082  if imp in sys.modules and sys.modules[imp] is not None:
1083  outfile.write(u"import {}\n".format(imp))
1084  self._save(outfile)
1085  finally:
1086  self._rename(tmp)
1087 
1088  def freeze(self):
1089  """Make this config, and all subconfigs, read-only.
1090  """
1091  self._frozen = True
1092  for field in self._fields.values():
1093  field.freeze(self)
1094 
1095  def _save(self, outfile):
1096  """Save this config to an open stream object.
1097 
1098  Parameters
1099  ----------
1100  outfile : file-like object
1101  Destination file object write the config into. Accepts strings not
1102  bytes.
1103  """
1104  for field in self._fields.values():
1105  field.save(outfile, self)
1106 
1107  def _collectImports(self):
1108  """Adds module containing self to the list of things to import and
1109  then loops over all the fields in the config calling a corresponding
1110  collect method. The field method will call _collectImports on any configs
1111  it may own and return the set of things to import. This returned set
1112  will be merged with the set of imports for this config class.
1113  """
1114  self._imports.add(self.__module__)
1115  for name, field in self._fields.items():
1116  field._collectImports(self, self._imports)
1117 
1118  def toDict(self):
1119  """Make a dictionary of field names and their values.
1120 
1121  Returns
1122  -------
1123  dict_ : `dict`
1124  Dictionary with keys that are `~lsst.pex.config.Field` names.
1125  Values are `~lsst.pex.config.Field` values.
1126 
1127  See also
1128  --------
1129  lsst.pex.config.Field.toDict
1130 
1131  Notes
1132  -----
1133  This method uses the `~lsst.pex.config.Field.toDict` method of
1134  individual fields. Subclasses of `~lsst.pex.config.Field` may need to
1135  implement a ``toDict`` method for *this* method to work.
1136  """
1137  dict_ = {}
1138  for name, field in self._fields.items():
1139  dict_[name] = field.toDict(self)
1140  return dict_
1141 
1142  def names(self):
1143  """Get all the field names in the config, recursively.
1144 
1145  Returns
1146  -------
1147  names : `list` of `str`
1148  Field names.
1149  """
1150  #
1151  # Rather than sort out the recursion all over again use the
1152  # pre-existing saveToStream()
1153  #
1154  with io.StringIO() as strFd:
1155  self.saveToStream(strFd, "config")
1156  contents = strFd.getvalue()
1157  strFd.close()
1158  #
1159  # Pull the names out of the dumped config
1160  #
1161  keys = []
1162  for line in contents.split("\n"):
1163  if re.search(r"^((assert|import)\s+|\s*$|#)", line):
1164  continue
1165 
1166  mat = re.search(r"^(?:config\.)?([^=]+)\s*=\s*.*", line)
1167  if mat:
1168  keys.append(mat.group(1))
1169 
1170  return keys
1171 
1172  def _rename(self, name):
1173  """Rename this config object in its parent `~lsst.pex.config.Config`.
1174 
1175  Parameters
1176  ----------
1177  name : `str`
1178  New name for this config in its parent `~lsst.pex.config.Config`.
1179 
1180  Notes
1181  -----
1182  This method uses the `~lsst.pex.config.Field.rename` method of
1183  individual `lsst.pex.config.Field` instances.
1184  `lsst.pex.config.Field` subclasses may need to implement a ``rename``
1185  method for *this* method to work.
1186 
1187  See also
1188  --------
1189  lsst.pex.config.Field.rename
1190  """
1191  self._name = name
1192  for field in self._fields.values():
1193  field.rename(self)
1194 
1195  def validate(self):
1196  """Validate the Config, raising an exception if invalid.
1197 
1198  Raises
1199  ------
1200  lsst.pex.config.FieldValidationError
1201  Raised if verification fails.
1202 
1203  Notes
1204  -----
1205  The base class implementation performs type checks on all fields by
1206  calling their `~lsst.pex.config.Field.validate` methods.
1207 
1208  Complex single-field validation can be defined by deriving new Field
1209  types. For convenience, some derived `lsst.pex.config.Field`-types
1210  (`~lsst.pex.config.ConfigField` and
1211  `~lsst.pex.config.ConfigChoiceField`) are defined in `lsst.pex.config`
1212  that handle recursing into subconfigs.
1213 
1214  Inter-field relationships should only be checked in derived
1215  `~lsst.pex.config.Config` classes after calling this method, and base
1216  validation is complete.
1217  """
1218  for field in self._fields.values():
1219  field.validate(self)
1220 
1221  def formatHistory(self, name, **kwargs):
1222  """Format a configuration field's history to a human-readable format.
1223 
1224  Parameters
1225  ----------
1226  name : `str`
1227  Name of a `~lsst.pex.config.Field` in this config.
1228  kwargs
1229  Keyword arguments passed to `lsst.pex.config.history.format`.
1230 
1231  Returns
1232  -------
1233  history : `str`
1234  A string containing the formatted history.
1235 
1236  See also
1237  --------
1238  lsst.pex.config.history.format
1239  """
1240  import lsst.pex.config.history as pexHist
1241  return pexHist.format(self, name, **kwargs)
1242 
1243  history = property(lambda x: x._history)
1244  """Read-only history.
1245  """
1246 
1247  def __setattr__(self, attr, value, at=None, label="assignment"):
1248  """Set an attribute (such as a field's value).
1249 
1250  Notes
1251  -----
1252  Unlike normal Python objects, `~lsst.pex.config.Config` objects are
1253  locked such that no additional attributes nor properties may be added
1254  to them dynamically.
1255 
1256  Although this is not the standard Python behavior, it helps to protect
1257  users from accidentally mispelling a field name, or trying to set a
1258  non-existent field.
1259  """
1260  if attr in self._fields:
1261  if at is None:
1262  at = getCallStack()
1263  # This allows Field descriptors to work.
1264  self._fields[attr].__set__(self, value, at=at, label=label)
1265  elif hasattr(getattr(self.__class__, attr, None), '__set__'):
1266  # This allows properties and other non-Field descriptors to work.
1267  return object.__setattr__(self, attr, value)
1268  elif attr in self.__dict__ or attr in ("_name", "_history", "_storage", "_frozen", "_imports"):
1269  # This allows specific private attributes to work.
1270  self.__dict__[attr] = value
1271  else:
1272  # We throw everything else.
1273  raise AttributeError("%s has no attribute %s" % (_typeStr(self), attr))
1274 
1275  def __delattr__(self, attr, at=None, label="deletion"):
1276  if attr in self._fields:
1277  if at is None:
1278  at = getCallStack()
1279  self._fields[attr].__delete__(self, at=at, label=label)
1280  else:
1281  object.__delattr__(self, attr)
1282 
1283  def __eq__(self, other):
1284  if type(other) == type(self):
1285  for name in self._fields:
1286  thisValue = getattr(self, name)
1287  otherValue = getattr(other, name)
1288  if isinstance(thisValue, float) and math.isnan(thisValue):
1289  if not math.isnan(otherValue):
1290  return False
1291  elif thisValue != otherValue:
1292  return False
1293  return True
1294  return False
1295 
1296  def __ne__(self, other):
1297  return not self.__eq__(other)
1298 
1299  def __str__(self):
1300  return str(self.toDict())
1301 
1302  def __repr__(self):
1303  return "%s(%s)" % (
1304  _typeStr(self),
1305  ", ".join("%s=%r" % (k, v) for k, v in self.toDict().items() if v is not None)
1306  )
1307 
1308  def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None):
1309  """Compare this configuration to another `~lsst.pex.config.Config` for
1310  equality.
1311 
1312  Parameters
1313  ----------
1314  other : `lsst.pex.config.Config`
1315  Other `~lsst.pex.config.Config` object to compare against this
1316  config.
1317  shortcut : `bool`, optional
1318  If `True`, return as soon as an inequality is found. Default is
1319  `True`.
1320  rtol : `float`, optional
1321  Relative tolerance for floating point comparisons.
1322  atol : `float`, optional
1323  Absolute tolerance for floating point comparisons.
1324  output : callable, optional
1325  A callable that takes a string, used (possibly repeatedly) to
1326  report inequalities.
1327 
1328  Returns
1329  -------
1330  isEqual : `bool`
1331  `True` when the two `lsst.pex.config.Config` instances are equal.
1332  `False` if there is an inequality.
1333 
1334  See also
1335  --------
1336  lsst.pex.config.compareConfigs
1337 
1338  Notes
1339  -----
1340  Unselected targets of `~lsst.pex.config.RegistryField` fields and
1341  unselected choices of `~lsst.pex.config.ConfigChoiceField` fields
1342  are not considered by this method.
1343 
1344  Floating point comparisons are performed by `numpy.allclose`.
1345  """
1346  name1 = self._name if self._name is not None else "config"
1347  name2 = other._name if other._name is not None else "config"
1348  name = getComparisonName(name1, name2)
1349  return compareConfigs(name, self, other, shortcut=shortcut,
1350  rtol=rtol, atol=atol, output=output)
1351 
1352 
1353 def unreduceConfig(cls, stream):
1354  """Create a `~lsst.pex.config.Config` from a stream.
1355 
1356  Parameters
1357  ----------
1358  cls : `lsst.pex.config.Config`-type
1359  A `lsst.pex.config.Config` type (not an instance) that is instantiated
1360  with configurations in the ``stream``.
1361  stream : file-like object, `str`, or compiled string
1362  Stream containing configuration override code.
1363 
1364  Returns
1365  -------
1366  config : `lsst.pex.config.Config`
1367  Config instance.
1368 
1369  See also
1370  --------
1371  lsst.pex.config.Config.loadFromStream
1372  """
1373  config = cls()
1374  config.loadFromStream(stream)
1375  return config
def toDict(self, instance)
Definition: config.py:449
def __eq__(self, other)
Definition: config.py:1283
def formatHistory(self, name, kwargs)
Definition: config.py:1221
def load(self, filename, root="config")
Definition: config.py:928
def _rename(self, name)
Definition: config.py:1172
def __init__(self, doc, dtype, default=None, check=None, optional=False)
Definition: config.py:266
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
Definition: comparison.py:105
def unreduceConfig(cls, stream)
Definition: config.py:1353
def _save(self, outfile)
Definition: config.py:1095
def __init__(self, field, config, msg)
Definition: config.py:151
def saveToStream(self, outfile, root="config")
Definition: config.py:1049
def __delattr__(self, attr, at=None, label="deletion")
Definition: config.py:1275
daf::base::PropertySet * set
Definition: fits.cc:884
def getCallStack(skip=0)
Definition: callStack.py:169
def __setattr__(cls, name, value)
Definition: config.py:130
def __get__(self, instance, owner=None, at=None, label="default")
Definition: config.py:476
def save(self, filename, root="config")
Definition: config.py:1017
def __ne__(self, other)
Definition: config.py:1296
def _setup(self, doc, dtype, default, check, optional, source)
Definition: config.py:273
def getStackFrame(relative=0)
Definition: callStack.py:52
def __contains__(self, name)
Return True if the specified field exists in this config.
Definition: config.py:803
table::Key< int > type
Definition: Detector.cc:167
def save(self, outfile, instance)
Definition: config.py:417
def _validateValue(self, value)
Definition: config.py:376
def validate(self, instance)
Definition: config.py:330
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
def __new__(cls, args, kw)
Definition: config.py:810
def __init__(cls, name, bases, dict_)
Definition: config.py:109
def __setattr__(self, attr, value, at=None, label="assignment")
Definition: config.py:1247
def freeze(self, instance)
Definition: config.py:359
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
Definition: comparison.py:56
def __set__(self, instance, value, at=None, label='assignment')
Definition: config.py:494
def loadFromStream(self, stream, root="config", filename=None)
Definition: config.py:963
def compare(self, other, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
Definition: config.py:1308
daf::base::PropertyList * list
Definition: fits.cc:885
def getComparisonName(name1, name2)
Definition: comparison.py:34
def rename(self, instance)
Definition: config.py:309
def __delete__(self, instance, at=None, label='deletion')
Definition: config.py:551
def find_module(self, fullname, path=None)
Definition: config.py:644