25 from .config
import Config, Field, _joinNamePath, _typeStr, FieldValidationError
26 from .comparison
import compareConfigs, getComparisonName
27 from .callStack
import getCallStack, getStackFrame
31 """A retargetable configuration for a configurable object. 33 The actual `~lsst.pex.config.Config` instance is accessed using the 34 ``value`` property (e.g. to get its documentation). The associated 35 configurable object (usually a `~lsst.pipe.base.Task`) is accessed 36 using the ``target`` property. 39 def __initValue(self, at, label):
41 if field.default is an instance of ConfigClass, custom construct 42 _value with the correct values from default. 43 otherwise call ConfigClass constructor 45 name = _joinNamePath(self._config._name, self._field.name)
47 storage = self._field.default._storage
50 value = self._ConfigClass(__name=name, __at=at, __label=label, **storage)
51 object.__setattr__(self,
"_value", value)
53 def __init__(self, config, field, at=None, label="default"):
54 object.__setattr__(self,
"_config", config)
55 object.__setattr__(self,
"_field", field)
56 object.__setattr__(self,
"__doc__", config)
57 object.__setattr__(self,
"_target", field.target)
58 object.__setattr__(self,
"_ConfigClass", field.ConfigClass)
59 object.__setattr__(self,
"_value",
None)
63 at += [self._field.source]
66 history = config._history.setdefault(field.name, [])
67 history.append((
"Targeted and initialized from defaults", at, label))
70 Read-only access to the targeted configurable 72 target = property(
lambda x: x._target)
74 Read-only access to the ConfigClass 76 ConfigClass = property(
lambda x: x._ConfigClass)
79 Read-only access to the ConfigClass instance 81 value = property(
lambda x: x._value)
85 Call the configurable. 86 With argument config=self.value along with any positional and kw args 90 def retarget(self, target, ConfigClass=None, at=None, label="retarget"):
91 """Target a new configurable and ConfigClass 93 if self._config._frozen:
97 ConfigClass = self._field.validateTarget(target, ConfigClass)
98 except BaseException
as e:
103 object.__setattr__(self,
"_target", target)
105 object.__setattr__(self,
"_ConfigClass", ConfigClass)
108 history = self._config._history.setdefault(self._field.name, [])
109 msg =
"retarget(target=%s, ConfigClass=%s)" % (_typeStr(target), _typeStr(ConfigClass))
110 history.append((msg, at, label))
113 return getattr(self._value, name)
117 Pretend to be an isntance of ConfigClass. 118 Attributes defined by ConfigurableInstance will shadow those defined in ConfigClass 120 if self._config._frozen:
123 if name
in self.__dict__:
125 object.__setattr__(self, name, value)
129 self._value.
__setattr__(name, value, at=at, label=label)
133 Pretend to be an isntance of ConfigClass. 134 Attributes defiend by ConfigurableInstance will shadow those defined in ConfigClass 136 if self._config._frozen:
141 object.__delattr__(self, name)
142 except AttributeError:
150 A variant of a ConfigField which has a known configurable target 152 Behaves just like a ConfigField except that it can be 'retargeted' to point 153 at a different configurable. Further you can 'apply' to construct a fully 154 configured configurable. 160 if ConfigClass
is None:
162 ConfigClass = target.ConfigClass
164 raise AttributeError(
"'target' must define attribute 'ConfigClass'")
165 if not issubclass(ConfigClass, Config):
166 raise TypeError(
"'ConfigClass' is of incorrect type %s." 167 "'ConfigClass' must be a subclass of Config" % _typeStr(ConfigClass))
168 if not hasattr(target,
'__call__'):
169 raise ValueError(
"'target' must be callable")
170 if not hasattr(target,
'__module__')
or not hasattr(target,
'__name__'):
171 raise ValueError(
"'target' must be statically defined" 172 "(must have '__module__' and '__name__' attributes)")
175 def __init__(self, doc, target, ConfigClass=None, default=None, check=None):
177 @param target is the configurable target. Must be callable, and the first 178 parameter will be the value of this field 179 @param ConfigClass is the class of Config object expected by the target. 180 If not provided by target.ConfigClass it must be provided explicitly in this argument 185 default = ConfigClass
186 if default != ConfigClass
and type(default) != ConfigClass:
187 raise TypeError(
"'default' is of incorrect type %s. Expected %s" %
188 (_typeStr(default), _typeStr(ConfigClass)))
191 self.
_setup(doc=doc, dtype=ConfigurableInstance, default=default,
192 check=check, optional=
False, source=source)
196 def __getOrMake(self, instance, at=None, label="default"):
197 value = instance._storage.get(self.name,
None)
202 instance._storage[self.name] = value
205 def __get__(self, instance, owner=None, at=None, label="default"):
206 if instance
is None or not isinstance(instance, Config):
209 return self.
__getOrMake(instance, at=at, label=label)
211 def __set__(self, instance, value, at=None, label="assignment"):
218 if isinstance(value, ConfigurableInstance):
219 oldValue.retarget(value.target, value.ConfigClass, at, label)
220 oldValue.update(__at=at, __label=label, **value._storage)
221 elif type(value) == oldValue._ConfigClass:
222 oldValue.update(__at=at, __label=label, **value._storage)
223 elif value == oldValue.ConfigClass:
224 value = oldValue.ConfigClass()
225 oldValue.update(__at=at, __label=label, **value._storage)
227 msg =
"Value %s is of incorrect type %s. Expected %s" % \
228 (value, _typeStr(value), _typeStr(oldValue.ConfigClass))
232 fullname = _joinNamePath(instance._name, self.name)
234 value._rename(fullname)
236 def save(self, outfile, instance):
237 fullname = _joinNamePath(instance._name, self.name)
239 target = value.target
244 ConfigClass = value.ConfigClass
245 for module
in set([target.__module__, ConfigClass.__module__]):
246 outfile.write(
u"import {}\n".
format(module))
247 outfile.write(
u"{}.retarget(target={}, ConfigClass={})\n\n".
format(fullname,
249 _typeStr(ConfigClass)))
259 return value.toDict()
265 if self.
check is not None and not self.
check(value):
266 msg =
"%s is not a valid value" % str(value)
270 """Customize deep-copying, because we always want a reference to the original typemap. 272 WARNING: this must be overridden by subclasses if they change the constructor signature! 275 default=copy.deepcopy(self.
default))
277 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
278 """Helper function for Config.compare; used to compare two fields for equality. 280 @param[in] instance1 LHS Config instance to compare. 281 @param[in] instance2 RHS Config instance to compare. 282 @param[in] shortcut If True, return as soon as an inequality is found. 283 @param[in] rtol Relative tolerance for floating point comparisons. 284 @param[in] atol Absolute tolerance for floating point comparisons. 285 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly) 286 to report inequalities. 288 Floating point comparisons are performed by numpy.allclose; refer to that for details. 290 c1 = getattr(instance1, self.name)._value
291 c2 = getattr(instance2, self.name)._value
293 _joinNamePath(instance1._name, self.name),
294 _joinNamePath(instance2._name, self.name)
296 return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
def save(self, outfile, instance)
def __setattr__(self, name, value, at=None, label="assignment")
def freeze(self, instance)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def __init__(self, doc, target, ConfigClass=None, default=None, check=None)
def __initValue(self, at, label)
def __get__(self, instance, owner=None, at=None, label="default")
def _setup(self, doc, dtype, default, check, optional, source)
def validateTarget(self, target, ConfigClass)
def getStackFrame(relative=0)
def toDict(self, instance)
def validate(self, instance)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def retarget(self, target, ConfigClass=None, at=None, label="retarget")
def __deepcopy__(self, memo)
def rename(self, instance)
def __init__(self, config, field, at=None, label="default")
def __getOrMake(self, instance, at=None, label="default")
def __delattr__(self, name, at=None, label="delete")
def apply(self, args, kw)
def __get__(self, instance, owner=None, at=None, label="default")
def __set__(self, instance, value, at=None, label="assignment")
def getComparisonName(name1, name2)
def __getattr__(self, name)