Coverage for python/lsst/ctrl/mpexec/singleQuantumExecutor.py: 65%

54 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-16 15:01 -0700

1# This file is part of ctrl_mpexec. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27 

28from __future__ import annotations 

29 

30__all__ = ("SingleQuantumExecutor",) 

31 

32import uuid 

33from collections.abc import Callable, Mapping 

34from typing import TYPE_CHECKING, Any 

35 

36from deprecated.sphinx import deprecated 

37 

38import lsst.pipe.base.single_quantum_executor 

39 

40if TYPE_CHECKING: 

41 from lsst.daf.butler import Butler, ButlerMetrics, LimitedButler, Quantum 

42 from lsst.pipe.base import ExecutionResources, PipelineTask, QuantumSuccessCaveats, TaskFactory 

43 from lsst.pipe.base.log_capture import _ExecutionLogRecordsExtra 

44 from lsst.pipe.base.pipeline_graph import TaskNode 

45 

46 

47# TODO[DM-51962]: Remove this module. 

48@deprecated( 

49 "The SingleQuantumExecutor class has moved to lsst.pipe.base.single_quantum_executor. " 

50 "This forwarding shim will be removed after v30.", 

51 version="v30", 

52 category=FutureWarning, 

53) 

54class SingleQuantumExecutor(lsst.pipe.base.single_quantum_executor.SingleQuantumExecutor): 

55 """Executor class which runs one Quantum at a time. 

56 

57 Parameters 

58 ---------- 

59 butler : `~lsst.daf.butler.Butler` or `None` 

60 Data butler, `None` means that Quantum-backed butler should be used 

61 instead. 

62 taskFactory : `~lsst.pipe.base.TaskFactory` 

63 Instance of a task factory. 

64 skipExistingIn : `~typing.Any` 

65 Expressions representing the collections to search for existing output 

66 datasets. See :ref:`daf_butler_ordered_collection_searches` for allowed 

67 types. This class only checks for the presence of butler output run in 

68 the list of collections. If the output run is present in the list then 

69 the quanta whose complete outputs exist in the output run will be 

70 skipped. `None` or empty string/sequence disables skipping. 

71 clobberOutputs : `bool`, optional 

72 If `True`, then outputs from a quantum that exist in output run 

73 collection will be removed prior to executing a quantum. If 

74 ``skipExistingIn`` contains output run, then only partial outputs from 

75 a quantum will be removed. Only used when ``butler`` is not `None`. 

76 enableLsstDebug : `bool`, optional 

77 Enable debugging with ``lsstDebug`` facility for a task. 

78 limited_butler_factory : `~collections.abc.Callable`, optional 

79 A method that creates a `~lsst.daf.butler.LimitedButler` instance for a 

80 given Quantum. This parameter must be defined if ``butler`` is `None`. 

81 If ``butler`` is not `None` then this parameter is ignored. 

82 resources : `~lsst.pipe.base.ExecutionResources`, optional 

83 The resources available to this quantum when executing. 

84 skipExisting : `bool`, optional 

85 If `True`, skip quanta whose metadata datasets are already stored. 

86 Unlike ``skipExistingIn``, this works with limited butlers as well as 

87 full butlers. Always set to `True` if ``skipExistingIn`` matches 

88 ``butler.run``. 

89 assumeNoExistingOutputs : `bool`, optional 

90 If `True`, assume preexisting outputs are impossible (e.g. because this 

91 is known by higher-level code to be a new ``RUN`` collection), and do 

92 not look for them. This causes the ``skipExisting`` and 

93 ``clobberOutputs`` options to be ignored, but unlike just setting both 

94 of those to `False`, it also avoids all dataset existence checks. 

95 raise_on_partial_outputs : `bool`, optional 

96 If `True` raise exceptions chained by 

97 `lsst.pipe.base.AnnotatedPartialOutputsError` immediately, instead of 

98 considering the partial result a success and continuing to run 

99 downstream tasks. 

100 job_metadata : `~collections.abc.Mapping` 

101 Mapping with extra metadata to embed within the quantum metadata under 

102 the "job" key. This is intended to correspond to information common 

103 to all quanta being executed in a single process, such as the time 

104 taken to load the quantum graph in a BPS job. 

105 

106 Notes 

107 ----- 

108 This is a deprecated backwards-compatibility shim for 

109 `lsst.pipe.base.single_quantum_executor.SingleQuantumExecutor`, which has 

110 the same functionality with very minor interface changes. 

111 """ 

