lsst.meas.base  16.0-14-ga5060d2+4
pluginRegistry.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008-2015 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 """Registry for measurement plugins and associated utilities generateAlgorithmName and PluginMap
24 """
25 import collections
26 
27 import lsst.pipe.base
28 import lsst.pex.config
29 from .apCorrRegistry import addApCorrName
30 
31 __all__ = ("generateAlgorithmName", "PluginRegistry", "register", "PluginMap")
32 
33 
34 def generateAlgorithmName(AlgClass):
35  """Generate a string name for an algorithm class that strips away terms that are generally redundant
36  while (hopefully) remaining easy to trace to the code.
37 
38  The returned name will cobmine the package name, with any "lsst" and/or "meas" prefix removed,
39  with the class name, with any "Algorithm" suffix removed. For instance,
40  lsst.meas.base.SdssShapeAlgorithm becomes "base_SdssShape".
41  """
42  name = AlgClass.__name__
43  pkg = AlgClass.__module__
44  name = name.replace("Algorithm", "")
45  terms = pkg.split(".")
46  # Hide private module name only if it's part of a public package
47  if len(terms) > 1 and terms[-1].startswith("_"):
48  terms = terms[:-1]
49  if len(terms) > 1 and terms[-1].endswith("Lib"):
50  terms = terms[:-1]
51  if terms[0] == "lsst":
52  terms = terms[1:]
53  if terms[0] == "meas":
54  terms = terms[1:]
55  if name.lower().startswith(terms[-1].lower()):
56  terms = terms[:-1]
57  return "%s_%s" % ("_".join(terms), name)
58 
59 
60 class PluginRegistry(lsst.pex.config.Registry):
61  """!
62  Base class for plugin registries
63 
64  The Plugin class allowed in the registry is defined in the ctor of the registry.
65 
66  Single-frame and forced plugins have different registries.
67  """
68 
69  class Configurable:
70  """!
71  Class used as the actual element in the registry
72 
73  Rather than constructing a Plugin instance, its __call__ method
74  (invoked by RegistryField.apply) returns a tuple
75  of (executionOrder, name, config, PluginClass), which can then
76  be sorted before the plugins are instantiated.
77  """
78 
79  __slots__ = "PluginClass", "name"
80 
81  def __init__(self, name, PluginClass):
82  """!
83  Create a Configurable object for the given PluginClass and name
84  """
85  self.name = name
86  self.PluginClass = PluginClass
87 
88  @property
89  def ConfigClass(self):
90  return self.PluginClass.ConfigClass
91 
92  def __call__(self, config):
93  return (self.PluginClass.getExecutionOrder(), self.name, config, self.PluginClass)
94 
95  def register(self, name, PluginClass, shouldApCorr=False, apCorrList=()):
96  """!
97  Register a Plugin class with the given name.
98 
99  The same Plugin may be registered multiple times with different names; this can
100  be useful if we often want to run it multiple times with different configuration.
101 
102  @param[in] name name of plugin class. This is used as a prefix for all fields produced by the Plugin,
103  and it should generally contain the name of the Plugin or Algorithm class itself
104  as well as enough of the namespace to make it clear where to find the code.
105  For example "base_GaussianFlux" indicates an algorithm in meas_base
106  that measures Gaussian Flux and produces fields such as "base_GaussianFlux_instFlux",
107  "base_GaussianFlux_instFluxErr" and "base_GaussianFlux_flag".
108  @param[in] shouldApCorr if True then this algorithm measures a instFlux that should be aperture
109  corrected. This is shorthand for apCorrList=[name] and is ignored if apCorrList is specified.
110  @param[in] apCorrList list of field name prefixes for instFlux fields to be aperture corrected.
111  If an algorithm produces a single instFlux that should be aperture corrected then it is simpler
112  to set shouldApCorr=True. But if an algorithm produces multiple such fields then it must
113  specify apCorrList, instead. For example modelfit_CModel produces 3 such fields:
114  apCorrList=("modelfit_CModel_exp", "modelfit_CModel_exp", "modelfit_CModel_def")
115  If apCorrList is non-empty then shouldApCorr is ignored.
116  """
117  lsst.pex.config.Registry.register(self, name, self.Configurable(name, PluginClass))
118  if shouldApCorr and not apCorrList:
119  apCorrList = [name]
120  for prefix in apCorrList:
121  addApCorrName(prefix)
122 
123  def makeField(self, doc, default=None, optional=False, multi=False):
124  return lsst.pex.config.RegistryField(doc, self, default, optional, multi)
125 
126 
127 def register(name, shouldApCorr=False, apCorrList=()):
128  """!
129  A Python decorator that registers a class, using the given name, in its base class's PluginRegistry.
130  For example,
131  @code
132  @register("base_TransformedCentroid")
133  class ForcedTransformedCentroidPlugin(ForcedPlugin):
134  ...
135  @endcode
136  is equivalent to:
137  @code
138  class ForcedTransformedCentroidPlugin(ForcedPlugin):
139  ...
140  @ForcedPlugin.registry.register("base_TransformedCentroid", ForcedTransformedCentroidPlugin)
141  @endcode
142  """
143  def decorate(PluginClass):
144  PluginClass.registry.register(name, PluginClass, shouldApCorr=shouldApCorr, apCorrList=apCorrList)
145  return PluginClass
146  return decorate
147 
148 
149 class PluginMap(collections.OrderedDict):
150  """!
151  Map of plugins (instances of subclasses of BasePlugin) to be run for a task
152 
153  We assume plugins are added to the PluginMap according to their "Execution Order", so this
154  class doesn't actually do any of the sorting (though it does have to maintain that order,
155  which it does by inheriting from OrderedDict).
156  """
157 
158  def iter(self):
159  """!Return an iterator over plugins for which plugin.config.doMeasure is true
160 
161  @note plugin.config.doMeasure is usually a simple boolean class attribute, not a normal Config field.
162  """
163  for plugin in self.values():
164  if plugin.config.doMeasure:
165  yield plugin
166 
167  def iterN(self):
168  """!Return an iterator over plugins for which plugin.config.doMeasureN is true
169 
170  @note plugin.config.doMeasureN is usually a simple boolean class attribute, not a normal Config field.
171  """
172  for plugin in self.values():
173  if plugin.config.doMeasureN:
174  yield plugin
def makeField(self, doc, default=None, optional=False, multi=False)
def addApCorrName(name)
Add to the set of field name prefixes for instrument flux that should be aperture corrected...
Class used as the actual element in the registry.
def register(name, shouldApCorr=False, apCorrList=())
A Python decorator that registers a class, using the given name, in its base class&#39;s PluginRegistry...
Base class for plugin registries.
def iter(self)
Return an iterator over plugins for which plugin.config.doMeasure is true.
Map of plugins (instances of subclasses of BasePlugin) to be run for a task.
def register(self, name, PluginClass, shouldApCorr=False, apCorrList=())
Register a Plugin class with the given name.
def iterN(self)
Return an iterator over plugins for which plugin.config.doMeasureN is true.
def __init__(self, name, PluginClass)
Create a Configurable object for the given PluginClass and name.