Coverage for python/lsst/meas/base/pluginRegistry.py: 72%
55 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-14 02:42 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-14 02:42 -0700
1# This file is part of meas_base.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
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 GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22"""Registry for measurement plugins and utilities for plugin management.
23"""
25import collections
27import lsst.pipe.base
28import lsst.pex.config
29from .apCorrRegistry import addApCorrName
31__all__ = ("generateAlgorithmName", "PluginRegistry", "register", "PluginMap")
34def generateAlgorithmName(AlgClass):
35 """Generate a name for an algorithm.
37 This generates a short name for an algorithmic class that strips away
38 terms that are generally redundant while remaining easy to trace to the
39 code.
41 Parameters
42 ----------
43 AlgClass : subclass of `BaseAlgorithm`
44 The class to generate a name for.
46 Returns
47 -------
48 name : `str`
49 A short name for the algorithm.
51 Notes
52 -----
53 The returned name will cobmine the package name, with any ``lsst`` and/or
54 ``meas`` prefix removed, with the class name, with any ``Algorithm``
55 suffix removed. For instance, ``lsst.meas.base.SdssShapeAlgorithm``
56 becomes ``base_SdssShape``.
57 """
58 name = AlgClass.__name__
59 pkg = AlgClass.__module__
60 name = name.replace("Algorithm", "")
61 terms = pkg.split(".")
62 # Hide private module name only if it's part of a public package
63 if len(terms) > 1 and terms[-1].startswith("_"): 63 ↛ 64line 63 didn't jump to line 64, because the condition on line 63 was never true
64 terms = terms[:-1]
65 if len(terms) > 1 and terms[-1].endswith("Lib"): 65 ↛ 66line 65 didn't jump to line 66, because the condition on line 65 was never true
66 terms = terms[:-1]
67 if terms[0] == "lsst":
68 terms = terms[1:]
69 if terms[0] == "meas":
70 terms = terms[1:]
71 if name.lower().startswith(terms[-1].lower()):
72 terms = terms[:-1]
73 return "%s_%s" % ("_".join(terms), name)
76class PluginRegistry(lsst.pex.config.Registry):
77 """Base class for plugin registries.
79 Notes
80 -----
81 The class of plugins allowed in the registry is defined in the constructor
82 of the registry.
84 Single-frame and forced plugins have different registries.
85 """
87 class Configurable:
88 """Class used as the element in the plugin registry.
90 Parameters
91 ----------
92 name : `str`
93 Name under which the plugin is registerd.
94 PluginClass : subclass of `BasePlugin`
95 The class of plugin which can be stored in the registry.
97 Notes
98 -----
99 Rather than constructing a Plugin instance, its __call__ method
100 (invoked by RegistryField.apply) returns a tuple
101 of ``(executionOrder, name, config, PluginClass)``, which can then
102 be sorted before the plugins are instantiated.
103 """
105 __slots__ = "PluginClass", "name"
107 def __init__(self, name, PluginClass):
108 self.name = name
109 self.PluginClass = PluginClass
111 @property
112 def ConfigClass(self):
113 return self.PluginClass.ConfigClass
115 def __call__(self, config):
116 return (self.PluginClass.getExecutionOrder(), self.name, config, self.PluginClass)
118 def register(self, name, PluginClass, shouldApCorr=False, apCorrList=()):
119 """Register a plugin class with the given name.
121 Parameters
122 ----------
123 name : `str`
124 The name of the plugin. This is used as a prefix for all fields
125 produced by the plugin, and it should generally contain the name
126 of the plugin or algorithm class itself as well as enough of the
127 namespace to make it clear where to find the code. For example
128 ``base_GaussianFlux`` indicates an algorithm in `lsst.meas.base`
129 that measures Gaussian Flux and produces fields such as
130 ``base_GaussianFlux_instFlux``, ``base_GaussianFlux_instFluxErr``
131 and ``base_GaussianFlux_flag``.
132 shouldApCorr : `bool`
133 If `True`, then this algorithm measures an instFlux that should
134 be aperture corrected. This is shorthand for ``apCorrList=[name]``
135 and is ignored if ``apCorrList`` is specified.
136 apCorrList : `list` of `str`
137 List of field name prefixes for instFlux fields to be aperture
138 corrected. If an algorithm produces a single instFlux that should
139 be aperture corrected then it is simpler to set
140 ``shouldApCorr=True``. But if an algorithm produces multiple such
141 fields then it must specify ``apCorrList`` instead. For example,
142 ``modelfit_CModel`` produces three such fields:
143 ``apCorrList=("modelfit_CModel_exp", "modelfit_CModel_exp",
144 "modelfit_CModel_def")``. If ``apCorrList`` is not empty then
145 shouldApCorr is ignored.
147 Notes
148 -----
149 The same plugin may be registered multiple times with different names;
150 this can be useful if we often want to run it multiple times with
151 different configuration.
152 """
153 lsst.pex.config.Registry.register(self, name, self.Configurable(name, PluginClass))
154 if shouldApCorr and not apCorrList: 154 ↛ 155line 154 didn't jump to line 155, because the condition on line 154 was never true
155 apCorrList = [name]
156 for prefix in apCorrList: 156 ↛ 157line 156 didn't jump to line 157, because the loop on line 156 never started
157 addApCorrName(prefix)
159 def makeField(self, doc, default=None, optional=False, multi=False):
160 return lsst.pex.config.RegistryField(doc, self, default, optional, multi)
163def register(name, shouldApCorr=False, apCorrList=()):
164 """A decorator to register a plugin class in its base class's registry.
166 Parameters
167 ----------
168 shouldApCorr : `bool`
169 If `True`, then this algorithm measures an instFlux that should be
170 aperture corrected. This is shorthand for ``apCorrList=[name]`` and is
171 ignored if ``apCorrList`` is specified.
172 apCorrList : `list` of `str`
173 List of field name prefixes for instFlux fields to be aperture
174 corrected. If an algorithm produces a single instFlux that should be
175 aperture corrected then it is simpler to set ``shouldApCorr=True``.
176 But if an algorithm produces multiple such fields then it must specify
177 ``apCorrList`` instead. For example, ``modelfit_CModel`` produces
178 three such fields: ``apCorrList=("modelfit_CModel_exp",
179 "modelfit_CModel_exp", "modelfit_CModel_def")``. If ``apCorrList`` is
180 not empty then shouldApCorr is ignored.
182 """
184 def decorate(PluginClass):
185 PluginClass.registry.register(name, PluginClass, shouldApCorr=shouldApCorr, apCorrList=apCorrList)
186 return PluginClass
187 return decorate
190class PluginMap(collections.OrderedDict):
191 """Map of plugins to be run for a given task.
193 Notes
194 -----
195 Plugins are classes derived from `BasePlugin`.
197 We assume plugins are added to the plugin map according to their
198 "Execution Order", so this class doesn't actually do any of the sorting
199 (though it does have to maintain that order, which it does by inheriting
200 from `collections.OrderedDict`).
201 """
203 def iter(self):
204 """Return an iterator over plugins for use in single-object mode.
206 Notes
207 -----
208 Plugins which should be used in single-object mode are identified by
209 having the `doMeasure` config attribute evaluate to `True`. This is
210 usually a simple boolean class attribute.
211 """
212 for plugin in self.values():
213 if plugin.config.doMeasure:
214 yield plugin
216 def iterN(self):
217 """Return an iterator over plugins for use in multi-object mode.
219 Notes
220 -----
221 Plugins which should be used in multi-object mode are identified by
222 having the `doMeasureN` config attribute evaluate to `True`.
223 This is usually a simple boolean class attribute.
224 """
225 for plugin in self.values():
226 if plugin.config.doMeasureN:
227 yield plugin