27from __future__
import annotations
34 "FieldValidationError",
35 "UnexpectedProxyUsageError",
50from types
import GenericAlias
51from typing
import Any, ForwardRef, Generic, TypeVar, cast, overload
60from .callStack
import getCallStack, getStackFrame
61from .comparison
import compareConfigs, compareScalars, getComparisonName
64 YamlLoaders: tuple[Any, ...] = (yaml.Loader, yaml.FullLoader, yaml.SafeLoader, yaml.UnsafeLoader)
68 from yaml
import CLoader
70 YamlLoaders += (CLoader,)
79 """A Subclass of python's GenericAlias used in defining and instantiating
82 This class differs from `types.GenericAlias` in that it calls a method
83 named _parseTypingArgs defined on Fields. This method gives Field and its
84 subclasses an opportunity to transform type parameters into class key word
85 arguments. Code authors do not need to implement any returns of this object
86 directly, and instead only need implement _parseTypingArgs, if a Field
87 subclass differs from the base class implementation.
89 This class is intended to be an implementation detail, returned from a
90 Field's `__class_getitem__` method.
93 def __call__(self, *args: Any, **kwds: Any) -> Any:
94 origin_kwargs = self._parseTypingArgs(self.__args__, kwds)
95 return super().
__call__(*args, **{**kwds, **origin_kwargs})
98FieldTypeVar = TypeVar(
"FieldTypeVar")
102 """Exception raised when a proxy class is used in a context that suggests
103 it should have already been converted to the thing it proxies.
108 """Generate nested configuration names."""
109 if not prefix
and not name:
110 raise ValueError(
"Invalid name: cannot be None")
113 elif prefix
and name:
114 name = prefix +
"." + name
116 if index
is not None:
117 return f
"{name}[{index!r}]"
123 """Cast a value to a type, if appropriate.
130 Data type, such as `float`, `int`, or `str`.
135 If appropriate, the returned value is ``x`` cast to the given type
136 ``dtype``. If the cast cannot be performed the original value of
141 Will convert numpy scalar types to the standard Python equivalents.
143 if dtype
is float
and isinstance(x, numbers.Real):
145 if dtype
is int
and isinstance(x, numbers.Integral):
151 """Generate a fully-qualified type name.
156 Fully-qualified type name.
160 This function is used primarily for writing config files to be executed
161 later upon with the 'load' function.
163 if hasattr(x,
"__module__")
and hasattr(x,
"__name__"):
167 if xtype.__module__ ==
"builtins":
168 return xtype.__name__
170 return f
"{xtype.__module__}.{xtype.__name__}"
176 """Represent a Config object in a form suitable for YAML.
178 Stores the serialized stream as a scalar block string.
180 stream = io.StringIO()
181 data.saveToStream(stream)
182 config_py = stream.getvalue()
186 config_py = config_py.rstrip() +
"\n"
190 config_py = re.sub(
r"\s+$",
"\n", config_py, flags=re.MULTILINE)
193 return dumper.represent_scalar(
"lsst.pex.config.Config", config_py, style=
"|")
196 """Construct a config from YAML."""
197 config_py = loader.construct_scalar(node)
198 return Config._fromPython(config_py)
202 for loader
in YamlLoaders:
203 yaml.add_constructor(
"lsst.pex.config.Config", _yaml_config_constructor, Loader=loader)
207 """A metaclass for `lsst.pex.config.Config`.
212 Name to use for class.
213 bases : `~collections.abc.Iterable`
216 Additional parameters.
220 ``ConfigMeta`` adds a dictionary containing all `~lsst.pex.config.Field`
221 class attributes as a class attribute called ``_fields``, and adds
222 the name of each field as an instance variable of the field itself (so you
223 don't have to pass the name of the field to the field constructor).
227 type.__init__(cls, name, bases, dict_)
231 def getFields(classtype):
233 bases = list(classtype.__bases__)
236 fields.update(getFields(b))
238 for k, v
in classtype.__dict__.items():
239 if isinstance(v, Field):
243 fields = getFields(cls)
244 for k, v
in fields.items():
245 setattr(cls, k, copy.deepcopy(v))
248 if isinstance(value, Field):
251 type.__setattr__(cls, name, value)
255 """Raised when a ``~lsst.pex.config.Field`` is not valid in a
256 particular ``~lsst.pex.config.Config``.
260 field : `lsst.pex.config.Field`
261 The field that was not valid.
262 config : `lsst.pex.config.Config`
263 The config containing the invalid field.
265 Text describing why the field was not valid.
270 """Type of the `~lsst.pex.config.Field` that incurred the error.
274 """Name of the `~lsst.pex.config.Field` instance that incurred the
279 lsst.pex.config.Field.name
283 """Fully-qualified name of the `~lsst.pex.config.Field` instance
287 self.
history = config.history.setdefault(field.name, [])
288 """Full history of all changes to the `~lsst.pex.config.Field`
293 """File and line number of the `~lsst.pex.config.Field` definition.
298 f
"{self.fieldType.__name__} '{self.fullname}' failed validation: {msg}\n"
299 f
"For more information see the Field definition at:\n{self.fieldSource.format()}"
300 f
" and the Config definition at:\n{self.configSource.format()}"
306 """A field in a `~lsst.pex.config.Config` that supports `int`, `float`,
307 `complex`, `bool`, and `str` data types.
312 A description of the field for users.
313 dtype : type, optional
314 The field's data type. ``Field`` only supports basic data types:
315 `int`, `float`, `complex`, `bool`, and `str`. See
316 `Field.supportedTypes`. Optional if supplied as a typing argument to
318 default : object, optional
319 The field's default value.
320 check : callable, optional
321 A callable that is called with the field's value. This callable should
322 return `False` if the value is invalid. More complex inter-field
323 validation can be written as part of the
324 `lsst.pex.config.Config.validate` method.
325 optional : `bool`, optional
326 This sets whether the field is considered optional, and therefore
327 doesn't need to be set by the user. When `False`,
328 `lsst.pex.config.Config.validate` fails if the field's value is `None`.
329 deprecated : None or `str`, optional
330 A description of why this Field is deprecated, including removal date.
331 If not None, the string is appended to the docstring for this Field.
336 Raised when the ``dtype`` parameter is not one of the supported types
337 (see `Field.supportedTypes`).
353 ``Field`` instances (including those of any subclass of ``Field``) are used
354 as class attributes of `~lsst.pex.config.Config` subclasses (see the
355 example, below). ``Field`` attributes work like the `property` attributes
356 of classes that implement custom setters and getters. `Field` attributes
357 belong to the class, but operate on the instance. Formally speaking,
358 `Field` attributes are `descriptors
359 <https://docs.python.org/3/howto/descriptor.html>`_.
361 When you access a `Field` attribute on a `Config` instance, you don't
362 get the `Field` instance itself. Instead, you get the value of that field,
363 which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
364 container type (like a `lsst.pex.config.List`) depending on the field's
365 type. See the example, below.
367 Fields can be annotated with a type similar to other python classes (python
368 specification `here <https://peps.python.org/pep-0484/#generics>`_ ).
369 See the name field in the Config example below for an example of this.
370 Unlike most other uses in python, this has an effect at type checking *and*
371 runtime. If the type is specified with a class annotation, it will be used
372 as the value of the ``dtype`` in the ``Field`` and there is no need to
373 specify it as an argument during instantiation.
375 There are Some notes on dtype through type annotation syntax. Type
376 annotation syntax supports supplying the argument as a string of a type
377 name. i.e. "float", but this cannot be used to resolve circular references.
378 Type annotation syntax can be used on an identifier in addition to Class
379 assignment i.e. ``variable: Field[str] = Config.someField`` vs
380 ``someField = Field[str](doc="some doc"). However, this syntax is only
381 useful for annotating the type of the identifier (i.e. variable in previous
382 example) and does nothing for assigning the dtype of the ``Field``.
386 Instances of ``Field`` should be used as class attributes of
387 `lsst.pex.config.Config` subclasses:
389 >>> from lsst.pex.config import Config, Field
390 >>> class Example(Config):
391 ... myInt = Field("An integer field.", int, default=0)
392 ... name = Field[str](doc="A string Field")
393 >>> print(config.myInt)
396 >>> print(config.myInt)
401 """Identifier (variable name) used to refer to a Field within a Config
405 supportedTypes = {str, bool, float, int, complex}
406 """Supported data types for field values (`set` of types).
411 params: tuple[type, ...] | tuple[str, ...], kwds: Mapping[str, Any]
412 ) -> Mapping[str, Any]:
413 """Parse type annotations into keyword constructor arguments.
415 This is a special private method that interprets type arguments (i.e.
416 Field[str]) into keyword arguments to be passed on to the constructor.
418 Subclasses of Field can implement this method to customize how they
419 handle turning type parameters into keyword arguments (see DictField
424 params : `tuple` of `type` or `tuple` of str
425 Parameters passed to the type annotation. These will either be
426 types or strings. Strings are to interpreted as forward references
427 and will be treated as such.
428 kwds : `MutableMapping` with keys of `str` and values of `Any`
429 These are the user supplied keywords that are to be passed to the
434 kwds : `MutableMapping` with keys of `str` and values of `Any`
435 The mapping of keywords that will be passed onto the constructor
436 of the Field. Should be filled in with any information gleaned
437 from the input parameters.
442 Raised if params is of incorrect length.
443 Raised if a forward reference could not be resolved
444 Raised if there is a conflict between params and values in kwds
447 raise ValueError(
"Only single type parameters are supported")
448 unpackedParams = params[0]
449 if isinstance(unpackedParams, str):
450 _typ = ForwardRef(unpackedParams)
454 result = _typ._evaluate(globals(), locals(), recursive_guard=set())
456 raise ValueError(
"Could not deduce type from input")
457 unpackedParams = cast(type, result)
458 if "dtype" in kwds
and kwds[
"dtype"] != unpackedParams:
459 raise ValueError(
"Conflicting definition for dtype")
460 elif "dtype" not in kwds:
461 kwds = {**kwds, **{
"dtype": unpackedParams}}
467 def __init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None):
470 "dtype must either be supplied as an argument or as a type argument to the class"
473 raise ValueError(f
"Unsupported Field dtype {_typeStr(dtype)}")
475 source = getStackFrame()
483 deprecated=deprecated,
486 def _setup(self, doc, dtype, default, check, optional, source, deprecated):
487 """Set attributes, usually during initialization."""
489 """Data type for the field.
493 raise ValueError(
"Docstring is empty.")
496 if deprecated
is not None:
497 doc = f
"{doc} Deprecated: {deprecated}"
499 """A description of the field (`str`).
503 """If not None, a description of why this field is deprecated (`str`).
506 self.
__doc__ = f
"{doc} (`{dtype.__name__}`"
507 if optional
or default
is not None:
508 self.
__doc__ += f
", default ``{default!r}``"
512 """Default value for this field.
516 """A user-defined function that validates the value of the field.
520 """Flag that determines if the field is required to be set (`bool`).
522 When `False`, `lsst.pex.config.Config.validate` will fail if the
523 field's value is `None`.
527 """The stack frame where this field is defined (`list` of
528 `~lsst.pex.config.callStack.StackFrame`).
532 r"""Rename the field in a `~lsst.pex.config.Config` (for internal use
537 instance : `lsst.pex.config.Config`
538 The config instance that contains this field.
542 This method is invoked by the `lsst.pex.config.Config` object that
543 contains this field and should not be called directly.
545 Renaming is only relevant for `~lsst.pex.config.Field` instances that
546 hold subconfigs. `~lsst.pex.config.Field`\s that hold subconfigs should
547 rename each subconfig with the full field name as generated by
548 `lsst.pex.config.config._joinNamePath`.
553 """Validate the field (for internal use only).
557 instance : `lsst.pex.config.Config`
558 The config instance that contains this field.
562 lsst.pex.config.FieldValidationError
563 Raised if verification fails.
567 This method provides basic validation:
569 - Ensures that the value is not `None` if the field is not optional.
570 - Ensures type correctness.
571 - Ensures that the user-provided ``check`` function is valid.
573 Most `~lsst.pex.config.Field` subclasses should call
574 `lsst.pex.config.Field.validate` if they re-implement
575 `~lsst.pex.config.Field.validate`.
577 value = self.__get__(instance)
578 if not self.optional
and value
is None:
579 raise FieldValidationError(self, instance,
"Required value cannot be None")
582 """Make this field read-only (for internal use only).
586 instance : `lsst.pex.config.Config`
587 The config instance that contains this field.
591 Freezing is only relevant for fields that hold subconfigs. Fields which
592 hold subconfigs should freeze each subconfig.
594 **Subclasses should implement this method.**
604 The value being validated.
609 Raised if the value's type is incompatible with the field's
612 Raised if the value is rejected by the ``check`` method.
617 if not isinstance(value, self.dtype):
619 f
"Value {value} is of incorrect type {_typeStr(value)}. Expected type {_typeStr(self.dtype)}"
622 if self.check
is not None and not self.check(value):
623 msg = f
"Value {value} is not a valid value"
624 raise ValueError(msg)
627 """Call the _collectImports method on all config
628 objects the field may own, and union them with the supplied imports
633 instance : instance or subclass of `lsst.pex.config.Config`
634 A config object that has this field defined on it
636 Set of python modules that need imported after persistence
640 def save(self, outfile, instance):
641 """Save this field to a file (for internal use only).
645 outfile : file-like object
646 A writeable field handle.
647 instance : `~lsst.pex.config.Config`
648 The `~lsst.pex.config.Config` instance that contains this field.
652 This method is invoked by the `~lsst.pex.config.Config` object that
653 contains this field and should not be called directly.
655 The output consists of the documentation string
656 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second
657 line is formatted as an assignment: ``{fullname}={value}``.
659 This output can be executed with Python.
661 value = self.__get__(instance)
664 if self.deprecated
and value == self.default:
669 doc =
"# " + str(self.doc).replace(
"\n",
"\n# ")
670 if isinstance(value, float)
and not math.isfinite(value):
672 outfile.write(f
"{doc}\n{fullname}=float('{value!r}')\n\n")
674 outfile.write(f
"{doc}\n{fullname}={value!r}\n\n")
677 """Convert the field value so that it can be set as the value of an
678 item in a `dict` (for internal use only).
682 instance : `~lsst.pex.config.Config`
683 The `~lsst.pex.config.Config` that contains this field.
688 The field's value. See *Notes*.
692 This method invoked by the owning `~lsst.pex.config.Config` object and
693 should not be called directly.
695 Simple values are passed through. Complex data structures must be
696 manipulated. For example, a `~lsst.pex.config.Field` holding a
697 subconfig should, instead of the subconfig object, return a `dict`
698 where the keys are the field names in the subconfig, and the values are
699 the field values in the subconfig.
705 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
706 ) -> Field[FieldTypeVar]: ...
710 self, instance: Config, owner: Any =
None, at: Any =
None, label: str =
"default"
711 ) -> FieldTypeVar: ...
713 def __get__(self, instance, owner=None, at=None, label="default"):
714 """Define how attribute access should occur on the Config instance
715 This is invoked by the owning config object and should not be called
718 When the field attribute is accessed on a Config class object, it
719 returns the field object itself in order to allow inspection of
722 When the field attribute is access on a config instance, the actual
723 value described by the field (and held by the Config instance) is
731 return instance._storage[self.name]
732 except AttributeError:
733 if not isinstance(instance, Config):
736 raise AttributeError(
737 f
"Config {instance} is missing _storage attribute, likely incorrectly initialized"
741 self, instance: Config, value: FieldTypeVar |
None, at: Any =
None, label: str =
"assignment"
743 """Set an attribute on the config instance.
747 instance : `lsst.pex.config.Config`
748 The config instance that contains this field.
750 Value to set on this field.
751 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`,\
753 The call stack (created by
754 `lsst.pex.config.callStack.getCallStack`).
755 label : `str`, optional
756 Event label for the history.
760 This method is invoked by the owning `lsst.pex.config.Config` object
761 and should not be called directly.
763 Derived `~lsst.pex.config.Field` classes may need to override the
764 behavior. When overriding ``__set__``, `~lsst.pex.config.Field` authors
765 should follow the following rules:
767 - Do not allow modification of frozen configs.
768 - Validate the new value **before** modifying the field. Except if the
769 new value is `None`. `None` is special and no attempt should be made
770 to validate it until `lsst.pex.config.Config.validate` is called.
771 - Do not modify the `~lsst.pex.config.Config` instance to contain
773 - If the field is modified, update the history of the
774 `lsst.pex.config.field.Field` to reflect the changes.
776 In order to decrease the need to implement this method in derived
777 `~lsst.pex.config.Field` types, value validation is performed in the
778 `lsst.pex.config.Field._validateValue`. If only the validation step
779 differs in the derived `~lsst.pex.config.Field`, it is simpler to
780 implement `lsst.pex.config.Field._validateValue` than to reimplement
781 ``__set__``. More complicated behavior, however, may require
787 history = instance._history.setdefault(self.name, [])
788 if value
is not None:
792 except BaseException
as e:
795 instance._storage[self.name] = value
798 history.append((value, at, label))
801 """Delete an attribute from a `lsst.pex.config.Config` instance.
805 instance : `lsst.pex.config.Config`
806 The config instance that contains this field.
807 at : `list` of `lsst.pex.config.callStack.StackFrame`
808 The call stack (created by
809 `lsst.pex.config.callStack.getCallStack`).
810 label : `str`, optional
811 Event label for the history.
815 This is invoked by the owning `~lsst.pex.config.Config` object and
816 should not be called directly.
820 self.
__set__(instance,
None, at=at, label=label)
822 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
823 """Compare a field (named `Field.name`) in two
824 `~lsst.pex.config.Config` instances for equality.
828 instance1 : `lsst.pex.config.Config`
829 Left-hand side `Config` instance to compare.
830 instance2 : `lsst.pex.config.Config`
831 Right-hand side `Config` instance to compare.
832 shortcut : `bool`, optional
834 rtol : `float`, optional
835 Relative tolerance for floating point comparisons.
836 atol : `float`, optional
837 Absolute tolerance for floating point comparisons.
838 output : callable, optional
839 A callable that takes a string, used (possibly repeatedly) to
844 This method must be overridden by more complex `Field` subclasses.
848 lsst.pex.config.compareScalars
850 v1 = getattr(instance1, self.name)
851 v2 = getattr(instance2, self.name)
859 """Importer (for `sys.meta_path`) that records which modules are being
862 *This class does not do any importing itself.*
866 Use this class as a context manager to ensure it is properly uninstalled
869 >>> with RecordingImporter() as importer:
871 ... import numpy as np
872 ... print("Imported: " + importer.getModules())
880 sys.meta_path = [self] + sys.meta_path
888 """Uninstall the importer."""
894 Called as part of the ``import`` chain of events.
900 path : `list` [`str`]
902 target : `~typing.Any`, optional
910 """Get the set of modules that were imported.
914 modules : `set` of `str`
915 Set of imported module names.
922 """Base class for configuration (*config*) objects.
926 A ``Config`` object will usually have several `~lsst.pex.config.Field`
927 instances as class attributes. These are used to define most of the base
930 ``Config`` implements a mapping API that provides many `dict`-like methods,
931 such as `keys`, `values`, and `items`. ``Config`` instances also support
932 the ``in`` operator to test if a field is in the config. Unlike a `dict`,
933 ``Config`` classes are not subscriptable. Instead, access individual
934 fields as attributes of the configuration instance.
938 Config classes are subclasses of ``Config`` that have
939 `~lsst.pex.config.Field` instances (or instances of
940 `~lsst.pex.config.Field` subclasses) as class attributes:
942 >>> from lsst.pex.config import Config, Field, ListField
943 >>> class DemoConfig(Config):
944 ... intField = Field(doc="An integer field", dtype=int, default=42)
945 ... listField = ListField(
946 ... doc="List of favorite beverages.",
948 ... default=["coffee", "green tea", "water"],
950 >>> config = DemoConfig()
952 Configs support many `dict`-like APIs:
955 ['intField', 'listField']
956 >>> "intField" in config
959 Individual fields can be accessed as attributes of the configuration:
963 >>> config.listField.append("earl grey tea")
964 >>> print(config.listField)
965 ['coffee', 'green tea', 'water', 'earl grey tea']
968 _storage: dict[str, Any]
969 _fields: dict[str, Field]
970 _history: dict[str, list[Any]]
974 """Iterate over fields."""
982 names : `~collections.abc.KeysView`
983 List of `lsst.pex.config.Field` names.
992 values : `~collections.abc.ValuesView`
993 Iterator of field values.
998 """Get configurations as ``(field name, field value)`` pairs.
1002 items : `~collections.abc.ItemsView`
1003 Iterator of tuples for each configuration. Tuple items are:
1011 """Return `True` if the specified field exists in this config.
1016 Field name to test for.
1021 `True` if the specified field exists in the config.
1026 """Allocate a new `lsst.pex.config.Config` object.
1028 In order to ensure that all Config object are always in a proper state
1029 when handed to users or to derived `~lsst.pex.config.Config` classes,
1030 some attributes are handled at allocation time rather than at
1033 This ensures that even if a derived `~lsst.pex.config.Config` class
1034 implements ``__init__``, its author does not need to be concerned about
1035 when or even the base ``Config.__init__`` should be called.
1037 name = kw.pop(
"__name",
None)
1038 at = kw.pop(
"__at", getCallStack())
1040 kw.pop(
"__label",
"default")
1042 instance = object.__new__(cls)
1043 instance._frozen =
False
1044 instance._name = name
1045 instance._storage = {}
1046 instance._history = {}
1047 instance._imports = set()
1049 for field
in instance._fields.values():
1050 instance._history[field.name] = []
1051 field.__set__(instance, field.default, at=at + [field.source], label=
"default")
1053 instance.setDefaults()
1055 instance.update(__at=at, **kw)
1059 """Reduction for pickling (function with arguments to reproduce).
1061 We need to condense and reconstitute the `~lsst.pex.config.Config`,
1062 since it may contain lambdas (as the ``check`` elements) that cannot
1067 stream = io.StringIO()
1069 return (unreduceConfig, (self.
__class__, stream.getvalue().encode()))
1072 """Subclass hook for computing defaults.
1076 Derived `~lsst.pex.config.Config` classes that must compute defaults
1077 rather than using the `~lsst.pex.config.Field` instances's defaults
1078 should do so here. To correctly use inherited defaults,
1079 implementations of ``setDefaults`` must call their base class's
1085 """Update values of fields specified by the keyword arguments.
1090 Keywords are configuration field names. Values are configuration
1095 The ``__at`` and ``__label`` keyword arguments are special internal
1096 keywords. They are used to strip out any internal steps from the
1097 history tracebacks of the config. Do not modify these keywords to
1098 subvert a `~lsst.pex.config.Config` instance's history.
1102 This is a config with three fields:
1104 >>> from lsst.pex.config import Config, Field
1105 >>> class DemoConfig(Config):
1106 ... fieldA = Field(doc="Field A", dtype=int, default=42)
1107 ... fieldB = Field(doc="Field B", dtype=bool, default=True)
1108 ... fieldC = Field(doc="Field C", dtype=str, default="Hello world")
1109 >>> config = DemoConfig()
1111 These are the default values of each field:
1113 >>> for name, value in config.iteritems():
1114 ... print(f"{name}: {value}")
1117 fieldC: 'Hello world'
1119 Using this method to update ``fieldA`` and ``fieldC``:
1121 >>> config.update(fieldA=13, fieldC="Updated!")
1123 Now the values of each field are:
1125 >>> for name, value in config.iteritems():
1126 ... print(f"{name}: {value}")
1131 at = kw.pop(
"__at", getCallStack())
1132 label = kw.pop(
"__label",
"update")
1134 for name, value
in kw.items():
1136 field = self._fields[name]
1137 field.__set__(self, value, at=at, label=label)
1138 except KeyError
as e:
1139 e.add_note(f
"No field of name {name} exists in config type {_typeStr(self)}")
1142 def load(self, filename, root="config"):
1143 """Modify this config in place by executing the Python code in a
1149 Name of the configuration file. A configuration file is Python
1151 root : `str`, optional
1152 Name of the variable in file that refers to the config being
1155 For example, the value of root is ``"config"`` and the file
1160 Then this config's field ``myField`` is set to ``5``.
1164 lsst.pex.config.Config.loadFromStream
1165 lsst.pex.config.Config.loadFromString
1166 lsst.pex.config.Config.save
1167 lsst.pex.config.Config.saveToStream
1168 lsst.pex.config.Config.saveToString
1170 with open(filename)
as f:
1171 code = compile(f.read(), filename=filename, mode=
"exec")
1175 """Modify this Config in place by executing the Python code in the
1180 stream : file-like object, `str`, `bytes`, or `~types.CodeType`
1181 Stream containing configuration override code. If this is a
1182 code object, it should be compiled with ``mode="exec"``.
1183 root : `str`, optional
1184 Name of the variable in file that refers to the config being
1187 For example, the value of root is ``"config"`` and the file
1192 Then this config's field ``myField`` is set to ``5``.
1193 filename : `str`, optional
1194 Name of the configuration file, or `None` if unknown or contained
1195 in the stream. Used for error reporting.
1196 extraLocals : `dict` of `str` to `object`, optional
1197 Any extra variables to include in local scope when loading.
1201 For backwards compatibility reasons, this method accepts strings, bytes
1202 and code objects as well as file-like objects. New code should use
1203 `loadFromString` instead for most of these types.
1207 lsst.pex.config.Config.load
1208 lsst.pex.config.Config.loadFromString
1209 lsst.pex.config.Config.save
1210 lsst.pex.config.Config.saveToStream
1211 lsst.pex.config.Config.saveToString
1213 if hasattr(stream,
"read"):
1214 if filename
is None:
1215 filename = getattr(stream,
"name",
"?")
1216 code = compile(stream.read(), filename=filename, mode=
"exec")
1219 self.
loadFromString(code, root=root, filename=filename, extraLocals=extraLocals)
1222 """Modify this Config in place by executing the Python code in the
1227 code : `str`, `bytes`, or `~types.CodeType`
1228 Stream containing configuration override code.
1229 root : `str`, optional
1230 Name of the variable in file that refers to the config being
1233 For example, the value of root is ``"config"`` and the file
1238 Then this config's field ``myField`` is set to ``5``.
1239 filename : `str`, optional
1240 Name of the configuration file, or `None` if unknown or contained
1241 in the stream. Used for error reporting.
1242 extraLocals : `dict` of `str` to `object`, optional
1243 Any extra variables to include in local scope when loading.
1248 Raised if a key in extraLocals is the same value as the value of
1253 lsst.pex.config.Config.load
1254 lsst.pex.config.Config.loadFromStream
1255 lsst.pex.config.Config.save
1256 lsst.pex.config.Config.saveToStream
1257 lsst.pex.config.Config.saveToString
1259 if filename
is None:
1262 filename = getattr(code,
"co_filename",
"?")
1264 globals = {
"__file__": filename}
1265 local = {root: self}
1266 if extraLocals
is not None:
1268 if root
in extraLocals:
1270 f
"{root} is reserved and cannot be used as a variable name in extraLocals"
1272 local.update(extraLocals)
1273 exec(code, globals, local)
1277 def save(self, filename, root="config"):
1278 """Save a Python script to the named file, which, when loaded,
1279 reproduces this config.
1284 Desination filename of this configuration.
1285 root : `str`, optional
1286 Name to use for the root config variable. The same value must be
1287 used when loading (see `lsst.pex.config.Config.load`).
1291 lsst.pex.config.Config.saveToStream
1292 lsst.pex.config.Config.saveToString
1293 lsst.pex.config.Config.load
1294 lsst.pex.config.Config.loadFromStream
1295 lsst.pex.config.Config.loadFromString
1297 d = os.path.dirname(filename)
1298 with tempfile.NamedTemporaryFile(mode=
"w", delete=
False, dir=d)
as outfile:
1303 umask = os.umask(0o077)
1305 os.chmod(outfile.name, (~umask & 0o666))
1309 shutil.move(outfile.name, filename)
1312 """Return the Python script form of this configuration as an executable
1317 skipImports : `bool`, optional
1318 If `True` then do not include ``import`` statements in output,
1319 this is to support human-oriented output from ``pipetask`` where
1320 additional clutter is not useful.
1325 A code string readable by `loadFromString`.
1329 lsst.pex.config.Config.save
1330 lsst.pex.config.Config.saveToStream
1331 lsst.pex.config.Config.load
1332 lsst.pex.config.Config.loadFromStream
1333 lsst.pex.config.Config.loadFromString
1335 buffer = io.StringIO()
1337 return buffer.getvalue()
1340 """Save a configuration file to a stream, which, when loaded,
1341 reproduces this config.
1345 outfile : file-like object
1346 Destination file object write the config into. Accepts strings not
1348 root : `str`, optional
1349 Name to use for the root config variable. The same value must be
1350 used when loading (see `lsst.pex.config.Config.load`).
1351 skipImports : `bool`, optional
1352 If `True` then do not include ``import`` statements in output,
1353 this is to support human-oriented output from ``pipetask`` where
1354 additional clutter is not useful.
1358 lsst.pex.config.Config.save
1359 lsst.pex.config.Config.saveToString
1360 lsst.pex.config.Config.load
1361 lsst.pex.config.Config.loadFromStream
1362 lsst.pex.config.Config.loadFromString
1371 configType = type(self)
1373 outfile.write(f
"import {configType.__module__}\n")
1378 f
'assert type({root}) is {typeString}, f"config is of type '
1379 f
'{{type({root}).__module__}}.{{type({root}).__name__}} instead of {typeString}"\n\n'
1382 if imp
in sys.modules
and sys.modules[imp]
is not None:
1383 outfile.write(f
"import {imp}\n")
1389 """Make this config, and all subconfigs, read-only."""
1395 """Save this config to an open stream object.
1399 outfile : file-like object
1400 Destination file object write the config into. Accepts strings not
1404 field.save(outfile, self)
1407 """Add module containing self to the list of things to import and
1408 then loops over all the fields in the config calling a corresponding
1411 The field method will call _collectImports on any
1412 configs it may own and return the set of things to import. This
1413 returned set will be merged with the set of imports for this config
1418 field._collectImports(self, self.
_imports)
1421 """Make a dictionary of field names and their values.
1426 Dictionary with keys that are `~lsst.pex.config.Field` names.
1427 Values are `~lsst.pex.config.Field` values.
1431 lsst.pex.config.Field.toDict
1435 This method uses the `~lsst.pex.config.Field.toDict` method of
1436 individual fields. Subclasses of `~lsst.pex.config.Field` may need to
1437 implement a ``toDict`` method for *this* method to work.
1441 dict_[name] = field.toDict(self)
1445 """Get all the field names in the config, recursively.
1449 names : `list` of `str`
1456 with io.StringIO()
as strFd:
1458 contents = strFd.getvalue()
1464 for line
in contents.split(
"\n"):
1465 if re.search(
r"^((assert|import)\s+|\s*$|#)", line):
1468 mat = re.search(
r"^(?:config\.)?([^=]+)\s*=\s*.*", line)
1470 keys.append(mat.group(1))
1475 """Rename this config object in its parent `~lsst.pex.config.Config`.
1480 New name for this config in its parent `~lsst.pex.config.Config`.
1484 This method uses the `~lsst.pex.config.Field.rename` method of
1485 individual `lsst.pex.config.Field` instances.
1486 `lsst.pex.config.Field` subclasses may need to implement a ``rename``
1487 method for *this* method to work.
1491 lsst.pex.config.Field.rename
1498 """Validate the Config, raising an exception if invalid.
1502 lsst.pex.config.FieldValidationError
1503 Raised if verification fails.
1507 The base class implementation performs type checks on all fields by
1508 calling their `~lsst.pex.config.Field.validate` methods.
1510 Complex single-field validation can be defined by deriving new Field
1511 types. For convenience, some derived `lsst.pex.config.Field`-types
1512 (`~lsst.pex.config.ConfigField` and
1513 `~lsst.pex.config.ConfigChoiceField`) are defined in
1514 ``lsst.pex.config`` that handle recursing into subconfigs.
1516 Inter-field relationships should only be checked in derived
1517 `~lsst.pex.config.Config` classes after calling this method, and base
1518 validation is complete.
1521 field.validate(self)
1524 """Format a configuration field's history to a human-readable format.
1529 Name of a `~lsst.pex.config.Field` in this config.
1531 Keyword arguments passed to `lsst.pex.config.history.format`.
1536 A string containing the formatted history.
1540 lsst.pex.config.history.format
1544 return pexHist.format(self, name, **kwargs)
1546 history = property(
lambda x: x._history)
1547 """Read-only history.
1551 """Set an attribute (such as a field's value).
1555 Unlike normal Python objects, `~lsst.pex.config.Config` objects are
1556 locked such that no additional attributes nor properties may be added
1557 to them dynamically.
1559 Although this is not the standard Python behavior, it helps to protect
1560 users from accidentally mispelling a field name, or trying to set a
1564 if self.
_fields[attr].deprecated
is not None:
1567 f
"Config field {fullname} is deprecated: {self._fields[attr].deprecated}",
1574 self.
_fields[attr].__set__(self, value, at=at, label=label)
1575 elif hasattr(getattr(self.
__class__, attr,
None),
"__set__"):
1577 return object.__setattr__(self, attr, value)
1578 elif attr
in self.__dict__
or attr
in (
"_name",
"_history",
"_storage",
"_frozen",
"_imports"):
1580 self.__dict__[attr] = value
1583 raise AttributeError(f
"{_typeStr(self)} has no attribute {attr}")
1589 self.
_fields[attr].__delete__(self, at=at, label=label)
1591 object.__delattr__(self, attr)
1594 if type(other)
is type(self):
1596 thisValue = getattr(self, name)
1597 otherValue = getattr(other, name)
1598 if isinstance(thisValue, float)
and math.isnan(thisValue):
1599 if not math.isnan(otherValue):
1601 elif thisValue != otherValue:
1607 return not self.
__eq__(other)
1610 return str(self.
toDict())
1613 return "{}({})".format(
1615 ", ".join(f
"{k}={v!r}" for k, v
in self.
toDict().
items()
if v
is not None),
1618 def compare(self, other, shortcut=True, rtol=1e-8, atol=1e-8, output=None):
1619 """Compare this configuration to another `~lsst.pex.config.Config` for
1624 other : `lsst.pex.config.Config`
1625 Other `~lsst.pex.config.Config` object to compare against this
1627 shortcut : `bool`, optional
1628 If `True`, return as soon as an inequality is found. Default is
1630 rtol : `float`, optional
1631 Relative tolerance for floating point comparisons.
1632 atol : `float`, optional
1633 Absolute tolerance for floating point comparisons.
1634 output : callable, optional
1635 A callable that takes a string, used (possibly repeatedly) to
1636 report inequalities.
1641 `True` when the two `lsst.pex.config.Config` instances are equal.
1642 `False` if there is an inequality.
1646 lsst.pex.config.compareConfigs
1650 Unselected targets of `~lsst.pex.config.RegistryField` fields and
1651 unselected choices of `~lsst.pex.config.ConfigChoiceField` fields
1652 are not considered by this method.
1654 Floating point comparisons are performed by `numpy.allclose`.
1656 name1 = self.
_name if self.
_name is not None else "config"
1657 name2 = other._name
if other._name
is not None else "config"
1659 return compareConfigs(name, self, other, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
1663 """Run initialization for every subclass.
1665 Specifically registers the subclass with a YAML representer
1666 and YAML constructor (if pyyaml is available)
1673 yaml.add_representer(cls, _yaml_config_representer)
1677 """Instantiate a `Config`-subclass from serialized Python form.
1682 A serialized form of the Config as created by
1683 `Config.saveToStream`.
1688 Reconstructed `Config` instant.
1695 """Return the Config subclass required by this Config serialization.
1700 A serialized form of the Config as created by
1701 `Config.saveToStream`.
1706 The `Config` subclass associated with this config.
1718 matches = re.search(
r"^import ([\w.]+)\nassert type\(\S+\)(?:\s*==\s*| is )(.*?),", config_py)
1721 first_line, second_line, _ = config_py.split(
"\n", 2)
1723 f
"First two lines did not match expected form. Got:\n - {first_line}\n - {second_line}"
1726 module_name = matches.group(1)
1727 module = importlib.import_module(module_name)
1730 full_name = matches.group(2)
1733 if not full_name.startswith(module_name):
1734 raise ValueError(f
"Module name ({module_name}) inconsistent with full name ({full_name})")
1739 remainder = full_name[len(module_name) + 1 :]
1740 components = remainder.split(
".")
1742 for component
in components:
1743 pytype = getattr(pytype, component)
1748 """Create a `~lsst.pex.config.Config` from a stream.
1752 cls_ : `lsst.pex.config.Config`-type
1753 A `lsst.pex.config.Config` type (not an instance) that is instantiated
1754 with configurations in the ``stream``.
1755 stream : file-like object, `str`, or `~types.CodeType`
1756 Stream containing configuration override code.
1760 config : `lsst.pex.config.Config`
1765 lsst.pex.config.Config.loadFromStream
1768 config.loadFromStream(stream)
Any __call__(self, *Any args, **Any kwds)
saveToStream(self, outfile, root="config", skipImports=False)
__setattr__(self, attr, value, at=None, label="assignment")
loadFromStream(self, stream, root="config", filename=None, extraLocals=None)
__new__(cls, *args, **kw)
save(self, filename, root="config")
_fromPython(cls, config_py)
loadFromString(self, code, root="config", filename=None, extraLocals=None)
__delattr__(self, attr, at=None, label="deletion")
saveToString(self, skipImports=False)
formatHistory(self, name, **kwargs)
__init_subclass__(cls, **kwargs)
load(self, filename, root="config")
compare(self, other, shortcut=True, rtol=1e-8, atol=1e-8, output=None)
Mapping[str, Any] _parseTypingArgs(tuple[type,...]|tuple[str,...] params, Mapping[str, Any] kwds)
save(self, outfile, instance)
_collectImports(self, instance, imports)
__delete__(self, instance, at=None, label="deletion")
Field[FieldTypeVar] __get__(self, None instance, Any owner=None, Any at=None, str label="default")
_compare(self, instance1, instance2, shortcut, rtol, atol, output)
_validateValue(self, value)
None __set__(self, Config instance, FieldTypeVar|None value, Any at=None, str label="assignment")
__class_getitem__(cls, tuple[type,...]|type|ForwardRef params)
__init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None)
_setup(self, doc, dtype, default, check, optional, source, deprecated)
__init__(self, field, config, msg)
find_spec(self, fullname, path, target=None)
compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=None)
getComparisonName(name1, name2)
compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None)
_yaml_config_representer(dumper, data)
_classFromPython(config_py)
_joinNamePath(prefix=None, name=None, index=None)
unreduceConfig(cls_, stream)