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