LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
registry.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ("Registry", "makeRegistry", "RegistryField", "registerConfig", "registerConfigurable")
24 
25 import collections.abc
26 import copy
27 
28 from .config import Config, FieldValidationError, _typeStr
29 from .configChoiceField import ConfigInstanceDict, ConfigChoiceField
30 
31 
33  """A wrapper for configurables.
34 
35  Used for configurables that don't contain a ``ConfigClass`` attribute,
36  or contain one that is being overridden.
37  """
38 
39  def __init__(self, target, ConfigClass):
40  self.ConfigClass = ConfigClass
41  self._target = target
42 
43  def __call__(self, *args, **kwargs):
44  return self._target(*args, **kwargs)
45 
46 
47 class Registry(collections.abc.Mapping):
48  """A base class for global registries, which map names to configurables.
49 
50  A registry acts like a read-only dictionary with an additional `register`
51  method to add targets. Targets in the registry are configurables (see
52  *Notes*).
53 
54  Parameters
55  ----------
56  configBaseType : `lsst.pex.config.Config`-type
57  The base class for config classes in the registry.
58 
59  Notes
60  -----
61  A configurable is a callable with call signature ``(config, *args)``
62  Configurables typically create an algorithm or are themselves the
63  algorithm. Often configurables are `lsst.pipe.base.Task` subclasses, but
64  this is not required.
65 
66  A ``Registry`` has these requirements:
67 
68  - All configurables added to a particular registry have the same call
69  signature.
70  - All configurables in a registry typically share something important
71  in common. For example, all configurables in ``psfMatchingRegistry``
72  return a PSF matching class that has a ``psfMatch`` method with a
73  particular call signature.
74 
75  Examples
76  --------
77  This examples creates a configurable class ``Foo`` and adds it to a
78  registry. First, creating the configurable:
79 
80  >>> from lsst.pex.config import Registry, Config
81  >>> class FooConfig(Config):
82  ... val = Field(dtype=int, default=3, doc="parameter for Foo")
83  ...
84  >>> class Foo:
85  ... ConfigClass = FooConfig
86  ... def __init__(self, config):
87  ... self.config = config
88  ... def addVal(self, num):
89  ... return self.config.val + num
90  ...
91 
92  Next, create a ``Registry`` instance called ``registry`` and register the
93  ``Foo`` configurable under the ``"foo"`` key:
94 
95  >>> registry = Registry()
96  >>> registry.register("foo", Foo)
97  >>> print(list(registry.keys()))
98  ["foo"]
99 
100  Now ``Foo`` is conveniently accessible from the registry itself.
101 
102  Finally, use the registry to get the configurable class and create an
103  instance of it:
104 
105  >>> FooConfigurable = registry["foo"]
106  >>> foo = FooConfigurable(FooConfigurable.ConfigClass())
107  >>> foo.addVal(5)
108  8
109  """
110 
111  def __init__(self, configBaseType=Config):
112  if not issubclass(configBaseType, Config):
113  raise TypeError("configBaseType=%s must be a subclass of Config" % _typeStr(configBaseType,))
114  self._configBaseType = configBaseType
115  self._dict = {}
116 
117  def register(self, name, target, ConfigClass=None):
118  """Add a new configurable target to the registry.
119 
120  Parameters
121  ----------
122  name : `str`
123  Name that the ``target`` is registered under. The target can
124  be accessed later with `dict`-like patterns using ``name`` as
125  the key.
126  target : obj
127  A configurable type, usually a subclass of `lsst.pipe.base.Task`.
128  ConfigClass : `lsst.pex.config.Config`-type, optional
129  A subclass of `lsst.pex.config.Config` used to configure the
130  configurable. If `None` then the configurable's ``ConfigClass``
131  attribute is used.
132 
133  Raises
134  ------
135  RuntimeError
136  Raised if an item with ``name`` is already in the registry.
137  AttributeError
138  Raised if ``ConfigClass`` is `None` and ``target`` does not have
139  a ``ConfigClass`` attribute.
140 
141  Notes
142  -----
143  If ``ConfigClass`` is provided then the ``target`` configurable is
144  wrapped in a new object that forwards function calls to it. Otherwise
145  the original ``target`` is stored.
146  """
147  if name in self._dict:
148  raise RuntimeError("An item with name %r already exists" % name)
149  if ConfigClass is None:
150  wrapper = target
151  else:
152  wrapper = ConfigurableWrapper(target, ConfigClass)
153  if not issubclass(wrapper.ConfigClass, self._configBaseType):
154  raise TypeError("ConfigClass=%s is not a subclass of %r" %
155  (_typeStr(wrapper.ConfigClass), _typeStr(self._configBaseType)))
156  self._dict[name] = wrapper
157 
158  def __getitem__(self, key):
159  return self._dict[key]
160 
161  def __len__(self):
162  return len(self._dict)
163 
164  def __iter__(self):
165  return iter(self._dict)
166 
167  def __contains__(self, key):
168  return key in self._dict
169 
170  def makeField(self, doc, default=None, optional=False, multi=False):
171  """Create a `RegistryField` configuration field from this registry.
172 
173  Parameters
174  ----------
175  doc : `str`
176  A description of the field.
177  default : object, optional
178  The default target for the field.
179  optional : `bool`, optional
180  When `False`, `lsst.pex.config.Config.validate` fails if the
181  field's value is `None`.
182  multi : `bool`, optional
183  A flag to allow multiple selections in the `RegistryField` if
184  `True`.
185 
186  Returns
187  -------
188  field : `lsst.pex.config.RegistryField`
189  `~lsst.pex.config.RegistryField` Configuration field.
190  """
191  return RegistryField(doc, self, default, optional, multi)
192 
193 
194 class RegistryAdaptor(collections.abc.Mapping):
195  """Private class that makes a `Registry` behave like the thing a
196  `~lsst.pex.config.ConfigChoiceField` expects.
197 
198  Parameters
199  ----------
200  registry : `Registry`
201  `Registry` instance.
202  """
203 
204  def __init__(self, registry):
205  self.registry = registry
206 
207  def __getitem__(self, k):
208  return self.registry[k].ConfigClass
209 
210  def __iter__(self):
211  return iter(self.registry)
212 
213  def __len__(self):
214  return len(self.registry)
215 
216  def __contains__(self, k):
217  return k in self.registry
218 
219 
221  """Dictionary of instantiated configs, used to populate a `RegistryField`.
222 
223  Parameters
224  ----------
225  config : `lsst.pex.config.Config`
226  Configuration instance.
227  field : `RegistryField`
228  Configuration field.
229  """
230 
231  def __init__(self, config, field):
232  ConfigInstanceDict.__init__(self, config, field)
233  self.registry = field.registry
234 
235  def _getTarget(self):
236  if self._field.multi:
237  raise FieldValidationError(self._field, self._config,
238  "Multi-selection field has no attribute 'target'")
239  return self._field.typemap.registry[self._selection]
240 
241  target = property(_getTarget)
242 
243  def _getTargets(self):
244  if not self._field.multi:
245  raise FieldValidationError(self._field, self._config,
246  "Single-selection field has no attribute 'targets'")
247  return [self._field.typemap.registry[c] for c in self._selection]
248 
249  targets = property(_getTargets)
250 
251  def apply(self, *args, **kw):
252  """Call the active target(s) with the active config as a keyword arg
253 
254  If this is a multi-selection field, return a list obtained by calling
255  each active target with its corresponding active config.
256 
257  Additional arguments will be passed on to the configurable target(s)
258  """
259  if self.active is None:
260  msg = "No selection has been made. Options: %s" % \
261  (" ".join(list(self._field.typemap.registry.keys())))
262  raise FieldValidationError(self._field, self._config, msg)
263  if self._field.multi:
264  retvals = []
265  for c in self._selection:
266  retvals.append(self._field.typemap.registry[c](*args, config=self[c], **kw))
267  return retvals
268  else:
269  return self._field.typemap.registry[self.name](*args, config=self[self.name], **kw)
270 
271  def __setattr__(self, attr, value):
272  if attr == "registry":
273  object.__setattr__(self, attr, value)
274  else:
275  ConfigInstanceDict.__setattr__(self, attr, value)
276 
277 
279  """A configuration field whose options are defined in a `Registry`.
280 
281  Parameters
282  ----------
283  doc : `str`
284  A description of the field.
285  registry : `Registry`
286  The registry that contains this field.
287  default : `str`, optional
288  The default target key.
289  optional : `bool`, optional
290  When `False`, `lsst.pex.config.Config.validate` fails if the field's
291  value is `None`.
292  multi : `bool`, optional
293  If `True`, the field allows multiple selections. The default is
294  `False`.
295 
296  See also
297  --------
298  ChoiceField
299  ConfigChoiceField
300  ConfigDictField
301  ConfigField
302  ConfigurableField
303  DictField
304  Field
305  ListField
306  RangeField
307  """
308 
309  instanceDictClass = RegistryInstanceDict
310  """Class used to hold configurable instances in the field.
311  """
312 
313  def __init__(self, doc, registry, default=None, optional=False, multi=False):
314  types = RegistryAdaptor(registry)
315  self.registry = registry
316  ConfigChoiceField.__init__(self, doc, types, default, optional, multi)
317 
318  def __deepcopy__(self, memo):
319  """Customize deep-copying, want a reference to the original registry.
320 
321  WARNING: this must be overridden by subclasses if they change the
322  constructor signature!
323  """
324  other = type(self)(doc=self.doc, registry=self.registry,
325  default=copy.deepcopy(self.default),
326  optional=self.optional, multi=self.multi)
327  other.source = self.source
328  return other
329 
330 
331 def makeRegistry(doc, configBaseType=Config):
332  """Create a `Registry`.
333 
334  Parameters
335  ----------
336  doc : `str`
337  Docstring for the created `Registry` (this is set as the ``__doc__``
338  attribute of the `Registry` instance.
339  configBaseType : `lsst.pex.config.Config`-type
340  Base type of config classes in the `Registry`
341  (`lsst.pex.config.Registry.configBaseType`).
342 
343  Returns
344  -------
345  registry : `Registry`
346  Registry with ``__doc__`` and `~Registry.configBaseType` attributes
347  set.
348  """
349  cls = type("Registry", (Registry,), {"__doc__": doc})
350  return cls(configBaseType=configBaseType)
351 
352 
353 def registerConfigurable(name, registry, ConfigClass=None):
354  """A decorator that adds a class as a configurable in a `Registry`
355  instance.
356 
357  Parameters
358  ----------
359  name : `str`
360  Name of the target (the decorated class) in the ``registry``.
361  registry : `Registry`
362  The `Registry` instance that the decorated class is added to.
363  ConfigClass : `lsst.pex.config.Config`-type, optional
364  Config class associated with the configurable. If `None`, the class's
365  ``ConfigClass`` attribute is used instead.
366 
367  See also
368  --------
369  registerConfig
370 
371  Notes
372  -----
373  Internally, this decorator runs `Registry.register`.
374  """
375  def decorate(cls):
376  registry.register(name, target=cls, ConfigClass=ConfigClass)
377  return cls
378  return decorate
379 
380 
381 def registerConfig(name, registry, target):
382  """Decorator that adds a class as a ``ConfigClass`` in a `Registry` and
383  associates it with the given configurable.
384 
385  Parameters
386  ----------
387  name : `str`
388  Name of the ``target`` in the ``registry``.
389  registry : `Registry`
390  The registry containing the ``target``.
391  target : obj
392  A configurable type, such as a subclass of `lsst.pipe.base.Task`.
393 
394  See also
395  --------
396  registerConfigurable
397 
398  Notes
399  -----
400  Internally, this decorator runs `Registry.register`.
401  """
402  def decorate(cls):
403  registry.register(name, target=target, ConfigClass=cls)
404  return cls
405  return decorate
def registerConfig(name, registry, target)
Definition: registry.py:381
def registerConfigurable(name, registry, ConfigClass=None)
Definition: registry.py:353
def __init__(self, configBaseType=Config)
Definition: registry.py:111
def __init__(self, doc, registry, default=None, optional=False, multi=False)
Definition: registry.py:313
table::Key< int > type
Definition: Detector.cc:164
def makeField(self, doc, default=None, optional=False, multi=False)
Definition: registry.py:170
def makeRegistry(doc, configBaseType=Config)
Definition: registry.py:331
def __init__(self, target, ConfigClass)
Definition: registry.py:39
daf::base::PropertyList * list
Definition: fits.cc:833
def register(self, name, target, ConfigClass=None)
Definition: registry.py:117