22 __all__ = (
'ConfigurableInstance',
'ConfigurableField')
26 from .config
import Config, Field, _joinNamePath, _typeStr, FieldValidationError
27 from .comparison
import compareConfigs, getComparisonName
28 from .callStack
import getCallStack, getStackFrame
32 """A retargetable configuration in a `ConfigurableField` that proxies 33 a `~lsst.pex.config.Config`. 37 ``ConfigurableInstance`` implements ``__getattr__`` and ``__setattr__`` 38 methods that forward to the `~lsst.pex.config.Config` it holds. 39 ``ConfigurableInstance`` adds a `retarget` method. 41 The actual `~lsst.pex.config.Config` instance is accessed using the 42 ``value`` property (e.g. to get its documentation). The associated 43 configurable object (usually a `~lsst.pipe.base.Task`) is accessed 44 using the ``target`` property. 47 def __initValue(self, at, label):
48 """Construct value of field. 52 If field.default is an instance of `lsst.pex.config.ConfigClass`, 53 custom construct ``_value`` with the correct values from default. 54 Otherwise, call ``ConfigClass`` constructor 56 name = _joinNamePath(self._config._name, self._field.name)
58 storage = self._field.default._storage
61 value = self._ConfigClass(__name=name, __at=at, __label=label, **storage)
62 object.__setattr__(self,
"_value", value)
64 def __init__(self, config, field, at=None, label="default"):
65 object.__setattr__(self,
"_config", config)
66 object.__setattr__(self,
"_field", field)
67 object.__setattr__(self,
"__doc__", config)
68 object.__setattr__(self,
"_target", field.target)
69 object.__setattr__(self,
"_ConfigClass", field.ConfigClass)
70 object.__setattr__(self,
"_value",
None)
74 at += [self._field.source]
77 history = config._history.setdefault(field.name, [])
78 history.append((
"Targeted and initialized from defaults", at, label))
80 target = property(
lambda x: x._target)
81 """The targeted configurable (read-only). 84 ConfigClass = property(
lambda x: x._ConfigClass)
85 """The configuration class (read-only) 88 value = property(
lambda x: x._value)
89 """The `ConfigClass` instance (`lsst.pex.config.ConfigClass`-type, 94 """Call the configurable. 98 In addition to the user-provided positional and keyword arguments, 99 the configurable is also provided a keyword argument ``config`` with 100 the value of `ConfigurableInstance.value`. 104 def retarget(self, target, ConfigClass=None, at=None, label="retarget"):
105 """Target a new configurable and ConfigClass 107 if self._config._frozen:
111 ConfigClass = self._field.validateTarget(target, ConfigClass)
112 except BaseException
as e:
117 object.__setattr__(self,
"_target", target)
119 object.__setattr__(self,
"_ConfigClass", ConfigClass)
122 history = self._config._history.setdefault(self._field.name, [])
123 msg =
"retarget(target=%s, ConfigClass=%s)" % (_typeStr(target), _typeStr(ConfigClass))
124 history.append((msg, at, label))
127 return getattr(self._value, name)
130 """Pretend to be an instance of ConfigClass. 132 Attributes defined by ConfigurableInstance will shadow those defined in ConfigClass 134 if self._config._frozen:
137 if name
in self.__dict__:
139 object.__setattr__(self, name, value)
143 self._value.
__setattr__(name, value, at=at, label=label)
147 Pretend to be an isntance of ConfigClass. 148 Attributes defiend by ConfigurableInstance will shadow those defined in ConfigClass 150 if self._config._frozen:
155 object.__delattr__(self, name)
156 except AttributeError:
163 """A configuration field (`~lsst.pex.config.Field` subclass) that can be 164 can be retargeted towards a different configurable (often a 165 `lsst.pipe.base.Task` subclass). 167 The ``ConfigurableField`` is often used to configure subtasks, which are 168 tasks (`~lsst.pipe.base.Task`) called by a parent task. 173 A description of the configuration field. 174 target : configurable class 175 The configurable target. Configurables have a ``ConfigClass`` 176 attribute. Within the task framework, configurables are 177 `lsst.pipe.base.Task` subclasses) 178 ConfigClass : `lsst.pex.config.Config`-type, optional 179 The subclass of `lsst.pex.config.Config` expected as the configuration 180 class of the ``target``. If ``ConfigClass`` is unset then 181 ``target.ConfigClass`` is used. 182 default : ``ConfigClass``-type, optional 183 The default configuration class. Normally this parameter is not set, 184 and defaults to ``ConfigClass`` (or ``target.ConfigClass``). 185 check : callable, optional 186 Callable that takes the field's value (the ``target``) as its only 187 positional argument, and returns `True` if the ``target`` is valid (and 204 You can use the `ConfigurableInstance.apply` method to construct a 205 fully-configured configurable. 209 """Validate the target and configuration class. 214 The configurable being verified. 215 ConfigClass : `lsst.pex.config.Config`-type or `None` 216 The configuration class associated with the ``target``. This can 217 be `None` if ``target`` has a ``ConfigClass`` attribute. 222 Raised if ``ConfigClass`` is `None` and ``target`` does not have a 223 ``ConfigClass`` attribute. 225 Raised if ``ConfigClass`` is not a `~lsst.pex.config.Config` 230 - ``target`` is not callable (callables have a ``__call__`` 232 - ``target`` is not startically defined (does not have 233 ``__module__`` or ``__name__`` attributes). 235 if ConfigClass
is None:
237 ConfigClass = target.ConfigClass
239 raise AttributeError(
"'target' must define attribute 'ConfigClass'")
240 if not issubclass(ConfigClass, Config):
241 raise TypeError(
"'ConfigClass' is of incorrect type %s." 242 "'ConfigClass' must be a subclass of Config" % _typeStr(ConfigClass))
243 if not hasattr(target,
'__call__'):
244 raise ValueError(
"'target' must be callable")
245 if not hasattr(target,
'__module__')
or not hasattr(target,
'__name__'):
246 raise ValueError(
"'target' must be statically defined" 247 "(must have '__module__' and '__name__' attributes)")
250 def __init__(self, doc, target, ConfigClass=None, default=None, check=None):
254 default = ConfigClass
255 if default != ConfigClass
and type(default) != ConfigClass:
256 raise TypeError(
"'default' is of incorrect type %s. Expected %s" %
257 (_typeStr(default), _typeStr(ConfigClass)))
260 self.
_setup(doc=doc, dtype=ConfigurableInstance, default=default,
261 check=check, optional=
False, source=source)
265 def __getOrMake(self, instance, at=None, label="default"):
266 value = instance._storage.get(self.name,
None)
271 instance._storage[self.name] = value
274 def __get__(self, instance, owner=None, at=None, label="default"):
275 if instance
is None or not isinstance(instance, Config):
278 return self.
__getOrMake(instance, at=at, label=label)
280 def __set__(self, instance, value, at=None, label="assignment"):
287 if isinstance(value, ConfigurableInstance):
288 oldValue.retarget(value.target, value.ConfigClass, at, label)
289 oldValue.update(__at=at, __label=label, **value._storage)
290 elif type(value) == oldValue._ConfigClass:
291 oldValue.update(__at=at, __label=label, **value._storage)
292 elif value == oldValue.ConfigClass:
293 value = oldValue.ConfigClass()
294 oldValue.update(__at=at, __label=label, **value._storage)
296 msg =
"Value %s is of incorrect type %s. Expected %s" % \
297 (value, _typeStr(value), _typeStr(oldValue.ConfigClass))
301 fullname = _joinNamePath(instance._name, self.name)
303 value._rename(fullname)
305 def _collectImports(self, instance, imports):
307 target = value.target
308 imports.add(target.__module__)
309 value.value._collectImports()
310 imports |= value.value._imports
312 def save(self, outfile, instance):
313 fullname = _joinNamePath(instance._name, self.name)
315 target = value.target
320 ConfigClass = value.ConfigClass
321 outfile.write(
u"{}.retarget(target={}, ConfigClass={})\n\n".
format(fullname,
323 _typeStr(ConfigClass)))
333 return value.toDict()
339 if self.
check is not None and not self.
check(value):
340 msg =
"%s is not a valid value" %
str(value)
344 """Customize deep-copying, because we always want a reference to the 347 WARNING: this must be overridden by subclasses if they change the 348 constructor signature! 351 default=copy.deepcopy(self.
default))
353 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
354 """Compare two fields for equality. 356 Used by `lsst.pex.ConfigDictField.compare`. 360 instance1 : `lsst.pex.config.Config` 361 Left-hand side config instance to compare. 362 instance2 : `lsst.pex.config.Config` 363 Right-hand side config instance to compare. 365 If `True`, this function returns as soon as an inequality if found. 367 Relative tolerance for floating point comparisons. 369 Absolute tolerance for floating point comparisons. 371 A callable that takes a string, used (possibly repeatedly) to 372 report inequalities. For example: `print`. 377 `True` if the fields are equal, `False` otherwise. 381 Floating point comparisons are performed by `numpy.allclose`. 383 c1 = getattr(instance1, self.name)._value
384 c2 = getattr(instance2, self.name)._value
386 _joinNamePath(instance1._name, self.name),
387 _joinNamePath(instance2._name, self.name)
389 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)