27from __future__
import annotations
33 "FieldValidationError",
34 "UnexpectedProxyUsageError",
49from typing
import Any, ForwardRef, Generic, TypeVar, cast, overload
52 from types
import GenericAlias
55 GenericAlias =
type(Mapping[int, int])
64from .callStack
import getCallStack, getStackFrame
65from .comparison
import compareConfigs, compareScalars, getComparisonName
68 YamlLoaders: tuple[Any, ...] = (yaml.Loader, yaml.FullLoader, yaml.SafeLoader, yaml.UnsafeLoader)
72 from yaml
import CLoader
74 YamlLoaders += (CLoader,)
83 """A Subclass of python's GenericAlias used in defining and instantiating
86 This class differs
from `types.GenericAlias`
in that it calls a method
87 named _parseTypingArgs defined on Fields. This method gives Field
and its
88 subclasses an opportunity to transform type parameters into
class key word
89 arguments. Code authors do
not need to implement any returns of this object
90 directly,
and instead only need implement _parseTypingArgs,
if a Field
91 subclass differs
from the base
class implementation.
93 This
class is intended
to be an implementation detail, returned
from a
94 Field
's `__class_getitem__` method.
97 def __call__(self, *args: Any, **kwds: Any) -> Any:
98 origin_kwargs = self._parseTypingArgs(self.__args__, kwds)
99 return super().
__call__(*args, **{**kwds, **origin_kwargs})
102FieldTypeVar = TypeVar(
"FieldTypeVar")
106 """Exception raised when a proxy class is used in a context that suggests
107 it should have already been converted to the thing it proxies.
111def _joinNamePath(prefix=None, name=None, index=None):
112 """Generate nested configuration names."""
113 if not prefix
and not name:
114 raise ValueError(
"Invalid name: cannot be None")
117 elif prefix
and name:
118 name = prefix +
"." + name
120 if index
is not None:
121 return f
"{name}[{index!r}]"
126def _autocast(x, dtype):
127 """Cast a value to a type, if appropriate.
134 Data type, such as `float`, `int`,
or `str`.
139 If appropriate, the returned value
is ``x`` cast to the given type
140 ``dtype``. If the cast cannot be performed the original value of
143 if dtype == float
and isinstance(x, int):
149 """Generate a fully-qualified type name.
154 Fully-qualified type name.
158 This function is used primarily
for writing config files to be executed
159 later upon
with the
'load' function.
161 if hasattr(x,
"__module__")
and hasattr(x,
"__name__"):
165 if xtype.__module__ ==
"builtins":
166 return xtype.__name__
168 return f
"{xtype.__module__}.{xtype.__name__}"
174 """Represent a Config object in a form suitable for YAML.
176 Stores the serialized stream as a scalar block string.
178 stream = io.StringIO()
179 data.saveToStream(stream)
180 config_py = stream.getvalue()
184 config_py = config_py.rstrip() +
"\n"
188 config_py = re.sub(
r"\s+$",
"\n", config_py, flags=re.MULTILINE)
191 return dumper.represent_scalar(
"lsst.pex.config.Config", config_py, style=
"|")
194 """Construct a config from YAML."""
195 config_py = loader.construct_scalar(node)
196 return Config._fromPython(config_py)
200 for loader
in YamlLoaders:
201 yaml.add_constructor(
"lsst.pex.config.Config", _yaml_config_constructor, Loader=loader)
205 """A metaclass for `lsst.pex.config.Config`.
210 class attributes as
a class attribute called ``_fields``,
and adds
211 the name of each field
as an instance variable of the field itself (so you
212 don
't have to pass the name of the field to the field constructor).
216 type.__init__(cls, name, bases, dict_)
220 def getFields(classtype):
222 bases =
list(classtype.__bases__)
225 fields.update(getFields(b))
227 for k, v
in classtype.__dict__.items():
228 if isinstance(v, Field):
232 fields = getFields(cls)
233 for k, v
in fields.items():
234 setattr(cls, k, copy.deepcopy(v))
237 if isinstance(value, Field):
240 type.__setattr__(cls, name, value)
244 """Raised when a ``~lsst.pex.config.Field`` is not valid in a
250 The field that was not valid.
252 The config containing the invalid field.
254 Text describing why the field was
not valid.
259 """Type of the `~lsst.pex.config.Field` that incurred the error.
263 """Name of the `~lsst.pex.config.Field` instance that incurred the
268 lsst.pex.config.Field.name
271 self.fullname = _joinNamePath(config._name, field.name)
272 """Fully-qualified name of the `~lsst.pex.config.Field` instance
276 self.history = config.history.setdefault(field.name, [])
277 """Full history of all changes to the `~lsst.pex.config.Field`
282 """File and line number of the `~lsst.pex.config.Field` definition.
287 "%s '%s' failed validation: %s\n"
288 "For more information see the Field definition at:\n%s"
289 " and the Config definition at:\n%s"
302 """A field in a `~lsst.pex.config.Config` that supports `int`, `float`,
303 `complex`, `bool`, and `str` data types.
308 A description of the field
for users.
309 dtype : type, optional
310 The field
's data type. ``Field`` only supports basic data types:
311 `int`, `float`, `complex`, `bool`, and `str`. See
312 `Field.supportedTypes`. Optional
if supplied
as a typing argument to
314 default : object, optional
315 The field
's default value.
316 check : callable, optional
317 A callable that is called
with the field
's value. This callable should
318 return `
False`
if the value
is invalid. More complex inter-field
319 validation can be written
as part of the
320 `lsst.pex.config.Config.validate` method.
321 optional : `bool`, optional
322 This sets whether the field
is considered optional,
and therefore
323 doesn
't need to be set by the user. When `False`,
324 `lsst.pex.config.Config.validate` fails if the field
's value is `None`.
325 deprecated : None or `str`, optional
326 A description of why this Field
is deprecated, including removal date.
327 If
not None, the string
is appended to the docstring
for this Field.
332 Raised when the ``dtype`` parameter
is not one of the supported types
333 (see `Field.supportedTypes`).
349 ``Field`` instances (including those of any subclass of ``Field``) are used
351 example, below). ``Field`` attributes work like the `property` attributes
352 of classes that implement custom setters
and getters. `Field` attributes
353 belong to the
class, but operate on the instance. Formally speaking,
354 `Field` attributes are `descriptors
355 <https://docs.python.org/3/howto/descriptor.html>`_.
357 When you access a `Field` attribute on a `Config` instance, you don
't
358 get the `Field` instance itself. Instead, you get the value of that field,
359 which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
361 type. See the example, below.
363 Fields can be annotated with a type similar to other python classes (python
364 specification `here <https://peps.python.org/pep-0484/
365 See the name field
in the Config example below
for an example of this.
366 Unlike most other uses
in python, this has an effect at type checking *
and*
367 runtime. If the type
is specified
with a
class annotation, it will be used
368 as the value of the ``dtype``
in the ``Field``
and there
is no need to
369 specify it
as an argument during instantiation.
371 There are Some notes on dtype through type annotation syntax. Type
372 annotation syntax supports supplying the argument
as a string of a type
373 name. i.e.
"float", but this cannot be used to resolve circular references.
374 Type annotation syntax can be used on an identifier
in addition to Class
375 assignment i.e. ``variable: Field[str] = Config.someField`` vs
376 ``someField = Field[str](doc=
"some doc"). However, this syntax
is only
377 useful
for annotating the type of the identifier (i.e. variable
in previous
378 example)
and does nothing
for assigning the dtype of the ``Field``.
383 Instances of ``Field`` should be used
as class attributes of
387 >>>
class Example(
Config):
388 ... myInt =
Field(
"An integer field.", int, default=0)
389 ... name = Field[str](doc=
"A string Field")
391 >>> print(config.myInt)
394 >>> print(config.myInt)
399 """Identifier (variable name) used to refer to a Field within a Config
403 supportedTypes = {str, bool, float, int, complex}
404 """Supported data types for field values (`set` of types).
409 params: tuple[type, ...] | tuple[str, ...], kwds: Mapping[str, Any]
410 ) -> Mapping[str, Any]:
411 """Parse type annotations into keyword constructor arguments.
413 This is a special private method that interprets type arguments (i.e.
414 Field[str]) into keyword arguments to be passed on to the constructor.
416 Subclasses of Field can implement this method to customize how they
417 handle turning type parameters into keyword arguments (see DictField
422 params : `tuple` of `type`
or `tuple` of str
423 Parameters passed to the type annotation. These will either be
424 types
or strings. Strings are to interpreted
as forward references
425 and will be treated
as such.
426 kwds : `MutableMapping`
with keys of `str`
and values of `Any`
427 These are the user supplied keywords that are to be passed to the
432 kwds : `MutableMapping`
with keys of `str`
and values of `Any`
433 The mapping of keywords that will be passed onto the constructor
434 of the Field. Should be filled
in with any information gleaned
435 from the input parameters.
440 Raised
if params
is of incorrect length.
441 Raised
if a forward reference could
not be resolved
442 Raised
if there
is a conflict between params
and values
in kwds
445 raise ValueError(
"Only single type parameters are supported")
446 unpackedParams = params[0]
447 if isinstance(unpackedParams, str):
448 _typ = ForwardRef(unpackedParams)
454 result = _typ._evaluate(globals(), locals(),
set())
457 result = _typ._evaluate(globals(), locals())
459 raise ValueError(
"Could not deduce type from input")
460 unpackedParams = cast(type, result)
461 if "dtype" in kwds
and kwds[
"dtype"] != unpackedParams:
462 raise ValueError(
"Conflicting definition for dtype")
463 elif "dtype" not in kwds:
464 kwds = {**kwds, **{
"dtype": unpackedParams}}
470 def __init__(self, doc, dtype=None, default=None, check=None, optional=False, deprecated=None):
473 "dtype must either be supplied as an argument or as a type argument to the class"
476 raise ValueError(
"Unsupported Field dtype %s" % _typeStr(dtype))
478 source = getStackFrame()
486 deprecated=deprecated,
489 def _setup(self, doc, dtype, default, check, optional, source, deprecated):
490 """Set attributes, usually during initialization."""
492 """Data type for the field.
496 raise ValueError(
"Docstring is empty.")
499 if deprecated
is not None:
500 doc = f
"{doc} Deprecated: {deprecated}"
502 """A description of the field (`str`).
506 """If not None, a description of why this field is deprecated (`str`).
509 self.__doc__ = f"{doc} (`{dtype.__name__}`"
510 if optional
or default
is not None:
511 self.
__doc__ += f
", default ``{default!r}``"
515 """Default value for this field.
519 """A user-defined function that validates the value of the field.
523 """Flag that determines if the field is required to be set (`bool`).
525 When `False`, `lsst.pex.config.Config.validate` will fail
if the
526 field
's value is `None`.
530 """The stack frame where this field is defined (`list` of
535 r"""Rename the field in a `~lsst.pex.config.Config` (for internal use
541 The config instance that contains this field.
546 contains this field
and should
not be called directly.
550 rename each subconfig
with the full field name
as generated by
551 `lsst.pex.config.config._joinNamePath`.
556 """Validate the field (for internal use only).
561 The config instance that contains this field.
566 Raised if verification fails.
570 This method provides basic validation:
572 - Ensures that the value
is not `
None`
if the field
is not optional.
573 - Ensures type correctness.
574 - Ensures that the user-provided ``check`` function
is valid.
577 `lsst.pex.config.Field.validate`
if they re-implement
578 `~lsst.pex.config.Field.validate`.
581 if not self.
optional and value
is None:
585 """Make this field read-only (for internal use only).
590 The config instance that contains this field.
594 Freezing is only relevant
for fields that hold subconfigs. Fields which
595 hold subconfigs should freeze each subconfig.
597 **Subclasses should implement this method.**
607 The value being validated.
612 Raised if the value
's type is incompatible with the field's
615 Raised
if the value
is rejected by the ``check`` method.
620 if not isinstance(value, self.
dtype):
621 msg =
"Value {} is of incorrect type {}. Expected type {}".format(
624 _typeStr(self.
dtype),
627 if self.
check is not None and not self.
check(value):
628 msg =
"Value %s is not a valid value" % str(value)
629 raise ValueError(msg)
632 """Call the _collectImports method on all config
633 objects the field may own, and union them
with the supplied imports
639 A config object that has this field defined on it
641 Set of python modules that need imported after persistence
645 def save(self, outfile, instance):
646 """Save this field to a file (for internal use only).
650 outfile : file-like object
651 A writeable field handle.
658 contains this field
and should
not be called directly.
660 The output consists of the documentation string
661 (`lsst.pex.config.Field.doc`) formatted
as a Python comment. The second
662 line
is formatted
as an assignment: ``{fullname}={value}``.
664 This output can be executed
with Python.
667 fullname = _joinNamePath(instance._name, self.namename)
674 doc =
"# " + str(self.
doc).replace(
"\n",
"\n# ")
675 if isinstance(value, float)
and not math.isfinite(value):
677 outfile.write(f
"{doc}\n{fullname}=float('{value!r}')\n\n")
679 outfile.write(f
"{doc}\n{fullname}={value!r}\n\n")
682 """Convert the field value so that it can be set as the value of an
683 item in a `dict` (
for internal use only).
693 The field
's value. See *Notes*.
698 should
not be called directly.
700 Simple values are passed through. Complex data structures must be
702 subconfig should, instead of the subconfig object,
return a `dict`
703 where the keys are the field names
in the subconfig,
and the values are
704 the field values
in the subconfig.
710 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
711 ) -> Field[FieldTypeVar]:
716 self, instance: Config, owner: Any =
None, at: Any =
None, label: str =
"default"
720 def __get__(self, instance, owner=None, at=None, label="default"):
721 """Define how attribute access should occur on the Config instance
722 This is invoked by the owning config object
and should
not be called
725 When the field attribute
is accessed on a Config
class object, it
726 returns the field object itself
in order to allow inspection of
729 When the field attribute
is access on a config instance, the actual
730 value described by the field (
and held by the Config instance)
is
738 return instance._storage[self.
namename]
739 except AttributeError:
740 if not isinstance(instance, Config):
743 raise AttributeError(
744 f
"Config {instance} is missing _storage attribute, likely incorrectly initialized"
748 self, instance: Config, value: FieldTypeVar |
None, at: Any =
None, label: str =
"assignment"
750 """Set an attribute on the config instance.
755 The config instance that contains this field.
757 Value to set on this field.
759 The call stack (created by
760 `lsst.pex.config.callStack.getCallStack`).
761 label : `str`, optional
762 Event label for the history.
767 and should
not be called directly.
771 should follow the following rules:
773 - Do
not allow modification of frozen configs.
774 - Validate the new value **before** modifying the field. Except
if the
775 new value
is `
None`. `
None`
is special
and no attempt should be made
776 to validate it until `lsst.pex.config.Config.validate`
is called.
779 - If the field
is modified, update the history of the
780 `lsst.pex.config.field.Field` to reflect the changes.
782 In order to decrease the need to implement this method
in derived
784 `lsst.pex.config.Field._validateValue`. If only the validation step
786 implement `lsst.pex.config.Field._validateValue` than to reimplement
787 ``__set__``. More complicated behavior, however, may require
793 history = instance._history.setdefault(self.
namename, [])
794 if value
is not None:
795 value = _autocast(value, self.
dtype)
798 except BaseException
as e:
801 instance._storage[self.
namename] = value
804 history.append((value, at, label))
807 """Delete an attribute from a `lsst.pex.config.Config` instance.
812 The config instance that contains this field.
814 The call stack (created by
815 `lsst.pex.config.callStack.getCallStack`).
816 label : `str`, optional
817 Event label for the history.
822 should
not be called directly.
826 self.
__set__(instance,
None, at=at, label=label)
828 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
829 """Compare a field (named `Field.name`) in two
835 Left-hand side `Config` instance to compare.
837 Right-hand side `Config` instance to compare.
838 shortcut : `bool`, optional
840 rtol : `float`, optional
841 Relative tolerance
for floating point comparisons.
842 atol : `float`, optional
843 Absolute tolerance
for floating point comparisons.
844 output : callable, optional
845 A callable that takes a string, used (possibly repeatedly) to
850 This method must be overridden by more complex `Field` subclasses.
854 lsst.pex.config.compareScalars
856 v1 = getattr(instance1, self.namename)
857 v2 = getattr(instance2, self.namename)
858 name = getComparisonName(
859 _joinNamePath(instance1._name, self.namename), _joinNamePath(instance2._name, self.namename)
861 return compareScalars(name, v1, v2, dtype=self.
dtype, rtol=rtol, atol=atol, output=output)
865 """Importer (for `sys.meta_path`) that records which modules are being
868 *This class does not do any importing itself.*
872 Use this
class as
a context manager
to ensure it is properly uninstalled
877 ...
import numpy
as np
878 ... print(
"Imported: " + importer.getModules())
886 sys.meta_path = [self] + sys.meta_path
894 """Uninstall the importer."""
900 Called as part of the ``
import`` chain of events.
907 """Get the set of modules that were imported.
911 modules : `set` of `str`
912 Set of imported module names.
919 """Base class for configuration (*config*) objects.
924 instances as class attributes. These are used to define most of the base
927 ``Config`` implements a mapping API that provides many `dict`-like methods,
928 such
as `keys`, `values`,
and `items`. ``Config`` instances also support
929 the ``
in`` operator to test
if a field
is in the config. Unlike a `dict`,
930 ``Config`` classes are
not subscriptable. Instead, access individual
931 fields
as attributes of the configuration instance.
935 Config classes are subclasses of ``Config`` that have
940 >>>
class DemoConfig(
Config):
941 ... intField =
Field(doc=
"An integer field", dtype=int, default=42)
942 ... listField =
ListField(doc=
"List of favorite beverages.", dtype=str,
943 ... default=[
'coffee',
'green tea',
'water'])
945 >>> config = DemoConfig()
947 Configs support many `dict`-like APIs:
950 [
'intField',
'listField']
951 >>>
'intField' in config
954 Individual fields can be accessed
as attributes of the configuration:
958 >>> config.listField.append(
'earl grey tea')
959 >>> print(config.listField)
960 [
'coffee',
'green tea',
'water',
'earl grey tea']
963 _storage: dict[str, Any]
964 _fields: dict[str, Field]
965 _history: dict[str, list[Any]]
969 """Iterate over fields."""
977 names : `~collections.abc.KeysView`
987 values : `~collections.abc.ValuesView`
988 Iterator of field values.
993 """Get configurations as ``(field name, field value)`` pairs.
997 items : `~collections.abc.ItemsView`
998 Iterator of tuples for each configuration. Tuple items are:
1006 """Return `True` if the specified field exists in this config.
1011 Field name to test for.
1016 `
True`
if the specified field exists
in the config.
1021 """Allocate a new `lsst.pex.config.Config` object.
1023 In order to ensure that all Config object are always in a proper state
1025 some attributes are handled at allocation time rather than at
1029 implements ``__init__``, its author does
not need to be concerned about
1030 when
or even the base ``Config.__init__`` should be called.
1032 name = kw.pop("__name",
None)
1033 at = kw.pop(
"__at", getCallStack())
1035 kw.pop(
"__label",
"default")
1037 instance = object.__new__(cls)
1038 instance._frozen =
False
1039 instance._name = name
1040 instance._storage = {}
1041 instance._history = {}
1042 instance._imports =
set()
1044 for field
in instance._fields.values():
1045 instance._history[field.name] = []
1046 field.__set__(instance, field.default, at=at + [field.source], label=
"default")
1048 instance.setDefaults()
1050 instance.update(__at=at, **kw)
1054 """Reduction for pickling (function with arguments to reproduce).
1057 since it may contain lambdas (
as the ``check`` elements) that cannot
1062 stream = io.StringIO()
1064 return (unreduceConfig, (self.
__class__, stream.getvalue().encode()))
1067 """Subclass hook for computing defaults.
1073 should do so here. To correctly use inherited defaults,
1074 implementations of ``setDefaults`` must call their base class's
1080 """Update values of fields specified by the keyword arguments.
1085 Keywords are configuration field names. Values are configuration
1090 The ``__at`` and ``__label`` keyword arguments are special internal
1091 keywords. They are used to strip out any internal steps
from the
1092 history tracebacks of the config. Do
not modify these keywords to
1097 This is a config
with three fields:
1100 >>>
class DemoConfig(
Config):
1101 ... fieldA =
Field(doc=
'Field A', dtype=int, default=42)
1102 ... fieldB =
Field(doc=
'Field B', dtype=bool, default=
True)
1103 ... fieldC =
Field(doc=
'Field C', dtype=str, default=
'Hello world')
1105 >>> config = DemoConfig()
1107 These are the default values of each field:
1109 >>>
for name, value
in config.iteritems():
1110 ... print(f
"{name}: {value}")
1114 fieldC:
'Hello world'
1116 Using this method to update ``fieldA``
and ``fieldC``:
1118 >>> config.update(fieldA=13, fieldC=
'Updated!')
1120 Now the values of each field are:
1122 >>>
for name, value
in config.iteritems():
1123 ... print(f
"{name}: {value}")
1129 at = kw.pop("__at", getCallStack())
1130 label = kw.pop(
"__label",
"update")
1132 for name, value
in kw.items():
1135 field.__set__(self, value, at=at, label=label)
1137 raise KeyError(f
"No field of name {name} exists in config type {_typeStr(self)}")
1139 def load(self, filename, root="config"):
1140 """Modify this config in place by executing the Python code in a
1146 Name of the configuration file. A configuration file is Python
1148 root : `str`, optional
1149 Name of the variable
in file that refers to the config being
1152 For example, the value of root
is ``
"config"``
and the file
1157 Then this config
's field ``myField`` is set to ``5``.
1161 lsst.pex.config.Config.loadFromStream
1162 lsst.pex.config.Config.loadFromString
1163 lsst.pex.config.Config.save
1164 lsst.pex.config.Config.saveToStream
1165 lsst.pex.config.Config.saveToString
1167 with open(filename)
as f:
1168 code = compile(f.read(), filename=filename, mode=
"exec")
1172 """Modify this Config in place by executing the Python code in the
1177 stream : file-like object, `str`, `bytes`, or compiled string
1178 Stream containing configuration override code. If this
is a
1179 code object, it should be compiled
with ``mode=
"exec"``.
1180 root : `str`, optional
1181 Name of the variable
in file that refers to the config being
1184 For example, the value of root
is ``
"config"``
and the file
1189 Then this config
's field ``myField`` is set to ``5``.
1190 filename : `str`, optional
1191 Name of the configuration file, or `
None`
if unknown
or contained
1192 in the stream. Used
for error reporting.
1193 extraLocals : `dict` of `str` to `object`, optional
1194 Any extra variables to include
in local scope when loading.
1198 For backwards compatibility reasons, this method accepts strings, bytes
1199 and code objects
as well
as file-like objects. New code should use
1200 `loadFromString` instead
for most of these types.
1204 lsst.pex.config.Config.load
1205 lsst.pex.config.Config.loadFromString
1206 lsst.pex.config.Config.save
1207 lsst.pex.config.Config.saveToStream
1208 lsst.pex.config.Config.saveToString
1210 if hasattr(stream,
"read"):
1211 if filename
is None:
1212 filename = getattr(stream,
"name",
"?")
1213 code = compile(stream.read(), filename=filename, mode=
"exec")
1216 self.
loadFromString(code, root=root, filename=filename, extraLocals=extraLocals)
1219 """Modify this Config in place by executing the Python code in the
1224 code : `str`, `bytes`, or compiled string
1225 Stream containing configuration override code.
1226 root : `str`, optional
1227 Name of the variable
in file that refers to the config being
1230 For example, the value of root
is ``
"config"``
and the file
1235 Then this config
's field ``myField`` is set to ``5``.
1236 filename : `str`, optional
1237 Name of the configuration file, or `
None`
if unknown
or contained
1238 in the stream. Used
for error reporting.
1239 extraLocals : `dict` of `str` to `object`, optional
1240 Any extra variables to include
in local scope when loading.
1245 Raised
if a key
in extraLocals
is the same value
as the value of
1250 lsst.pex.config.Config.load
1251 lsst.pex.config.Config.loadFromStream
1252 lsst.pex.config.Config.save
1253 lsst.pex.config.Config.saveToStream
1254 lsst.pex.config.Config.saveToString
1256 if filename
is None:
1259 filename = getattr(code,
"co_filename",
"?")
1261 globals = {
"__file__": filename}
1262 local = {root: self}
1263 if extraLocals
is not None:
1265 if root
in extraLocals:
1267 f
"{root} is reserved and cannot be used as a variable name in extraLocals"
1269 local.update(extraLocals)
1270 exec(code, globals, local)
1274 def save(self, filename, root="config"):
1275 """Save a Python script to the named file, which, when loaded,
1276 reproduces this config.
1281 Desination filename of this configuration.
1282 root : `str`, optional
1283 Name to use for the root config variable. The same value must be
1284 used when loading (see `lsst.pex.config.Config.load`).
1288 lsst.pex.config.Config.saveToStream
1289 lsst.pex.config.Config.saveToString
1290 lsst.pex.config.Config.load
1291 lsst.pex.config.Config.loadFromStream
1292 lsst.pex.config.Config.loadFromString
1294 d = os.path.dirname(filename)
1295 with tempfile.NamedTemporaryFile(mode=
"w", delete=
False, dir=d)
as outfile:
1300 umask = os.umask(0o077)
1302 os.chmod(outfile.name, (~umask & 0o666))
1306 shutil.move(outfile.name, filename)
1309 """Return the Python script form of this configuration as an executable
1314 skipImports : `bool`, optional
1315 If `True` then do
not include ``
import`` statements
in output,
1316 this
is to support human-oriented output
from ``pipetask`` where
1317 additional clutter
is not useful.
1322 A code string readable by `loadFromString`.
1326 lsst.pex.config.Config.save
1327 lsst.pex.config.Config.saveToStream
1328 lsst.pex.config.Config.load
1329 lsst.pex.config.Config.loadFromStream
1330 lsst.pex.config.Config.loadFromString
1332 buffer = io.StringIO()
1334 return buffer.getvalue()
1337 """Save a configuration file to a stream, which, when loaded,
1338 reproduces this config.
1342 outfile : file-like object
1343 Destination file object write the config into. Accepts strings not
1346 Name to use
for the root config variable. The same value must be
1347 used when loading (see `lsst.pex.config.Config.load`).
1348 skipImports : `bool`, optional
1349 If `
True` then do
not include ``
import`` statements
in output,
1350 this
is to support human-oriented output
from ``pipetask`` where
1351 additional clutter
is not useful.
1355 lsst.pex.config.Config.save
1356 lsst.pex.config.Config.saveToString
1357 lsst.pex.config.Config.load
1358 lsst.pex.config.Config.loadFromStream
1359 lsst.pex.config.Config.loadFromString
1368 configType =
type(self)
1369 typeString = _typeStr(configType)
1370 outfile.write(f
"import {configType.__module__}\n")
1372 f
"assert type({root})=={typeString}, 'config is of type %s.%s instead of "
1373 f
"{typeString}' % (type({root}).__module__, type({root}).__name__)\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.
1425 lsst.pex.config.Field.toDict
1429 This method uses the `~lsst.pex.config.Field.toDict` method of
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`.
1478 This method uses the `~lsst.pex.config.Field.rename` method of
1481 method
for *this* method to work.
1485 lsst.pex.config.Field.rename
1492 """Validate the Config, raising an exception if invalid.
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
1510 Inter-field relationships should only be checked
in derived
1512 validation
is complete.
1515 field.validate(self)
1518 """Format a configuration field's history to a human-readable format.
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).
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)
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
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.
1636 `
False`
if there
is an inequality.
1640 lsst.pex.config.compareConfigs
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.
1709 matches = re.search(
r"^import ([\w.]+)\nassert .*==(.*?),", config_py)
1712 first_line, second_line, _ = config_py.split(
"\n", 2)
1714 f
"First two lines did not match expected form. Got:\n - {first_line}\n - {second_line}"
1717 module_name = matches.group(1)
1718 module = importlib.import_module(module_name)
1721 full_name = matches.group(2)
1724 if not full_name.startswith(module_name):
1725 raise ValueError(f
"Module name ({module_name}) inconsistent with full name ({full_name})")
1730 remainder = full_name[len(module_name) + 1 :]
1731 components = remainder.split(
".")
1733 for component
in components:
1734 pytype = getattr(pytype, component)
1739 """Create a `~lsst.pex.config.Config` from a stream.
1745 with configurations
in the ``stream``.
1746 stream : file-like object, `str`,
or compiled string
1747 Stream containing configuration override code.
1756 lsst.pex.config.Config.loadFromStream
1759 config.loadFromStream(stream)
std::vector< SchemaItem< Flag > > * items
table::Key< std::string > object
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)
daf::base::PropertyList * list
daf::base::PropertySet * set
_yaml_config_representer(dumper, data)
_classFromPython(config_py)
unreduceConfig(cls, stream)