27from __future__
import annotations
33 "FieldValidationError",
34 "UnexpectedProxyUsageError",
49from types
import GenericAlias
50from typing
import Any, ForwardRef, Generic, TypeVar, cast, overload
59from .callStack
import getCallStack, getStackFrame
60from .comparison
import compareConfigs, compareScalars, getComparisonName
63 YamlLoaders: tuple[Any, ...] = (yaml.Loader, yaml.FullLoader, yaml.SafeLoader, yaml.UnsafeLoader)
67 from yaml
import CLoader
69 YamlLoaders += (CLoader,)
78 """A Subclass of python's GenericAlias used in defining and instantiating
81 This class differs from `types.GenericAlias` in that it calls a method
82 named _parseTypingArgs defined on Fields. This method gives Field and its
83 subclasses an opportunity to transform type parameters into class key word
84 arguments. Code authors do not need to implement any returns of this object
85 directly, and instead only need implement _parseTypingArgs, if a Field
86 subclass differs from the base class implementation.
88 This class is intended to be an implementation detail, returned from a
89 Field's `__class_getitem__` method.
92 def __call__(self, *args: Any, **kwds: Any) -> Any:
93 origin_kwargs = self._parseTypingArgs(self.__args__, kwds)
94 return super().
__call__(*args, **{**kwds, **origin_kwargs})
97FieldTypeVar = TypeVar(
"FieldTypeVar")
101 """Exception raised when a proxy class is used in a context that suggests
102 it should have already been converted to the thing it proxies.
107 """Generate nested configuration names."""
108 if not prefix
and not name:
109 raise ValueError(
"Invalid name: cannot be None")
112 elif prefix
and name:
113 name = prefix +
"." + name
115 if index
is not None:
116 return f
"{name}[{index!r}]"
122 """Cast a value to a type, if appropriate.
129 Data type, such as `float`, `int`, or `str`.
134 If appropriate, the returned value is ``x`` cast to the given type
135 ``dtype``. If the cast cannot be performed the original value of
138 if dtype
is float
and isinstance(x, int):
144 """Generate a fully-qualified type name.
149 Fully-qualified type name.
153 This function is used primarily for writing config files to be executed
154 later upon with the 'load' function.
156 if hasattr(x,
"__module__")
and hasattr(x,
"__name__"):
160 if xtype.__module__ ==
"builtins":
161 return xtype.__name__
163 return f
"{xtype.__module__}.{xtype.__name__}"
169 """Represent a Config object in a form suitable for YAML.
171 Stores the serialized stream as a scalar block string.
173 stream = io.StringIO()
174 data.saveToStream(stream)
175 config_py = stream.getvalue()
179 config_py = config_py.rstrip() +
"\n"
183 config_py = re.sub(
r"\s+$",
"\n", config_py, flags=re.MULTILINE)
186 return dumper.represent_scalar(
"lsst.pex.config.Config", config_py, style=
"|")
189 """Construct a config from YAML."""
190 config_py = loader.construct_scalar(node)
191 return Config._fromPython(config_py)
195 for loader
in YamlLoaders:
196 yaml.add_constructor(
"lsst.pex.config.Config", _yaml_config_constructor, Loader=loader)
200 """A metaclass for `lsst.pex.config.Config`.
205 Name to use for class.
206 bases : `~collections.abc.Iterable`
209 Additional parameters.
213 ``ConfigMeta`` adds a dictionary containing all `~lsst.pex.config.Field`
214 class attributes as a class attribute called ``_fields``, and adds
215 the name of each field as an instance variable of the field itself (so you
216 don't have to pass the name of the field to the field constructor).
220 type.__init__(cls, name, bases, dict_)
224 def getFields(classtype):
226 bases = list(classtype.__bases__)
229 fields.update(getFields(b))
231 for k, v
in classtype.__dict__.items():
232 if isinstance(v, Field):
236 fields = getFields(cls)
237 for k, v
in fields.items():
238 setattr(cls, k, copy.deepcopy(v))
241 if isinstance(value, Field):
244 type.__setattr__(cls, name, value)
248 """Raised when a ``~lsst.pex.config.Field`` is not valid in a
249 particular ``~lsst.pex.config.Config``.
253 field : `lsst.pex.config.Field`
254 The field that was not valid.
255 config : `lsst.pex.config.Config`
256 The config containing the invalid field.
258 Text describing why the field was not valid.
263 """Type of the `~lsst.pex.config.Field` that incurred the error.
267 """Name of the `~lsst.pex.config.Field` instance that incurred the
272 lsst.pex.config.Field.name
275 self.
fullname = _joinNamePath(config._name, field.name)
276 """Fully-qualified name of the `~lsst.pex.config.Field` instance
280 self.
history = config.history.setdefault(field.name, [])
281 """Full history of all changes to the `~lsst.pex.config.Field`
286 """File and line number of the `~lsst.pex.config.Field` definition.
291 f
"{self.fieldType.__name__} '{self.fullname}' failed validation: {msg}\n"
292 f
"For more information see the Field definition at:\n{self.fieldSource.format()}"
293 f
" and the Config definition at:\n{self.configSource.format()}"
299 """A field in a `~lsst.pex.config.Config` that supports `int`, `float`,
300 `complex`, `bool`, and `str` data types.
305 A description of the field for users.
306 dtype : type, optional
307 The field's data type. ``Field`` only supports basic data types:
308 `int`, `float`, `complex`, `bool`, and `str`. See
309 `Field.supportedTypes`. Optional if supplied as a typing argument to
311 default : object, optional
312 The field's default value.
313 check : callable, optional
314 A callable that is called with the field's value. This callable should
315 return `False` if the value is invalid. More complex inter-field
316 validation can be written as part of the
317 `lsst.pex.config.Config.validate` method.
318 optional : `bool`, optional
319 This sets whether the field is considered optional, and therefore
320 doesn't need to be set by the user. When `False`,
321 `lsst.pex.config.Config.validate` fails if the field's value is `None`.
322 deprecated : None or `str`, optional
323 A description of why this Field is deprecated, including removal date.
324 If not None, the string is appended to the docstring for this Field.
329 Raised when the ``dtype`` parameter is not one of the supported types
330 (see `Field.supportedTypes`).
346 ``Field`` instances (including those of any subclass of ``Field``) are used
347 as class attributes of `~lsst.pex.config.Config` subclasses (see the
348 example, below). ``Field`` attributes work like the `property` attributes
349 of classes that implement custom setters and getters. `Field` attributes
350 belong to the class, but operate on the instance. Formally speaking,
351 `Field` attributes are `descriptors
352 <https://docs.python.org/3/howto/descriptor.html>`_.
354 When you access a `Field` attribute on a `Config` instance, you don't
355 get the `Field` instance itself. Instead, you get the value of that field,
356 which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
357 container type (like a `lsst.pex.config.List`) depending on the field's
358 type. See the example, below.
360 Fields can be annotated with a type similar to other python classes (python
361 specification `here <https://peps.python.org/pep-0484/#generics>`_ ).
362 See the name field in the Config example below for an example of this.
363 Unlike most other uses in python, this has an effect at type checking *and*
364 runtime. If the type is specified with a class annotation, it will be used
365 as the value of the ``dtype`` in the ``Field`` and there is no need to
366 specify it as an argument during instantiation.
368 There are Some notes on dtype through type annotation syntax. Type
369 annotation syntax supports supplying the argument as a string of a type
370 name. i.e. "float", but this cannot be used to resolve circular references.
371 Type annotation syntax can be used on an identifier in addition to Class
372 assignment i.e. ``variable: Field[str] = Config.someField`` vs
373 ``someField = Field[str](doc="some doc"). However, this syntax is only
374 useful for annotating the type of the identifier (i.e. variable in previous
375 example) and does nothing for assigning the dtype of the ``Field``.
379 Instances of ``Field`` should be used as class attributes of
380 `lsst.pex.config.Config` subclasses:
382 >>> from lsst.pex.config import Config, Field
383 >>> class Example(Config):
384 ... myInt = Field("An integer field.", int, default=0)
385 ... name = Field[str](doc="A string Field")
387 >>> print(config.myInt)
390 >>> print(config.myInt)
395 """Identifier (variable name) used to refer to a Field within a Config
399 supportedTypes = {str, bool, float, int, complex}
400 """Supported data types for field values (`set` of types).
405 params: tuple[type, ...] | tuple[str, ...], kwds: Mapping[str, Any]
406 ) -> Mapping[str, Any]:
407 """Parse type annotations into keyword constructor arguments.
409 This is a special private method that interprets type arguments (i.e.
410 Field[str]) into keyword arguments to be passed on to the constructor.
412 Subclasses of Field can implement this method to customize how they
413 handle turning type parameters into keyword arguments (see DictField
418 params : `tuple` of `type` or `tuple` of str
419 Parameters passed to the type annotation. These will either be
420 types or strings. Strings are to interpreted as forward references
421 and will be treated as such.
422 kwds : `MutableMapping` with keys of `str` and values of `Any`
423 These are the user supplied keywords that are to be passed to the
428 kwds : `MutableMapping` with keys of `str` and values of `Any`
429 The mapping of keywords that will be passed onto the constructor
430 of the Field. Should be filled in with any information gleaned
431 from the input parameters.
436 Raised if params is of incorrect length.
437 Raised if a forward reference could not be resolved
438 Raised if there is a conflict between params and values in kwds
441 raise ValueError(
"Only single type parameters are supported")
442 unpackedParams = params[0]
443 if isinstance(unpackedParams, str):
444 _typ = ForwardRef(unpackedParams)
448 result = _typ._evaluate(globals(), locals(), recursive_guard=set())
450 raise ValueError(
"Could not deduce type from input")
451 unpackedParams = cast(type, result)
452 if "dtype" in kwds
and kwds[
"dtype"] != unpackedParams:
453 raise ValueError(
"Conflicting definition for dtype")
454 elif "dtype" not in kwds:
455 kwds = {**kwds, **{
"dtype": unpackedParams}}
461 def __init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None):
464 "dtype must either be supplied as an argument or as a type argument to the class"
467 raise ValueError(f
"Unsupported Field dtype {_typeStr(dtype)}")
469 source = getStackFrame()
477 deprecated=deprecated,
480 def _setup(self, doc, dtype, default, check, optional, source, deprecated):
481 """Set attributes, usually during initialization."""
483 """Data type for the field.
487 raise ValueError(
"Docstring is empty.")
490 if deprecated
is not None:
491 doc = f
"{doc} Deprecated: {deprecated}"
493 """A description of the field (`str`).
497 """If not None, a description of why this field is deprecated (`str`).
500 self.
__doc__ = f
"{doc} (`{dtype.__name__}`"
501 if optional
or default
is not None:
502 self.
__doc__ += f
", default ``{default!r}``"
506 """Default value for this field.
510 """A user-defined function that validates the value of the field.
514 """Flag that determines if the field is required to be set (`bool`).
516 When `False`, `lsst.pex.config.Config.validate` will fail if the
517 field's value is `None`.
521 """The stack frame where this field is defined (`list` of
522 `~lsst.pex.config.callStack.StackFrame`).
526 r"""Rename the field in a `~lsst.pex.config.Config` (for internal use
531 instance : `lsst.pex.config.Config`
532 The config instance that contains this field.
536 This method is invoked by the `lsst.pex.config.Config` object that
537 contains this field and should not be called directly.
539 Renaming is only relevant for `~lsst.pex.config.Field` instances that
540 hold subconfigs. `~lsst.pex.config.Field`\s that hold subconfigs should
541 rename each subconfig with the full field name as generated by
542 `lsst.pex.config.config._joinNamePath`.
547 """Validate the field (for internal use only).
551 instance : `lsst.pex.config.Config`
552 The config instance that contains this field.
556 lsst.pex.config.FieldValidationError
557 Raised if verification fails.
561 This method provides basic validation:
563 - Ensures that the value is not `None` if the field is not optional.
564 - Ensures type correctness.
565 - Ensures that the user-provided ``check`` function is valid.
567 Most `~lsst.pex.config.Field` subclasses should call
568 `lsst.pex.config.Field.validate` if they re-implement
569 `~lsst.pex.config.Field.validate`.
571 value = self.__get__(instance)
572 if not self.optional
and value
is None:
573 raise FieldValidationError(self, instance,
"Required value cannot be None")
576 """Make this field read-only (for internal use only).
580 instance : `lsst.pex.config.Config`
581 The config instance that contains this field.
585 Freezing is only relevant for fields that hold subconfigs. Fields which
586 hold subconfigs should freeze each subconfig.
588 **Subclasses should implement this method.**
598 The value being validated.
603 Raised if the value's type is incompatible with the field's
606 Raised if the value is rejected by the ``check`` method.
611 if not isinstance(value, self.dtype):
613 f
"Value {value} is of incorrect type {_typeStr(value)}. Expected type {_typeStr(self.dtype)}"
616 if self.check
is not None and not self.check(value):
617 msg = f
"Value {value} is not a valid value"
618 raise ValueError(msg)
621 """Call the _collectImports method on all config
622 objects the field may own, and union them with the supplied imports
627 instance : instance or subclass of `lsst.pex.config.Config`
628 A config object that has this field defined on it
630 Set of python modules that need imported after persistence
634 def save(self, outfile, instance):
635 """Save this field to a file (for internal use only).
639 outfile : file-like object
640 A writeable field handle.
641 instance : `~lsst.pex.config.Config`
642 The `~lsst.pex.config.Config` instance that contains this field.
646 This method is invoked by the `~lsst.pex.config.Config` object that
647 contains this field and should not be called directly.
649 The output consists of the documentation string
650 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second
651 line is formatted as an assignment: ``{fullname}={value}``.
653 This output can be executed with Python.
655 value = self.__get__(instance)
656 fullname = _joinNamePath(instance._name, self.name)
658 if self.deprecated
and value == self.default:
663 doc =
"# " + str(self.doc).replace(
"\n",
"\n# ")
664 if isinstance(value, float)
and not math.isfinite(value):
666 outfile.write(f
"{doc}\n{fullname}=float('{value!r}')\n\n")
668 outfile.write(f
"{doc}\n{fullname}={value!r}\n\n")
671 """Convert the field value so that it can be set as the value of an
672 item in a `dict` (for internal use only).
676 instance : `~lsst.pex.config.Config`
677 The `~lsst.pex.config.Config` that contains this field.
682 The field's value. See *Notes*.
686 This method invoked by the owning `~lsst.pex.config.Config` object and
687 should not be called directly.
689 Simple values are passed through. Complex data structures must be
690 manipulated. For example, a `~lsst.pex.config.Field` holding a
691 subconfig should, instead of the subconfig object, return a `dict`
692 where the keys are the field names in the subconfig, and the values are
693 the field values in the subconfig.
699 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
700 ) -> Field[FieldTypeVar]: ...
704 self, instance: Config, owner: Any =
None, at: Any =
None, label: str =
"default"
705 ) -> FieldTypeVar: ...
707 def __get__(self, instance, owner=None, at=None, label="default"):
708 """Define how attribute access should occur on the Config instance
709 This is invoked by the owning config object and should not be called
712 When the field attribute is accessed on a Config class object, it
713 returns the field object itself in order to allow inspection of
716 When the field attribute is access on a config instance, the actual
717 value described by the field (and held by the Config instance) is
725 return instance._storage[self.
namename]
726 except AttributeError:
727 if not isinstance(instance, Config):
730 raise AttributeError(
731 f
"Config {instance} is missing _storage attribute, likely incorrectly initialized"
735 self, instance: Config, value: FieldTypeVar |
None, at: Any =
None, label: str =
"assignment"
737 """Set an attribute on the config instance.
741 instance : `lsst.pex.config.Config`
742 The config instance that contains this field.
744 Value to set on this field.
745 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`,\
747 The call stack (created by
748 `lsst.pex.config.callStack.getCallStack`).
749 label : `str`, optional
750 Event label for the history.
754 This method is invoked by the owning `lsst.pex.config.Config` object
755 and should not be called directly.
757 Derived `~lsst.pex.config.Field` classes may need to override the
758 behavior. When overriding ``__set__``, `~lsst.pex.config.Field` authors
759 should follow the following rules:
761 - Do not allow modification of frozen configs.
762 - Validate the new value **before** modifying the field. Except if the
763 new value is `None`. `None` is special and no attempt should be made
764 to validate it until `lsst.pex.config.Config.validate` is called.
765 - Do not modify the `~lsst.pex.config.Config` instance to contain
767 - If the field is modified, update the history of the
768 `lsst.pex.config.field.Field` to reflect the changes.
770 In order to decrease the need to implement this method in derived
771 `~lsst.pex.config.Field` types, value validation is performed in the
772 `lsst.pex.config.Field._validateValue`. If only the validation step
773 differs in the derived `~lsst.pex.config.Field`, it is simpler to
774 implement `lsst.pex.config.Field._validateValue` than to reimplement
775 ``__set__``. More complicated behavior, however, may require
781 history = instance._history.setdefault(self.
namename, [])
782 if value
is not None:
783 value = _autocast(value, self.
dtype)
786 except BaseException
as e:
789 instance._storage[self.
namename] = value
792 history.append((value, at, label))
795 """Delete an attribute from a `lsst.pex.config.Config` instance.
799 instance : `lsst.pex.config.Config`
800 The config instance that contains this field.
801 at : `list` of `lsst.pex.config.callStack.StackFrame`
802 The call stack (created by
803 `lsst.pex.config.callStack.getCallStack`).
804 label : `str`, optional
805 Event label for the history.
809 This is invoked by the owning `~lsst.pex.config.Config` object and
810 should not be called directly.
814 self.
__set__(instance,
None, at=at, label=label)
816 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
817 """Compare a field (named `Field.name`) in two
818 `~lsst.pex.config.Config` instances for equality.
822 instance1 : `lsst.pex.config.Config`
823 Left-hand side `Config` instance to compare.
824 instance2 : `lsst.pex.config.Config`
825 Right-hand side `Config` instance to compare.
826 shortcut : `bool`, optional
828 rtol : `float`, optional
829 Relative tolerance for floating point comparisons.
830 atol : `float`, optional
831 Absolute tolerance for floating point comparisons.
832 output : callable, optional
833 A callable that takes a string, used (possibly repeatedly) to
838 This method must be overridden by more complex `Field` subclasses.
842 lsst.pex.config.compareScalars
844 v1 = getattr(instance1, self.
namename)
845 v2 = getattr(instance2, self.
namename)
846 name = getComparisonName(
847 _joinNamePath(instance1._name, self.
namename), _joinNamePath(instance2._name, self.
namename)
849 return compareScalars(name, v1, v2, dtype=self.
dtype, rtol=rtol, atol=atol, output=output)
853 """Importer (for `sys.meta_path`) that records which modules are being
856 *This class does not do any importing itself.*
860 Use this class as a context manager to ensure it is properly uninstalled
863 >>> with RecordingImporter() as importer:
865 ... import numpy as np
866 ... print("Imported: " + importer.getModules())
874 sys.meta_path = [self] + sys.meta_path
882 """Uninstall the importer."""
888 Called as part of the ``import`` chain of events.
894 path : `list` [`str`]
896 target : `~typing.Any`, optional
904 """Get the set of modules that were imported.
908 modules : `set` of `str`
909 Set of imported module names.
916 """Base class for configuration (*config*) objects.
920 A ``Config`` object will usually have several `~lsst.pex.config.Field`
921 instances as class attributes. These are used to define most of the base
924 ``Config`` implements a mapping API that provides many `dict`-like methods,
925 such as `keys`, `values`, and `items`. ``Config`` instances also support
926 the ``in`` operator to test if a field is in the config. Unlike a `dict`,
927 ``Config`` classes are not subscriptable. Instead, access individual
928 fields as attributes of the configuration instance.
932 Config classes are subclasses of ``Config`` that have
933 `~lsst.pex.config.Field` instances (or instances of
934 `~lsst.pex.config.Field` subclasses) as class attributes:
936 >>> from lsst.pex.config import Config, Field, ListField
937 >>> class DemoConfig(Config):
938 ... intField = Field(doc="An integer field", dtype=int, default=42)
939 ... listField = ListField(doc="List of favorite beverages.", dtype=str,
940 ... default=['coffee', 'green tea', 'water'])
942 >>> config = DemoConfig()
944 Configs support many `dict`-like APIs:
947 ['intField', 'listField']
948 >>> 'intField' in config
951 Individual fields can be accessed as attributes of the configuration:
955 >>> config.listField.append('earl grey tea')
956 >>> print(config.listField)
957 ['coffee', 'green tea', 'water', 'earl grey tea']
960 _storage: dict[str, Any]
961 _fields: dict[str, Field]
962 _history: dict[str, list[Any]]
966 """Iterate over fields."""
974 names : `~collections.abc.KeysView`
975 List of `lsst.pex.config.Field` names.
984 values : `~collections.abc.ValuesView`
985 Iterator of field values.
990 """Get configurations as ``(field name, field value)`` pairs.
994 items : `~collections.abc.ItemsView`
995 Iterator of tuples for each configuration. Tuple items are:
1003 """Return `True` if the specified field exists in this config.
1008 Field name to test for.
1013 `True` if the specified field exists in the config.
1018 """Allocate a new `lsst.pex.config.Config` object.
1020 In order to ensure that all Config object are always in a proper state
1021 when handed to users or to derived `~lsst.pex.config.Config` classes,
1022 some attributes are handled at allocation time rather than at
1025 This ensures that even if a derived `~lsst.pex.config.Config` class
1026 implements ``__init__``, its author does not need to be concerned about
1027 when or even the base ``Config.__init__`` should be called.
1029 name = kw.pop(
"__name",
None)
1030 at = kw.pop(
"__at", getCallStack())
1032 kw.pop(
"__label",
"default")
1034 instance = object.__new__(cls)
1035 instance._frozen =
False
1036 instance._name = name
1037 instance._storage = {}
1038 instance._history = {}
1039 instance._imports = set()
1041 for field
in instance._fields.values():
1042 instance._history[field.name] = []
1043 field.__set__(instance, field.default, at=at + [field.source], label=
"default")
1045 instance.setDefaults()
1047 instance.update(__at=at, **kw)
1051 """Reduction for pickling (function with arguments to reproduce).
1053 We need to condense and reconstitute the `~lsst.pex.config.Config`,
1054 since it may contain lambdas (as the ``check`` elements) that cannot
1059 stream = io.StringIO()
1061 return (unreduceConfig, (self.
__class__, stream.getvalue().encode()))
1064 """Subclass hook for computing defaults.
1068 Derived `~lsst.pex.config.Config` classes that must compute defaults
1069 rather than using the `~lsst.pex.config.Field` instances's defaults
1070 should do so here. To correctly use inherited defaults,
1071 implementations of ``setDefaults`` must call their base class's
1077 """Update values of fields specified by the keyword arguments.
1082 Keywords are configuration field names. Values are configuration
1087 The ``__at`` and ``__label`` keyword arguments are special internal
1088 keywords. They are used to strip out any internal steps from the
1089 history tracebacks of the config. Do not modify these keywords to
1090 subvert a `~lsst.pex.config.Config` instance's history.
1094 This is a config with three fields:
1096 >>> from lsst.pex.config import Config, Field
1097 >>> class DemoConfig(Config):
1098 ... fieldA = Field(doc='Field A', dtype=int, default=42)
1099 ... fieldB = Field(doc='Field B', dtype=bool, default=True)
1100 ... fieldC = Field(doc='Field C', dtype=str, default='Hello world')
1102 >>> config = DemoConfig()
1104 These are the default values of each field:
1106 >>> for name, value in config.iteritems():
1107 ... print(f"{name}: {value}")
1111 fieldC: 'Hello world'
1113 Using this method to update ``fieldA`` and ``fieldC``:
1115 >>> config.update(fieldA=13, fieldC='Updated!')
1117 Now the values of each field are:
1119 >>> for name, value in config.iteritems():
1120 ... print(f"{name}: {value}")
1126 at = kw.pop(
"__at", getCallStack())
1127 label = kw.pop(
"__label",
"update")
1129 for name, value
in kw.items():
1131 field = self._fields[name]
1132 field.__set__(self, value, at=at, label=label)
1134 raise KeyError(f
"No field of name {name} exists in config type {_typeStr(self)}")
1136 def load(self, filename, root="config"):
1137 """Modify this config in place by executing the Python code in a
1143 Name of the configuration file. A configuration file is Python
1145 root : `str`, optional
1146 Name of the variable in file that refers to the config being
1149 For example, the value of root is ``"config"`` and the file
1154 Then this config's field ``myField`` is set to ``5``.
1158 lsst.pex.config.Config.loadFromStream
1159 lsst.pex.config.Config.loadFromString
1160 lsst.pex.config.Config.save
1161 lsst.pex.config.Config.saveToStream
1162 lsst.pex.config.Config.saveToString
1164 with open(filename)
as f:
1165 code = compile(f.read(), filename=filename, mode=
"exec")
1169 """Modify this Config in place by executing the Python code in the
1174 stream : file-like object, `str`, `bytes`, or `~types.CodeType`
1175 Stream containing configuration override code. If this is a
1176 code object, it should be compiled with ``mode="exec"``.
1177 root : `str`, optional
1178 Name of the variable in file that refers to the config being
1181 For example, the value of root is ``"config"`` and the file
1186 Then this config's field ``myField`` is set to ``5``.
1187 filename : `str`, optional
1188 Name of the configuration file, or `None` if unknown or contained
1189 in the stream. Used for error reporting.
1190 extraLocals : `dict` of `str` to `object`, optional
1191 Any extra variables to include in local scope when loading.
1195 For backwards compatibility reasons, this method accepts strings, bytes
1196 and code objects as well as file-like objects. New code should use
1197 `loadFromString` instead for most of these types.
1201 lsst.pex.config.Config.load
1202 lsst.pex.config.Config.loadFromString
1203 lsst.pex.config.Config.save
1204 lsst.pex.config.Config.saveToStream
1205 lsst.pex.config.Config.saveToString
1207 if hasattr(stream,
"read"):
1208 if filename
is None:
1209 filename = getattr(stream,
"name",
"?")
1210 code = compile(stream.read(), filename=filename, mode=
"exec")
1213 self.
loadFromString(code, root=root, filename=filename, extraLocals=extraLocals)
1216 """Modify this Config in place by executing the Python code in the
1221 code : `str`, `bytes`, or `~types.CodeType`
1222 Stream containing configuration override code.
1223 root : `str`, optional
1224 Name of the variable in file that refers to the config being
1227 For example, the value of root is ``"config"`` and the file
1232 Then this config's field ``myField`` is set to ``5``.
1233 filename : `str`, optional
1234 Name of the configuration file, or `None` if unknown or contained
1235 in the stream. Used for error reporting.
1236 extraLocals : `dict` of `str` to `object`, optional
1237 Any extra variables to include in local scope when loading.
1242 Raised if a key in extraLocals is the same value as the value of
1247 lsst.pex.config.Config.load
1248 lsst.pex.config.Config.loadFromStream
1249 lsst.pex.config.Config.save
1250 lsst.pex.config.Config.saveToStream
1251 lsst.pex.config.Config.saveToString
1253 if filename
is None:
1256 filename = getattr(code,
"co_filename",
"?")
1258 globals = {
"__file__": filename}
1259 local = {root: self}
1260 if extraLocals
is not None:
1262 if root
in extraLocals:
1264 f
"{root} is reserved and cannot be used as a variable name in extraLocals"
1266 local.update(extraLocals)
1267 exec(code, globals, local)
1271 def save(self, filename, root="config"):
1272 """Save a Python script to the named file, which, when loaded,
1273 reproduces this config.
1278 Desination filename of this configuration.
1279 root : `str`, optional
1280 Name to use for the root config variable. The same value must be
1281 used when loading (see `lsst.pex.config.Config.load`).
1285 lsst.pex.config.Config.saveToStream
1286 lsst.pex.config.Config.saveToString
1287 lsst.pex.config.Config.load
1288 lsst.pex.config.Config.loadFromStream
1289 lsst.pex.config.Config.loadFromString
1291 d = os.path.dirname(filename)
1292 with tempfile.NamedTemporaryFile(mode=
"w", delete=
False, dir=d)
as outfile:
1297 umask = os.umask(0o077)
1299 os.chmod(outfile.name, (~umask & 0o666))
1303 shutil.move(outfile.name, filename)
1306 """Return the Python script form of this configuration as an executable
1311 skipImports : `bool`, optional
1312 If `True` then do not include ``import`` statements in output,
1313 this is to support human-oriented output from ``pipetask`` where
1314 additional clutter is not useful.
1319 A code string readable by `loadFromString`.
1323 lsst.pex.config.Config.save
1324 lsst.pex.config.Config.saveToStream
1325 lsst.pex.config.Config.load
1326 lsst.pex.config.Config.loadFromStream
1327 lsst.pex.config.Config.loadFromString
1329 buffer = io.StringIO()
1331 return buffer.getvalue()
1334 """Save a configuration file to a stream, which, when loaded,
1335 reproduces this config.
1339 outfile : file-like object
1340 Destination file object write the config into. Accepts strings not
1342 root : `str`, optional
1343 Name to use for the root config variable. The same value must be
1344 used when loading (see `lsst.pex.config.Config.load`).
1345 skipImports : `bool`, optional
1346 If `True` then do not include ``import`` statements in output,
1347 this is to support human-oriented output from ``pipetask`` where
1348 additional clutter is not useful.
1352 lsst.pex.config.Config.save
1353 lsst.pex.config.Config.saveToString
1354 lsst.pex.config.Config.load
1355 lsst.pex.config.Config.loadFromStream
1356 lsst.pex.config.Config.loadFromString
1365 configType = type(self)
1366 typeString = _typeStr(configType)
1367 outfile.write(f
"import {configType.__module__}\n")
1372 f
'assert type({root}) is {typeString}, f"config is of type '
1373 f
'{{type({root}).__module__}}.{{type({root}).__name__}} instead of {typeString}"\n\n'
1376 if imp
in sys.modules
and sys.modules[imp]
is not None:
1377 outfile.write(f
"import {imp}\n")
1383 """Make this config, and all subconfigs, read-only."""
1389 """Save this config to an open stream object.
1393 outfile : file-like object
1394 Destination file object write the config into. Accepts strings not
1398 field.save(outfile, self)
1401 """Add module containing self to the list of things to import and
1402 then loops over all the fields in the config calling a corresponding
1405 The field method will call _collectImports on any
1406 configs it may own and return the set of things to import. This
1407 returned set will be merged with the set of imports for this config
1415 """Make a dictionary of field names and their values.
1420 Dictionary with keys that are `~lsst.pex.config.Field` names.
1421 Values are `~lsst.pex.config.Field` values.
1425 lsst.pex.config.Field.toDict
1429 This method uses the `~lsst.pex.config.Field.toDict` method of
1430 individual fields. Subclasses of `~lsst.pex.config.Field` may need to
1431 implement a ``toDict`` method for *this* method to work.
1435 dict_[name] = field.toDict(self)
1439 """Get all the field names in the config, recursively.
1443 names : `list` of `str`
1450 with io.StringIO()
as strFd:
1452 contents = strFd.getvalue()
1458 for line
in contents.split(
"\n"):
1459 if re.search(
r"^((assert|import)\s+|\s*$|#)", line):
1462 mat = re.search(
r"^(?:config\.)?([^=]+)\s*=\s*.*", line)
1464 keys.append(mat.group(1))
1469 """Rename this config object in its parent `~lsst.pex.config.Config`.
1474 New name for this config in its parent `~lsst.pex.config.Config`.
1478 This method uses the `~lsst.pex.config.Field.rename` method of
1479 individual `lsst.pex.config.Field` instances.
1480 `lsst.pex.config.Field` subclasses may need to implement a ``rename``
1481 method for *this* method to work.
1485 lsst.pex.config.Field.rename
1492 """Validate the Config, raising an exception if invalid.
1496 lsst.pex.config.FieldValidationError
1497 Raised if verification fails.
1501 The base class implementation performs type checks on all fields by
1502 calling their `~lsst.pex.config.Field.validate` methods.
1504 Complex single-field validation can be defined by deriving new Field
1505 types. For convenience, some derived `lsst.pex.config.Field`-types
1506 (`~lsst.pex.config.ConfigField` and
1507 `~lsst.pex.config.ConfigChoiceField`) are defined in
1508 ``lsst.pex.config`` that handle recursing into subconfigs.
1510 Inter-field relationships should only be checked in derived
1511 `~lsst.pex.config.Config` classes after calling this method, and base
1512 validation is complete.
1515 field.validate(self)
1518 """Format a configuration field's history to a human-readable format.
1523 Name of a `~lsst.pex.config.Field` in this config.
1525 Keyword arguments passed to `lsst.pex.config.history.format`.
1530 A string containing the formatted history.
1534 lsst.pex.config.history.format
1538 return pexHist.format(self, name, **kwargs)
1540 history = property(
lambda x: x._history)
1541 """Read-only history.
1545 """Set an attribute (such as a field's value).
1549 Unlike normal Python objects, `~lsst.pex.config.Config` objects are
1550 locked such that no additional attributes nor properties may be added
1551 to them dynamically.
1553 Although this is not the standard Python behavior, it helps to protect
1554 users from accidentally mispelling a field name, or trying to set a
1561 f
"Config field {fullname} is deprecated: {self._fields[attr].deprecated}",
1568 self.
_fields_fields[attr].__set__(self, value, at=at, label=label)
1569 elif hasattr(getattr(self.
__class__, attr,
None),
"__set__"):
1571 return object.__setattr__(self, attr, value)
1572 elif attr
in self.__dict__
or attr
in (
"_name",
"_history",
"_storage",
"_frozen",
"_imports"):
1574 self.__dict__[attr] = value
1577 raise AttributeError(f
"{_typeStr(self)} has no attribute {attr}")
1585 object.__delattr__(self, attr)
1588 if type(other)
is type(self):
1590 thisValue = getattr(self, name)
1591 otherValue = getattr(other, name)
1592 if isinstance(thisValue, float)
and math.isnan(thisValue):
1593 if not math.isnan(otherValue):
1595 elif thisValue != otherValue:
1601 return not self.
__eq__(other)
1604 return str(self.
toDict())
1607 return "{}({})".format(
1609 ", ".join(f
"{k}={v!r}" for k, v
in self.
toDict().
items()
if v
is not None),
1612 def compare(self, other, shortcut=True, rtol=1e-8, atol=1e-8, output=None):
1613 """Compare this configuration to another `~lsst.pex.config.Config` for
1618 other : `lsst.pex.config.Config`
1619 Other `~lsst.pex.config.Config` object to compare against this
1621 shortcut : `bool`, optional
1622 If `True`, return as soon as an inequality is found. Default is
1624 rtol : `float`, optional
1625 Relative tolerance for floating point comparisons.
1626 atol : `float`, optional
1627 Absolute tolerance for floating point comparisons.
1628 output : callable, optional
1629 A callable that takes a string, used (possibly repeatedly) to
1630 report inequalities.
1635 `True` when the two `lsst.pex.config.Config` instances are equal.
1636 `False` if there is an inequality.
1640 lsst.pex.config.compareConfigs
1644 Unselected targets of `~lsst.pex.config.RegistryField` fields and
1645 unselected choices of `~lsst.pex.config.ConfigChoiceField` fields
1646 are not considered by this method.
1648 Floating point comparisons are performed by `numpy.allclose`.
1650 name1 = self.
_name if self.
_name is not None else "config"
1651 name2 = other._name
if other._name
is not None else "config"
1652 name = getComparisonName(name1, name2)
1653 return compareConfigs(name, self, other, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
1657 """Run initialization for every subclass.
1659 Specifically registers the subclass with a YAML representer
1660 and YAML constructor (if pyyaml is available)
1667 yaml.add_representer(cls, _yaml_config_representer)
1671 """Instantiate a `Config`-subclass from serialized Python form.
1676 A serialized form of the Config as created by
1677 `Config.saveToStream`.
1682 Reconstructed `Config` instant.
1689 """Return the Config subclass required by this Config serialization.
1694 A serialized form of the Config as created by
1695 `Config.saveToStream`.
1700 The `Config` subclass associated with this config.
1712 matches = re.search(
r"^import ([\w.]+)\nassert type\(\S+\)(?:\s*==\s*| is )(.*?),", config_py)
1715 first_line, second_line, _ = config_py.split(
"\n", 2)
1717 f
"First two lines did not match expected form. Got:\n - {first_line}\n - {second_line}"
1720 module_name = matches.group(1)
1721 module = importlib.import_module(module_name)
1724 full_name = matches.group(2)
1727 if not full_name.startswith(module_name):
1728 raise ValueError(f
"Module name ({module_name}) inconsistent with full name ({full_name})")
1733 remainder = full_name[len(module_name) + 1 :]
1734 components = remainder.split(
".")
1736 for component
in components:
1737 pytype = getattr(pytype, component)
1742 """Create a `~lsst.pex.config.Config` from a stream.
1746 cls_ : `lsst.pex.config.Config`-type
1747 A `lsst.pex.config.Config` type (not an instance) that is instantiated
1748 with configurations in the ``stream``.
1749 stream : file-like object, `str`, or `~types.CodeType`
1750 Stream containing configuration override code.
1754 config : `lsst.pex.config.Config`
1759 lsst.pex.config.Config.loadFromStream
1762 config.loadFromStream(stream)
std::vector< SchemaItem< Flag > > * items
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)
__get__(self, instance, owner=None, at=None, label="default")
FieldTypeVar __get__(self, Config instance, Any owner=None, Any at=None, str label="default")
__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)
_yaml_config_representer(dumper, data)
_classFromPython(config_py)
_joinNamePath(prefix=None, name=None, index=None)
unreduceConfig(cls_, stream)