Coverage for python/lsst/meas/base/pluginRegistry.py: 72%

55 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-03 02:56 -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/>. 

21 

22"""Registry for measurement plugins and utilities for plugin management. 

23""" 

24 

25import collections 

26 

27import lsst.pipe.base 

28import lsst.pex.config 

29from .apCorrRegistry import addApCorrName 

30 

31__all__ = ("generateAlgorithmName", "PluginRegistry", "register", "PluginMap") 

32 

33 

34def generateAlgorithmName(AlgClass): 

35 """Generate a name for an algorithm. 

36 

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. 

40 

41 Parameters 

42 ---------- 

43 AlgClass : subclass of `BaseAlgorithm` 

44 The class to generate a name for. 

45 

46 Returns 

47 ------- 

48 name : `str` 

49 A short name for the algorithm. 

50 

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) 

74 

75 

76class PluginRegistry(lsst.pex.config.Registry): 

77 """Base class for plugin registries. 

78 

79 Notes 

80 ----- 

81 The class of plugins allowed in the registry is defined in the constructor 

82 of the registry. 

83 

84 Single-frame and forced plugins have different registries. 

85 """ 

86 

87 class Configurable: 

88 """Class used as the element in the plugin registry. 

89 

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. 

96 

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 """ 

104 

105 __slots__ = "PluginClass", "name" 

106 

107 def __init__(self, name, PluginClass): 

108 self.name = name 

109 self.PluginClass = PluginClass 

110 

111 @property 

112 def ConfigClass(self): 

113 return self.PluginClass.ConfigClass 

114 

115 def __call__(self, config): 

116 return (self.PluginClass.getExecutionOrder(), self.name, config, self.PluginClass) 

117 

118 def register(self, name, PluginClass, shouldApCorr=False, apCorrList=()): 

119 """Register a plugin class with the given name. 

120 

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. 

146 

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) 

158 

159 def makeField(self, doc, default=None, optional=False, multi=False): 

160 return lsst.pex.config.RegistryField(doc, self, default, optional, multi) 

161 

162 

163def register(name, shouldApCorr=False, apCorrList=()): 

164 """A decorator to register a plugin class in its base class's registry. 

165 

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. 

181 

182 """ 

183 

184 def decorate(PluginClass): 

185 PluginClass.registry.register(name, PluginClass, shouldApCorr=shouldApCorr, apCorrList=apCorrList) 

186 return PluginClass 

187 return decorate 

188 

189 

190class PluginMap(collections.OrderedDict): 

191 """Map of plugins to be run for a given task. 

192 

193 Notes 

194 ----- 

195 Plugins are classes derived from `BasePlugin`. 

196 

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 """ 

202 

203 def iter(self): 

204 """Return an iterator over plugins for use in single-object mode. 

205 

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 

215 

216 def iterN(self): 

217 """Return an iterator over plugins for use in multi-object mode. 

218 

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