112 

113 def __init__( 

114 self, 

115 butler: Butler | None, 

116 taskFactory: TaskFactory, 

117 skipExistingIn: Any = None, 

118 clobberOutputs: bool = False, 

119 enableLsstDebug: bool = False, 

120 limited_butler_factory: Callable[[Quantum], LimitedButler] | None = None, 

121 resources: ExecutionResources | None = None, 

122 skipExisting: bool = False, 

123 assumeNoExistingOutputs: bool = False, 

124 raise_on_partial_outputs: bool = True, 

125 job_metadata: Mapping[str, int | str | float] | None = None, 

126 ): 

127 super().__init__( 

128 butler=butler, 

129 task_factory=taskFactory, 

130 skip_existing_in=skipExistingIn, 

131 clobber_outputs=clobberOutputs, 

132 enable_lsst_debug=enableLsstDebug, 

133 limited_butler_factory=limited_butler_factory, 

134 resources=resources, 

135 skip_existing=skipExisting, 

136 assume_no_existing_outputs=assumeNoExistingOutputs, 

137 raise_on_partial_outputs=raise_on_partial_outputs, 

138 job_metadata=job_metadata, 

139 ) 

140 

141 def checkExistingOutputs( 

142 self, 

143 quantum: Quantum, 

144 task_node: TaskNode, 

145 /, 

146 limited_butler: LimitedButler, 

147 log_extra: _ExecutionLogRecordsExtra, 

148 ) -> bool: 

149 return super()._check_existing_outputs( 

150 quantum, task_node, limited_butler=limited_butler, log_extra=log_extra 

151 ) 

152 

153 def updatedQuantumInputs( 

154 self, quantum: Quantum, task_node: TaskNode, /, limited_butler: LimitedButler 

155 ) -> Quantum: 

156 return super()._updated_quantum_inputs(quantum, task_node, limited_butler=limited_butler) 

157 

158 def runQuantum( 

159 self, 

160 task: PipelineTask, 

161 quantum: Quantum, 

162 task_node: TaskNode, 

163 /, 

164 limited_butler: LimitedButler, 

165 quantum_id: uuid.UUID | None = None, 

166 ) -> tuple[QuantumSuccessCaveats, list[uuid.UUID], ButlerMetrics]: 

167 ids_put: list[uuid.UUID] = [] 

168 with limited_butler.record_metrics() as butler_metrics: 

169 quantum_success_caveats = super()._run_quantum( 

170 task, 

171 quantum, 

172 task_node, 

173 limited_butler=limited_butler, 

174 quantum_id=quantum_id, 

175 ids_put=ids_put, 

176 ) 

177 return quantum_success_caveats, ids_put, butler_metrics 

178 

179 def writeMetadata( 

180 self, quantum: Quantum, metadata: Any, task_node: TaskNode, /, limited_butler: LimitedButler 

181 ) -> None: 

182 return super()._write_metadata(quantum, metadata, task_node, limited_butler=limited_butler) 

183 

184 def initGlobals(self, quantum: Quantum) -> None: 

185 pass 

186 

187 @property 

188 def butler(self) -> Butler | None: 

189 return self._butler 

190 

191 @property 

192 def taskFactory(self) -> TaskFactory: 

193 return self._task_factory 

194 

195 @property 

196 def clobberOutputs(self) -> bool: 

197 return self._clobber_outputs 

198 

199 @property 

200 def enableLsstDebug(self) -> bool: 

201 return self._enable_lsst_debug 

202 

203 @property 

204 def limited_butler_factory(self) -> Callable[[Quantum], LimitedButler] | None: 

205 return self._limited_butler_factory 

206 

207 @property 

208 def resources(self) -> ExecutionResources | None: 

209 return self._resources 

210 

211 @property 

212 def assumeNoExistingOutputs(self) -> bool: 

213 return self._assume_no_existing_outputs 

214 

215 @property 

216 def raise_on_partial_outputs(self) -> bool: 

217 return self._raise_on_partial_outputs 

218 

219 @property 

220 def job_metadata(self) -> Mapping[str, int | str | float] | None: 

221 return self._job_metadata 

222 

223 @property 

224 def skipExisting(self) -> bool: 

225 return self._skip_existing