28 __all__ = (
"Registry",
"makeRegistry",
"RegistryField",
"registerConfig",
"registerConfigurable")
30 import collections.abc
33 from .config
import Config, FieldValidationError, _typeStr
34 from .configChoiceField
import ConfigInstanceDict, ConfigChoiceField
38 """A wrapper for configurables.
40 Used for configurables that don't contain a ``ConfigClass`` attribute,
41 or contain one that is being overridden.
49 return self.
_target(*args, **kwargs)
53 """A base class for global registries, which map names to configurables.
55 A registry acts like a read-only dictionary with an additional `register`
56 method to add targets. Targets in the registry are configurables (see
61 configBaseType : `lsst.pex.config.Config`-type
62 The base class for config classes in the registry.
66 A configurable is a callable with call signature ``(config, *args)``
67 Configurables typically create an algorithm or are themselves the
68 algorithm. Often configurables are `lsst.pipe.base.Task` subclasses, but
71 A ``Registry`` has these requirements:
73 - All configurables added to a particular registry have the same call
75 - All configurables in a registry typically share something important
76 in common. For example, all configurables in ``psfMatchingRegistry``
77 return a PSF matching class that has a ``psfMatch`` method with a
78 particular call signature.
82 This examples creates a configurable class ``Foo`` and adds it to a
83 registry. First, creating the configurable:
85 >>> from lsst.pex.config import Registry, Config
86 >>> class FooConfig(Config):
87 ... val = Field(dtype=int, default=3, doc="parameter for Foo")
90 ... ConfigClass = FooConfig
91 ... def __init__(self, config):
92 ... self.config = config
93 ... def addVal(self, num):
94 ... return self.config.val + num
97 Next, create a ``Registry`` instance called ``registry`` and register the
98 ``Foo`` configurable under the ``"foo"`` key:
100 >>> registry = Registry()
101 >>> registry.register("foo", Foo)
102 >>> print(list(registry.keys()))
105 Now ``Foo`` is conveniently accessible from the registry itself.
107 Finally, use the registry to get the configurable class and create an
110 >>> FooConfigurable = registry["foo"]
111 >>> foo = FooConfigurable(FooConfigurable.ConfigClass())
117 if not issubclass(configBaseType, Config):
118 raise TypeError(
"configBaseType=%s must be a subclass of Config" % _typeStr(configBaseType,))
122 def register(self, name, target, ConfigClass=None):
123 """Add a new configurable target to the registry.
128 Name that the ``target`` is registered under. The target can
129 be accessed later with `dict`-like patterns using ``name`` as
132 A configurable type, usually a subclass of `lsst.pipe.base.Task`.
133 ConfigClass : `lsst.pex.config.Config`-type, optional
134 A subclass of `lsst.pex.config.Config` used to configure the
135 configurable. If `None` then the configurable's ``ConfigClass``
141 Raised if an item with ``name`` is already in the registry.
143 Raised if ``ConfigClass`` is `None` and ``target`` does not have
144 a ``ConfigClass`` attribute.
148 If ``ConfigClass`` is provided then the ``target`` configurable is
149 wrapped in a new object that forwards function calls to it. Otherwise
150 the original ``target`` is stored.
152 if name
in self.
_dict:
153 raise RuntimeError(
"An item with name %r already exists" % name)
154 if ConfigClass
is None:
159 raise TypeError(
"ConfigClass=%s is not a subclass of %r" %
161 self.
_dict[name] = wrapper
164 return self.
_dict[key]
167 return len(self.
_dict)
173 return key
in self.
_dict
175 def makeField(self, doc, default=None, optional=False, multi=False):
176 """Create a `RegistryField` configuration field from this registry.
181 A description of the field.
182 default : object, optional
183 The default target for the field.
184 optional : `bool`, optional
185 When `False`, `lsst.pex.config.Config.validate` fails if the
186 field's value is `None`.
187 multi : `bool`, optional
188 A flag to allow multiple selections in the `RegistryField` if
193 field : `lsst.pex.config.RegistryField`
194 `~lsst.pex.config.RegistryField` Configuration field.
200 """Private class that makes a `Registry` behave like the thing a
201 `~lsst.pex.config.ConfigChoiceField` expects.
205 registry : `Registry`
226 """Dictionary of instantiated configs, used to populate a `RegistryField`.
230 config : `lsst.pex.config.Config`
231 Configuration instance.
232 field : `RegistryField`
237 ConfigInstanceDict.__init__(self, config, field)
240 def _getTarget(self):
243 "Multi-selection field has no attribute 'target'")
246 target = property(_getTarget)
248 def _getTargets(self):
251 "Single-selection field has no attribute 'targets'")
254 targets = property(_getTargets)
257 """Call the active target(s) with the active config as a keyword arg
259 If this is a multi-selection field, return a list obtained by calling
260 each active target with its corresponding active config.
262 Additional arguments will be passed on to the configurable target(s)
265 msg =
"No selection has been made. Options: %s" % \
266 " ".join(self.
types.registry.keys())
271 retvals.append(self.
types.registry[c](*args, config=self[c], **kw))
274 return self.
types.registry[self.
name](*args, config=self[self.
name], **kw)
277 if attr ==
"registry":
278 object.__setattr__(self, attr, value)
280 ConfigInstanceDict.__setattr__(self, attr, value)
284 """A configuration field whose options are defined in a `Registry`.
289 A description of the field.
290 registry : `Registry`
291 The registry that contains this field.
292 default : `str`, optional
293 The default target key.
294 optional : `bool`, optional
295 When `False`, `lsst.pex.config.Config.validate` fails if the field's
297 multi : `bool`, optional
298 If `True`, the field allows multiple selections. The default is
314 instanceDictClass = RegistryInstanceDict
315 """Class used to hold configurable instances in the field.
318 def __init__(self, doc, registry, default=None, optional=False, multi=False):
321 ConfigChoiceField.__init__(self, doc, types, default, optional, multi)
324 """Customize deep-copying, want a reference to the original registry.
326 WARNING: this must be overridden by subclasses if they change the
327 constructor signature!
330 default=copy.deepcopy(self.
default),
332 other.source = self.
source
337 """Create a `Registry`.
342 Docstring for the created `Registry` (this is set as the ``__doc__``
343 attribute of the `Registry` instance.
344 configBaseType : `lsst.pex.config.Config`-type
345 Base type of config classes in the `Registry`
346 (`lsst.pex.config.Registry.configBaseType`).
350 registry : `Registry`
351 Registry with ``__doc__`` and `~Registry.configBaseType` attributes
354 cls =
type(
"Registry", (Registry,), {
"__doc__": doc})
355 return cls(configBaseType=configBaseType)
359 """A decorator that adds a class as a configurable in a `Registry`
365 Name of the target (the decorated class) in the ``registry``.
366 registry : `Registry`
367 The `Registry` instance that the decorated class is added to.
368 ConfigClass : `lsst.pex.config.Config`-type, optional
369 Config class associated with the configurable. If `None`, the class's
370 ``ConfigClass`` attribute is used instead.
378 Internally, this decorator runs `Registry.register`.
381 registry.register(name, target=cls, ConfigClass=ConfigClass)
387 """Decorator that adds a class as a ``ConfigClass`` in a `Registry` and
388 associates it with the given configurable.
393 Name of the ``target`` in the ``registry``.
394 registry : `Registry`
395 The registry containing the ``target``.
397 A configurable type, such as a subclass of `lsst.pipe.base.Task`.
405 Internally, this decorator runs `Registry.register`.
408 registry.register(name, target=target, ConfigClass=cls)