27from __future__
import annotations
33 "FieldValidationError",
34 "UnexpectedProxyUsageError",
48from typing
import Any, ForwardRef, Generic, Mapping, Optional, TypeVar, Union, cast, overload
51 from types
import GenericAlias
54 GenericAlias =
type(Mapping[int, int])
63from .callStack
import getCallStack, getStackFrame
64from .comparison
import compareConfigs, compareScalars, getComparisonName
67 YamlLoaders: tuple[Any, ...] = (yaml.Loader, yaml.FullLoader, yaml.SafeLoader, yaml.UnsafeLoader)
71 from yaml
import CLoader
73 YamlLoaders += (CLoader,)
81if int(sys.version_info.minor) < 9:
82 genericAliasKwds = {
"_root":
True}
88 """A Subclass of python's GenericAlias used in defining and instantiating
91 This class differs
from `types.GenericAlias`
in that it calls a method
92 named _parseTypingArgs defined on Fields. This method gives Field
and its
93 subclasses an opportunity to transform type parameters into
class key word
94 arguments. Code authors do
not need to implement any returns of this object
95 directly,
and instead only need implement _parseTypingArgs,
if a Field
96 subclass differs
from the base
class implementation.
98 This
class is intended
to be an implementation detail, returned
from a
99 Field
's `__class_getitem__` method.
102 def __call__(self, *args: Any, **kwds: Any) -> Any:
103 origin_kwargs = self._parseTypingArgs(self.__args__, kwds)
104 return super().
__call__(*args, **{**kwds, **origin_kwargs})
107FieldTypeVar = TypeVar(
"FieldTypeVar")
111 """Exception raised when a proxy class is used in a context that suggests
112 it should have already been converted to the thing it proxies.
116def _joinNamePath(prefix=None, name=None, index=None):
117 """Generate nested configuration names."""
118 if not prefix
and not name:
119 raise ValueError(
"Invalid name: cannot be None")
122 elif prefix
and name:
123 name = prefix +
"." + name
125 if index
is not None:
126 return "%s[%r]" % (name, index)
131def _autocast(x, dtype):
132 """Cast a value to a type, if appropriate.
139 Data type, such as `float`, `int`,
or `str`.
144 If appropriate, the returned value
is ``x`` cast to the given type
145 ``dtype``. If the cast cannot be performed the original value of
148 if dtype == float
and isinstance(x, int):
154 """Generate a fully-qualified type name.
159 Fully-qualified type name.
163 This function is used primarily
for writing config files to be executed
164 later upon
with the
'load' function.
166 if hasattr(x,
"__module__")
and hasattr(x,
"__name__"):
170 if (sys.version_info.major <= 2
and xtype.__module__ ==
"__builtin__")
or xtype.__module__ ==
"builtins":
171 return xtype.__name__
173 return "%s.%s" % (xtype.__module__, xtype.__name__)
178 def _yaml_config_representer(dumper, data):
179 """Represent a Config object in a form suitable for YAML.
181 Stores the serialized stream as a scalar block string.
183 stream = io.StringIO()
184 data.saveToStream(stream)
185 config_py = stream.getvalue()
189 config_py = config_py.rstrip() +
"\n"
193 config_py = re.sub(
r"\s+$",
"\n", config_py, flags=re.MULTILINE)
196 return dumper.represent_scalar(
"lsst.pex.config.Config", config_py, style=
"|")
198 def _yaml_config_constructor(loader, node):
199 """Construct a config from YAML"""
200 config_py = loader.construct_scalar(node)
201 return Config._fromPython(config_py)
205 for loader
in YamlLoaders:
206 yaml.add_constructor(
"lsst.pex.config.Config", _yaml_config_constructor, Loader=loader)
210 """A metaclass for `lsst.pex.config.Config`.
215 class attributes as
a class attribute called ``_fields``,
and adds
216 the name of each field
as an instance variable of the field itself (so you
217 don
't have to pass the name of the field to the field constructor).
221 type.__init__(cls, name, bases, dict_)
225 def getFields(classtype):
227 bases =
list(classtype.__bases__)
230 fields.update(getFields(b))
232 for k, v
in classtype.__dict__.items():
233 if isinstance(v, Field):
237 fields = getFields(cls)
238 for k, v
in fields.items():
239 setattr(cls, k, copy.deepcopy(v))
242 if isinstance(value, Field):
245 type.__setattr__(cls, name, value)
249 """Raised when a ``~lsst.pex.config.Field`` is not valid in a
255 The field that was not valid.
257 The config containing the invalid field.
259 Text describing why the field was
not valid.
264 """Type of the `~lsst.pex.config.Field` that incurred the error.
268 """Name of the `~lsst.pex.config.Field` instance that incurred the
273 lsst.pex.config.Field.name
276 self.fullname = _joinNamePath(config._name, field.name)
277 """Fully-qualified name of the `~lsst.pex.config.Field` instance
281 self.history = config.history.setdefault(field.name, [])
282 """Full history of all changes to the `~lsst.pex.config.Field`
287 """File and line number of the `~lsst.pex.config.Field` definition.
292 "%s '%s' failed validation: %s\n"
293 "For more information see the Field definition at:\n%s"
294 " and the Config definition at:\n%s"
307 """A field in a `~lsst.pex.config.Config` that supports `int`, `float`,
308 `complex`, `bool`, and `str` data types.
313 A description of the field
for users.
314 dtype : type, optional
315 The field
's data type. ``Field`` only supports basic data types:
316 `int`, `float`, `complex`, `bool`, and `str`. See
317 `Field.supportedTypes`. Optional
if supplied
as a typing argument to
319 default : object, optional
320 The field
's default value.
321 check : callable, optional
322 A callable that is called
with the field
's value. This callable should
323 return `
False`
if the value
is invalid. More complex inter-field
324 validation can be written
as part of the
325 `lsst.pex.config.Config.validate` method.
326 optional : `bool`, optional
327 This sets whether the field
is considered optional,
and therefore
328 doesn
't need to be set by the user. When `False`,
329 `lsst.pex.config.Config.validate` fails if the field
's value is `None`.
330 deprecated : None or `str`, optional
331 A description of why this Field
is deprecated, including removal date.
332 If
not None, the string
is appended to the docstring
for this Field.
337 Raised when the ``dtype`` parameter
is not one of the supported types
338 (see `Field.supportedTypes`).
354 ``Field`` instances (including those of any subclass of ``Field``) are used
356 example, below). ``Field`` attributes work like the `property` attributes
357 of classes that implement custom setters
and getters. `Field` attributes
358 belong to the
class, but operate on the instance. Formally speaking,
359 `Field` attributes are `descriptors
360 <https://docs.python.org/3/howto/descriptor.html>`_.
362 When you access a `Field` attribute on a `Config` instance, you don
't
363 get the `Field` instance itself. Instead, you get the value of that field,
364 which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
366 type. See the example, below.
368 Fields can be annotated with a type similar to other python classes (python
369 specification `here <https://peps.python.org/pep-0484/
370 See the name field
in the Config example below
for an example of this.
371 Unlike most other uses
in python, this has an effect at type checking *
and*
372 runtime. If the type
is specified
with a
class annotation, it will be used
373 as the value of the ``dtype``
in the ``Field``
and there
is no need to
374 specify it
as an argument during instantiation.
376 There are Some notes on dtype through type annotation syntax. Type
377 annotation syntax supports supplying the argument
as a string of a type
378 name. i.e.
"float", but this cannot be used to resolve circular references.
379 Type annotation syntax can be used on an identifier
in addition to Class
380 assignment i.e. ``variable: Field[str] = Config.someField`` vs
381 ``someField = Field[str](doc=
"some doc"). However, this syntax
is only
382 useful
for annotating the type of the identifier (i.e. variable
in previous
383 example)
and does nothing
for assigning the dtype of the ``Field``.
388 Instances of ``Field`` should be used
as class attributes of
392 >>>
class Example(
Config):
393 ... myInt =
Field(
"An integer field.", int, default=0)
394 ... name = Field[str](doc=
"A string Field")
396 >>> print(config.myInt)
399 >>> print(config.myInt)
404 """Identifier (variable name) used to refer to a Field within a Config
408 supportedTypes = set((str, bool, float, int, complex))
409 """Supported data types for field values (`set` of types).
413 def _parseTypingArgs(
414 params: Union[tuple[type, ...], tuple[str, ...]], kwds: Mapping[str, Any]
415 ) -> Mapping[str, Any]:
416 """Parses type annotations into keyword constructor arguments.
418 This is a special private method that interprets type arguments (i.e.
419 Field[str]) into keyword arguments to be passed on to the constructor.
421 Subclasses of Field can implement this method to customize how they
422 handle turning type parameters into keyword arguments (see DictField
427 params : `tuple` of `type`
or `tuple` of str
428 Parameters passed to the type annotation. These will either be
429 types
or strings. Strings are to interpreted
as forward references
430 and will be treated
as such.
431 kwds : `MutableMapping`
with keys of `str`
and values of `Any`
432 These are the user supplied keywords that are to be passed to the
437 kwds : `MutableMapping`
with keys of `str`
and values of `Any`
438 The mapping of keywords that will be passed onto the constructor
439 of the Field. Should be filled
in with any information gleaned
440 from the input parameters.
445 Raised
if params
is of incorrect length.
446 Raised
if a forward reference could
not be resolved
447 Raised
if there
is a conflict between params
and values
in kwds
450 raise ValueError(
"Only single type parameters are supported")
451 unpackedParams = params[0]
452 if isinstance(unpackedParams, str):
453 _typ = ForwardRef(unpackedParams)
459 result = _typ._evaluate(globals(), locals(),
set())
462 result = _typ._evaluate(globals(), locals())
464 raise ValueError(
"Could not deduce type from input")
465 unpackedParams = cast(type, result)
466 if "dtype" in kwds
and kwds[
"dtype"] != unpackedParams:
467 raise ValueError(
"Conflicting definition for dtype")
468 elif "dtype" not in kwds:
469 kwds = {**kwds, **{
"dtype": unpackedParams}}
475 def __init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None):
478 "dtype must either be supplied as an argument or as a type argument to the class"
481 raise ValueError(
"Unsupported Field dtype %s" % _typeStr(dtype))
483 source = getStackFrame()
491 deprecated=deprecated,
494 def _setup(self, doc, dtype, default, check, optional, source, deprecated):
495 """Set attributes, usually during initialization."""
497 """Data type for the field.
501 if deprecated
is not None:
502 doc = f
"{doc} Deprecated: {deprecated}"
504 """A description of the field (`str`).
508 """If not None, a description of why this field is deprecated (`str`).
511 self.__doc__ = f"{doc} (`{dtype.__name__}`"
512 if optional
or default
is not None:
513 self.
__doc__ += f
", default ``{default!r}``"
517 """Default value for this field.
521 """A user-defined function that validates the value of the field.
525 """Flag that determines if the field is required to be set (`bool`).
527 When `False`, `lsst.pex.config.Config.validate` will fail
if the
528 field
's value is `None`.
532 """The stack frame where this field is defined (`list` of
537 """Rename the field in a `~lsst.pex.config.Config` (for internal use
543 The config instance that contains this field.
548 contains this field
and should
not be called directly.
551 hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should
552 rename each subconfig
with the full field name
as generated by
553 `lsst.pex.config.config._joinNamePath`.
558 """Validate the field (for internal use only).
563 The config instance that contains this field.
568 Raised if verification fails.
572 This method provides basic validation:
574 - Ensures that the value
is not `
None`
if the field
is not optional.
575 - Ensures type correctness.
576 - Ensures that the user-provided ``check`` function
is valid.
579 `lsst.pex.config.field.Field.validate`
if they re-implement
580 `~lsst.pex.config.field.Field.validate`.
583 if not self.
optional and value
is None:
587 """Make this field read-only (for internal use only).
592 The config instance that contains this field.
596 Freezing is only relevant
for fields that hold subconfigs. Fields which
597 hold subconfigs should freeze each subconfig.
599 **Subclasses should implement this method.**
603 def _validateValue(self, value):
609 The value being validated.
614 Raised if the value
's type is incompatible with the field's
617 Raised
if the value
is rejected by the ``check`` method.
622 if not isinstance(value, self.
dtype):
623 msg =
"Value %s is of incorrect type %s. Expected type %s" % (
626 _typeStr(self.
dtype),
629 if self.
check is not None and not self.
check(value):
630 msg =
"Value %s is not a valid value" %
str(value)
631 raise ValueError(msg)
633 def _collectImports(self, instance, imports):
634 """This function should call the _collectImports method on all config
635 objects the field may own, and union them
with the supplied imports
641 A config object that has this field defined on it
643 Set of python modules that need imported after persistence
647 def save(self, outfile, instance):
648 """Save this field to a file (for internal use only).
652 outfile : file-like object
653 A writeable field handle.
655 The `Config` instance that contains this field.
660 contains this field
and should
not be called directly.
662 The output consists of the documentation string
663 (`lsst.pex.config.Field.doc`) formatted
as a Python comment. The second
664 line
is formatted
as an assignment: ``{fullname}={value}``.
666 This output can be executed
with Python.
669 fullname = _joinNamePath(instance._name, self.name)
676 doc =
"# " +
str(self.
doc).replace(
"\n",
"\n# ")
677 if isinstance(value, float)
and not math.isfinite(value):
679 outfile.write(
"{}\n{}=float('{!r}')\n\n".format(doc, fullname, value))
681 outfile.write(
"{}\n{}={!r}\n\n".format(doc, fullname, value))
684 """Convert the field value so that it can be set as the value of an
685 item in a `dict` (
for internal use only).
690 The `Config` that contains this field.
695 The field
's value. See *Notes*.
700 should
not be called directly.
702 Simple values are passed through. Complex data structures must be
704 subconfig should, instead of the subconfig object,
return a `dict`
705 where the keys are the field names
in the subconfig,
and the values are
706 the field values
in the subconfig.
712 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
713 ) ->
"Field[FieldTypeVar]":
718 self, instance:
"Config", owner: Any =
None, at: Any =
None, label: str =
"default"
722 def __get__(self, instance, owner=None, at=None, label="default"):
723 """Define how attribute access should occur on the Config instance
724 This is invoked by the owning config object
and should
not be called
727 When the field attribute
is accessed on a Config
class object, it
728 returns the field object itself
in order to allow inspection of
731 When the field attribute
is access on a config instance, the actual
732 value described by the field (
and held by the Config instance)
is
740 return instance._storage[self.name]
741 except AttributeError:
742 if not isinstance(instance, Config):
745 raise AttributeError(
746 f
"Config {instance} is missing "
747 "_storage attribute, likely"
748 " incorrectly initialized"
752 self, instance:
"Config", value: Optional[FieldTypeVar], at: Any =
None, label: str =
"assignment"
754 """Set an attribute on the config instance.
759 The config instance that contains this field.
761 Value to set on this field.
763 The call stack (created by
764 `lsst.pex.config.callStack.getCallStack`).
765 label : `str`, optional
766 Event label for the history.
771 and should
not be called directly.
775 should follow the following rules:
777 - Do
not allow modification of frozen configs.
778 - Validate the new value **before** modifying the field. Except
if the
779 new value
is `
None`. `
None`
is special
and no attempt should be made
780 to validate it until `lsst.pex.config.Config.validate`
is called.
783 - If the field
is modified, update the history of the
784 `lsst.pex.config.field.Field` to reflect the changes.
786 In order to decrease the need to implement this method
in derived
788 `lsst.pex.config.Field._validateValue`. If only the validation step
790 implement `lsst.pex.config.Field._validateValue` than to reimplement
791 ``__set__``. More complicated behavior, however, may require
797 history = instance._history.setdefault(self.name, [])
798 if value
is not None:
799 value = _autocast(value, self.
dtype)
802 except BaseException
as e:
805 instance._storage[self.name] = value
808 history.append((value, at, label))
811 """Delete an attribute from a `lsst.pex.config.Config` instance.
816 The config instance that contains this field.
818 The call stack (created by
819 `lsst.pex.config.callStack.getCallStack`).
820 label : `str`, optional
821 Event label for the history.
826 should
not be called directly.
830 self.
__set__(instance,
None, at=at, label=label)
832 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
833 """Compare a field (named `Field.name`) in two
839 Left-hand side `Config` instance to compare.
841 Right-hand side `Config` instance to compare.
842 shortcut : `bool`, optional
844 rtol : `float`, optional
845 Relative tolerance
for floating point comparisons.
846 atol : `float`, optional
847 Absolute tolerance
for floating point comparisons.
848 output : callable, optional
849 A callable that takes a string, used (possibly repeatedly) to
854 This method must be overridden by more complex `Field` subclasses.
858 lsst.pex.config.compareScalars
860 v1 = getattr(instance1, self.name)
861 v2 = getattr(instance2, self.name)
862 name = getComparisonName(
863 _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name)
865 return compareScalars(name, v1, v2, dtype=self.
dtype, rtol=rtol, atol=atol, output=output)
869 """Importer (for `sys.meta_path`) that records which modules are being
872 *This class does not do any importing itself.*
876 Use this
class as
a context manager
to ensure it is properly uninstalled
881 ...
import numpy
as np
882 ... print(
"Imported: " + importer.getModules())
890 sys.meta_path = [self] + sys.meta_path
898 """Uninstall the importer."""
902 """Called as part of the ``import`` chain of events."""
908 """Get the set of modules that were imported.
912 modules : `set` of `str`
913 Set of imported module names.
920 """Base class for configuration (*config*) objects.
925 instances as class attributes. These are used to define most of the base
928 ``Config`` implements a mapping API that provides many `dict`-like methods,
929 such
as `keys`, `values`, `items`, `iteritems`, `iterkeys`,
and
930 `itervalues`. ``Config`` instances also support the ``
in`` operator to
931 test
if a field
is in the config. Unlike a `dict`, ``Config`` classes are
932 not subscriptable. Instead, access individual fields
as attributes of the
933 configuration instance.
937 Config classes are subclasses of ``Config`` that have
942 >>>
class DemoConfig(
Config):
943 ... intField =
Field(doc=
"An integer field", dtype=int, default=42)
944 ... listField =
ListField(doc=
"List of favorite beverages.", dtype=str,
945 ... default=[
'coffee',
'green tea',
'water'])
947 >>> config = DemoConfig()
949 Configs support many `dict`-like APIs:
952 [
'intField',
'listField']
953 >>>
'intField' in config
956 Individual fields can be accessed
as attributes of the configuration:
960 >>> config.listField.append(
'earl grey tea')
961 >>> print(config.listField)
962 [
'coffee',
'green tea',
'water',
'earl grey tea']
965 _storage: dict[str, Any]
966 _fields: dict[str, Field]
967 _history: dict[str, list[Any]]
971 """Iterate over fields."""
984 lsst.pex.config.Config.iterkeys
986 return self._storage.
keys()
993 values : `dict_values`
994 Iterator of field values.
996 return self._storage.
values()
999 """Get configurations as ``(field name, field value)`` pairs.
1003 items : `dict_items`
1004 Iterator of tuples for each configuration. Tuple items are:
1009 return self._storage.
items()
1012 """!Return True if the specified field exists in this config
1014 @param[
in] name field name to test
for
1019 """Allocate a new `lsst.pex.config.Config` object.
1021 In order to ensure that all Config object are always in a proper state
1023 some attributes are handled at allocation time rather than at
1027 implements ``__init__``, its author does
not need to be concerned about
1028 when
or even the base ``Config.__init__`` should be called.
1030 name = kw.pop("__name",
None)
1031 at = kw.pop(
"__at", getCallStack())
1033 kw.pop(
"__label",
"default")
1035 instance = object.__new__(cls)
1036 instance._frozen =
False
1037 instance._name = name
1038 instance._storage = {}
1039 instance._history = {}
1040 instance._imports =
set()
1042 for field
in instance._fields.values():
1043 instance._history[field.name] = []
1044 field.__set__(instance, field.default, at=at + [field.source], label=
"default")
1046 instance.setDefaults()
1048 instance.update(__at=at, **kw)
1052 """Reduction for pickling (function with arguments to reproduce).
1055 since it may contain lambdas (
as the ``check`` elements) that cannot
1060 stream = io.StringIO()
1062 return (unreduceConfig, (self.__class__, stream.getvalue().encode()))
1065 """Subclass hook for computing defaults.
1071 should do so here. To correctly use inherited defaults,
1072 implementations of ``setDefaults`` must call their base class's
1078 """Update values of fields specified by the keyword arguments.
1083 Keywords are configuration field names. Values are configuration
1088 The ``__at`` and ``__label`` keyword arguments are special internal
1089 keywords. They are used to strip out any internal steps
from the
1090 history tracebacks of the config. Do
not modify these keywords to
1095 This is a config
with three fields:
1098 >>>
class DemoConfig(
Config):
1099 ... fieldA =
Field(doc=
'Field A', dtype=int, default=42)
1100 ... fieldB =
Field(doc=
'Field B', dtype=bool, default=
True)
1101 ... fieldC =
Field(doc=
'Field C', dtype=str, default=
'Hello world')
1103 >>> config = DemoConfig()
1105 These are the default values of each field:
1107 >>>
for name, value
in config.iteritems():
1108 ... print(f
"{name}: {value}")
1112 fieldC:
'Hello world'
1114 Using this method to update ``fieldA``
and ``fieldC``:
1116 >>> config.update(fieldA=13, fieldC=
'Updated!')
1118 Now the values of each field are:
1120 >>>
for name, value
in config.iteritems():
1121 ... print(f
"{name}: {value}")
1127 at = kw.pop("__at", getCallStack())
1128 label = kw.pop(
"__label",
"update")
1130 for name, value
in kw.items():
1133 field.__set__(self, value, at=at, label=label)
1135 raise KeyError(
"No field of name %s exists in config type %s" % (name, _typeStr(self)))
1137 def load(self, filename, root="config"):
1138 """Modify this config in place by executing the Python code in a
1144 Name of the configuration file. A configuration file is Python
1146 root : `str`, optional
1147 Name of the variable
in file that refers to the config being
1150 For example, the value of root
is ``
"config"``
and the file
1155 Then this config
's field ``myField`` is set to ``5``.
1159 lsst.pex.config.Config.loadFromStream
1160 lsst.pex.config.Config.loadFromString
1161 lsst.pex.config.Config.save
1162 lsst.pex.config.Config.saveToStream
1163 lsst.pex.config.Config.saveToString
1165 with open(filename,
"r")
as f:
1166 code = compile(f.read(), filename=filename, mode=
"exec")
1170 """Modify this Config in place by executing the Python code in the
1175 stream : file-like object, `str`, `bytes`, or compiled string
1176 Stream containing configuration override code. If this
is a
1177 code object, it should be compiled
with ``mode=
"exec"``.
1178 root : `str`, optional
1179 Name of the variable
in file that refers to the config being
1182 For example, the value of root
is ``
"config"``
and the file
1187 Then this config
's field ``myField`` is set to ``5``.
1188 filename : `str`, optional
1189 Name of the configuration file, or `
None`
if unknown
or contained
1190 in the stream. Used
for error reporting.
1194 For backwards compatibility reasons, this method accepts strings, bytes
1195 and code objects
as well
as file-like objects. New code should use
1196 `loadFromString` instead
for most of these types.
1200 lsst.pex.config.Config.load
1201 lsst.pex.config.Config.loadFromString
1202 lsst.pex.config.Config.save
1203 lsst.pex.config.Config.saveToStream
1204 lsst.pex.config.Config.saveToString
1206 if hasattr(stream,
"read"):
1207 if filename
is None:
1208 filename = getattr(stream,
"name",
"?")
1209 code = compile(stream.read(), filename=filename, mode=
"exec")
1215 """Modify this Config in place by executing the Python code in the
1220 code : `str`, `bytes`, or compiled string
1221 Stream containing configuration override code.
1222 root : `str`, optional
1223 Name of the variable
in file that refers to the config being
1226 For example, the value of root
is ``
"config"``
and the file
1231 Then this config
's field ``myField`` is set to ``5``.
1232 filename : `str`, optional
1233 Name of the configuration file, or `
None`
if unknown
or contained
1234 in the stream. Used
for error reporting.
1238 lsst.pex.config.Config.load
1239 lsst.pex.config.Config.loadFromStream
1240 lsst.pex.config.Config.save
1241 lsst.pex.config.Config.saveToStream
1242 lsst.pex.config.Config.saveToString
1244 if filename
is None:
1247 filename = getattr(code,
"co_filename",
"?")
1249 globals = {
"__file__": filename}
1250 local = {root: self}
1251 exec(code, globals, local)
1253 self._imports.
update(importer.getModules())
1255 def save(self, filename, root="config"):
1256 """Save a Python script to the named file, which, when loaded,
1257 reproduces this config.
1262 Desination filename of this configuration.
1263 root : `str`, optional
1264 Name to use for the root config variable. The same value must be
1265 used when loading (see `lsst.pex.config.Config.load`).
1269 lsst.pex.config.Config.saveToStream
1270 lsst.pex.config.Config.saveToString
1271 lsst.pex.config.Config.load
1272 lsst.pex.config.Config.loadFromStream
1273 lsst.pex.config.Config.loadFromString
1275 d = os.path.dirname(filename)
1276 with tempfile.NamedTemporaryFile(mode=
"w", delete=
False, dir=d)
as outfile:
1281 umask = os.umask(0o077)
1283 os.chmod(outfile.name, (~umask & 0o666))
1287 shutil.move(outfile.name, filename)
1290 """Return the Python script form of this configuration as an executable
1295 skipImports : `bool`, optional
1296 If `True` then do
not include ``
import`` statements
in output,
1297 this
is to support human-oriented output
from ``pipetask`` where
1298 additional clutter
is not useful.
1303 A code string readable by `loadFromString`.
1307 lsst.pex.config.Config.save
1308 lsst.pex.config.Config.saveToStream
1309 lsst.pex.config.Config.load
1310 lsst.pex.config.Config.loadFromStream
1311 lsst.pex.config.Config.loadFromString
1313 buffer = io.StringIO()
1315 return buffer.getvalue()
1318 """Save a configuration file to a stream, which, when loaded,
1319 reproduces this config.
1323 outfile : file-like object
1324 Destination file object write the config into. Accepts strings not
1327 Name to use
for the root config variable. The same value must be
1328 used when loading (see `lsst.pex.config.Config.load`).
1329 skipImports : `bool`, optional
1330 If `
True` then do
not include ``
import`` statements
in output,
1331 this
is to support human-oriented output
from ``pipetask`` where
1332 additional clutter
is not useful.
1336 lsst.pex.config.Config.save
1337 lsst.pex.config.Config.saveToString
1338 lsst.pex.config.Config.load
1339 lsst.pex.config.Config.loadFromStream
1340 lsst.pex.config.Config.loadFromString
1348 self._imports.remove(self.__module__)
1349 configType =
type(self)
1350 typeString = _typeStr(configType)
1351 outfile.write(f
"import {configType.__module__}\n")
1353 f
"assert type({root})=={typeString}, 'config is of type %s.%s instead of "
1354 f
"{typeString}' % (type({root}).__module__, type({root}).__name__)\n"
1356 for imp
in sorted(self._imports):
1357 if imp
in sys.modules
and sys.modules[imp]
is not None:
1358 outfile.write(
"import {}\n".format(imp))
1364 """Make this config, and all subconfigs, read-only."""
1369 def _save(self, outfile):
1370 """Save this config to an open stream object.
1374 outfile : file-like object
1375 Destination file object write the config into. Accepts strings not
1379 field.save(outfile, self)
1381 def _collectImports(self):
1382 """Adds module containing self to the list of things to import and
1383 then loops over all the fields in the config calling a corresponding
1384 collect method. The field method will call _collectImports on any
1385 configs it may own
and return the set of things to
import. This
1386 returned set will be merged
with the set of imports
for this config
1389 self._imports.add(self.__module__)
1391 field._collectImports(self, self._imports)
1394 """Make a dictionary of field names and their values.
1404 lsst.pex.config.Field.toDict
1408 This method uses the `~lsst.pex.config.Field.toDict` method of
1410 implement a ``toDict`` method
for *this* method to work.
1414 dict_[name] = field.toDict(self)
1418 """Get all the field names in the config, recursively.
1422 names : `list` of `str`
1429 with io.StringIO()
as strFd:
1431 contents = strFd.getvalue()
1437 for line
in contents.split(
"\n"):
1438 if re.search(
r"^((assert|import)\s+|\s*$|#)", line):
1441 mat = re.search(
r"^(?:config\.)?([^=]+)\s*=\s*.*", line)
1443 keys.append(mat.group(1))
1447 def _rename(self, name):
1448 """Rename this config object in its parent `~lsst.pex.config.Config`.
1457 This method uses the `~lsst.pex.config.Field.rename` method of
1460 method
for *this* method to work.
1464 lsst.pex.config.Field.rename
1471 """Validate the Config, raising an exception if invalid.
1476 Raised if verification fails.
1480 The base
class implementation performs
type checks on all fields by
1481 calling their `~lsst.pex.config.Field.validate` methods.
1483 Complex single-field validation can be defined by deriving new Field
1487 that handle recursing into subconfigs.
1489 Inter-field relationships should only be checked
in derived
1491 validation
is complete.
1494 field.validate(self)
1497 """Format a configuration field's history to a human-readable format.
1504 Keyword arguments passed to `lsst.pex.config.history.format`.
1509 A string containing the formatted history.
1513 lsst.pex.config.history.format
1517 return pexHist.format(self, name, **kwargs)
1519 history = property(
lambda x: x._history)
1520 """Read-only history.
1524 """Set an attribute (such as a field's value).
1529 locked such that no additional attributes nor properties may be added
1530 to them dynamically.
1532 Although this is not the standard Python behavior, it helps to protect
1533 users
from accidentally mispelling a field name,
or trying to set a
1537 if self.
_fields[attr].deprecated
is not None:
1538 fullname = _joinNamePath(self.
_name, self.
_fields[attr].name)
1540 f
"Config field {fullname} is deprecated: {self._fields[attr].deprecated}",
1547 self.
_fields[attr].__set__(self, value, at=at, label=label)
1548 elif hasattr(getattr(self.__class__, attr,
None),
"__set__"):
1550 return object.__setattr__(self, attr, value)
1551 elif attr
in self.__dict__
or attr
in (
"_name",
"_history",
"_storage",
"_frozen",
"_imports"):
1553 self.__dict__[attr] = value
1556 raise AttributeError(
"%s has no attribute %s" % (_typeStr(self), attr))
1562 self.
_fields[attr].__delete__(self, at=at, label=label)
1564 object.__delattr__(self, attr)
1569 thisValue = getattr(self, name)
1570 otherValue = getattr(other, name)
1571 if isinstance(thisValue, float)
and math.isnan(thisValue):
1572 if not math.isnan(otherValue):
1574 elif thisValue != otherValue:
1580 return not self.
__eq__(other)
1588 ", ".join(
"%s=%r" % (k, v)
for k, v
in self.
toDict().
items()
if v
is not None),
1591 def compare(self, other, shortcut=True, rtol=1e-8, atol=1e-8, output=None):
1592 """Compare this configuration to another `~lsst.pex.config.Config` for
1600 shortcut : `bool`, optional
1601 If `True`,
return as soon
as an inequality
is found. Default
is
1603 rtol : `float`, optional
1604 Relative tolerance
for floating point comparisons.
1605 atol : `float`, optional
1606 Absolute tolerance
for floating point comparisons.
1607 output : callable, optional
1608 A callable that takes a string, used (possibly repeatedly) to
1609 report inequalities.
1615 `
False`
if there
is an inequality.
1619 lsst.pex.config.compareConfigs
1625 are
not considered by this method.
1627 Floating point comparisons are performed by `numpy.allclose`.
1629 name1 = self._name if self.
_name is not None else "config"
1630 name2 = other._name
if other._name
is not None else "config"
1631 name = getComparisonName(name1, name2)
1632 return compareConfigs(name, self, other, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
1636 """Run initialization for every subclass.
1638 Specifically registers the subclass with a YAML representer
1639 and YAML constructor (
if pyyaml
is available)
1646 yaml.add_representer(cls, _yaml_config_representer)
1649 def _fromPython(cls, config_py):
1650 """Instantiate a `Config`-subclass from serialized Python form.
1655 A serialized form of the Config as created by
1656 `Config.saveToStream`.
1661 Reconstructed `Config` instant.
1663 cls = _classFromPython(config_py)
1667def _classFromPython(config_py):
1668 """Return the Config subclass required by this Config serialization.
1673 A serialized form of the Config as created by
1674 `Config.saveToStream`.
1679 The `Config` subclass associated
with this config.
1688 matches = re.search(
r"^import ([\w.]+)\nassert .*==(.*?),", config_py)
1691 first_line, second_line, _ = config_py.split(
"\n", 2)
1693 "First two lines did not match expected form. Got:\n" f
" - {first_line}\n" f
" - {second_line}"
1696 module_name = matches.group(1)
1697 module = importlib.import_module(module_name)
1700 full_name = matches.group(2)
1703 if not full_name.startswith(module_name):
1704 raise ValueError(f
"Module name ({module_name}) inconsistent with full name ({full_name})")
1709 remainder = full_name[len(module_name) + 1 :]
1710 components = remainder.split(
".")
1712 for component
in components:
1713 pytype = getattr(pytype, component)
1718 """Create a `~lsst.pex.config.Config` from a stream.
1724 with configurations
in the ``stream``.
1725 stream : file-like object, `str`,
or compiled string
1726 Stream containing configuration override code.
1735 lsst.pex.config.Config.loadFromStream
1738 config.loadFromStream(stream)
std::vector< SchemaItem< Flag > > * items
Any __call__(self, *Any args, **Any kwds)
def __init_subclass__(cls, **kwargs)
def compare(self, other, shortcut=True, rtol=1e-8, atol=1e-8, output=None)
def __delattr__(self, attr, at=None, label="deletion")
def __new__(cls, *args, **kw)
def loadFromStream(self, stream, root="config", filename=None)
def saveToStream(self, outfile, root="config", skipImports=False)
def saveToString(self, skipImports=False)
def __contains__(self, name)
Return True if the specified field exists in this config.
def load(self, filename, root="config")
def __setattr__(self, attr, value, at=None, label="assignment")
def formatHistory(self, name, **kwargs)
def _collectImports(self)
def loadFromString(self, code, root="config", filename=None)
def save(self, filename, root="config")
"Field[FieldTypeVar]" __get__(self, None instance, Any owner=None, Any at=None, str label="default")
def __class_getitem__(cls, Union[tuple[type,...], type, ForwardRef] params)
def _validateValue(self, value)
None __set__(self, "Config" instance, Optional[FieldTypeVar] value, Any at=None, str label="assignment")
def rename(self, instance)
def __get__(self, instance, owner=None, at=None, label="default")
def freeze(self, instance)
FieldTypeVar __get__(self, "Config" instance, Any owner=None, Any at=None, str label="default")
def save(self, outfile, instance)
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
def toDict(self, instance)
def __delete__(self, instance, at=None, label="deletion")
def __init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None)
def __init__(self, field, config, msg)
def __exit__(self, *args)
def find_module(self, fullname, path=None)
daf::base::PropertyList * list
daf::base::PropertySet * set
def unreduceConfig(cls, stream)