lsst-ctrl-mpexec 29.2025.2400__py3-none-any.whl → 29.2025.3200__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lsst/ctrl/mpexec/__init__.py +1 -2
- lsst/ctrl/mpexec/cli/butler_factory.py +464 -0
- lsst/ctrl/mpexec/cli/cmd/commands.py +7 -1
- lsst/ctrl/mpexec/cli/opt/optionGroups.py +0 -13
- lsst/ctrl/mpexec/cli/opt/options.py +0 -46
- lsst/ctrl/mpexec/cli/script/build.py +49 -36
- lsst/ctrl/mpexec/cli/script/pre_exec_init_qbb.py +3 -1
- lsst/ctrl/mpexec/cli/script/qgraph.py +0 -25
- lsst/ctrl/mpexec/cli/script/run.py +2 -1
- lsst/ctrl/mpexec/cli/script/run_qbb.py +2 -1
- lsst/ctrl/mpexec/cmdLineFwk.py +30 -556
- lsst/ctrl/mpexec/execFixupDataId.py +9 -101
- lsst/ctrl/mpexec/executionGraphFixup.py +12 -37
- lsst/ctrl/mpexec/log_capture.py +9 -195
- lsst/ctrl/mpexec/mpGraphExecutor.py +60 -696
- lsst/ctrl/mpexec/quantumGraphExecutor.py +20 -90
- lsst/ctrl/mpexec/reports.py +30 -206
- lsst/ctrl/mpexec/separablePipelineExecutor.py +12 -263
- lsst/ctrl/mpexec/showInfo.py +2 -2
- lsst/ctrl/mpexec/simple_pipeline_executor.py +11 -590
- lsst/ctrl/mpexec/singleQuantumExecutor.py +75 -532
- lsst/ctrl/mpexec/taskFactory.py +12 -38
- lsst/ctrl/mpexec/version.py +1 -1
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/METADATA +1 -1
- lsst_ctrl_mpexec-29.2025.3200.dist-info/RECORD +51 -0
- lsst/ctrl/mpexec/dotTools.py +0 -100
- lsst_ctrl_mpexec-29.2025.2400.dist-info/RECORD +0 -51
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/WHEEL +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/entry_points.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/licenses/COPYRIGHT +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/licenses/LICENSE +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/licenses/bsd_license.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/licenses/gpl-v3.0.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/top_level.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.2400.dist-info → lsst_ctrl_mpexec-29.2025.3200.dist-info}/zip-safe +0 -0
|
@@ -25,100 +25,30 @@
|
|
|
25
25
|
# You should have received a copy of the GNU General Public License
|
|
26
26
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
__all__ = ("QuantumExecutor", "QuantumGraphExecutor")
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
from deprecated.sphinx import deprecated
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
from typing import TYPE_CHECKING
|
|
32
|
+
import lsst.pipe.base.quantum_graph_executor
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
# TODO[DM-51962]: Remove this module.
|
|
36
35
|
|
|
37
|
-
if TYPE_CHECKING:
|
|
38
|
-
import uuid
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
@deprecated(
|
|
38
|
+
"The QuantumExecutor class has moved to lsst.pipe.base.quantum_graph_executor. "
|
|
39
|
+
"This forwarding shim will be removed after v30.",
|
|
40
|
+
version="v30",
|
|
41
|
+
category=FutureWarning,
|
|
42
|
+
)
|
|
43
|
+
class QuantumExecutor(lsst.pipe.base.quantum_graph_executor.QuantumExecutor): # noqa: D101
|
|
44
|
+
pass
|
|
43
45
|
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@abstractmethod
|
|
55
|
-
def execute(
|
|
56
|
-
self, task_node: TaskNode, /, quantum: Quantum, quantum_id: uuid.UUID | None = None
|
|
57
|
-
) -> tuple[Quantum, QuantumReport | None]:
|
|
58
|
-
"""Execute single quantum.
|
|
59
|
-
|
|
60
|
-
Parameters
|
|
61
|
-
----------
|
|
62
|
-
task_node : `~lsst.pipe.base.pipeline_graph.TaskNode`
|
|
63
|
-
Task definition structure.
|
|
64
|
-
quantum : `~lsst.daf.butler.Quantum`
|
|
65
|
-
Quantum for this execution.
|
|
66
|
-
quantum_id : `uuid.UUID` or `None`, optional
|
|
67
|
-
The ID of the quantum to be executed.
|
|
68
|
-
|
|
69
|
-
Returns
|
|
70
|
-
-------
|
|
71
|
-
quantum : `~lsst.daf.butler.Quantum`
|
|
72
|
-
The quantum actually executed.
|
|
73
|
-
report : `~lsst.ctrl.mpexec.QuantumReport`
|
|
74
|
-
Structure describing the status of the execution of a quantum.
|
|
75
|
-
`None` is returned if implementation does not support this
|
|
76
|
-
feature.
|
|
77
|
-
|
|
78
|
-
Notes
|
|
79
|
-
-----
|
|
80
|
-
Any exception raised by the task or code that wraps task execution is
|
|
81
|
-
propagated to the caller of this method.
|
|
82
|
-
"""
|
|
83
|
-
raise NotImplementedError()
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class QuantumGraphExecutor(ABC):
|
|
87
|
-
"""Class which abstracts QuantumGraph execution.
|
|
88
|
-
|
|
89
|
-
Any specific execution model is implemented in sub-class by overriding
|
|
90
|
-
the `execute` method.
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
@abstractmethod
|
|
94
|
-
def execute(self, graph: QuantumGraph) -> None:
|
|
95
|
-
"""Execute whole graph.
|
|
96
|
-
|
|
97
|
-
Implementation of this method depends on particular execution model
|
|
98
|
-
and it has to be provided by a subclass. Execution model determines
|
|
99
|
-
what happens here; it can be either actual running of the task or,
|
|
100
|
-
for example, generation of the scripts for delayed batch execution.
|
|
101
|
-
|
|
102
|
-
Parameters
|
|
103
|
-
----------
|
|
104
|
-
graph : `~lsst.pipe.base.QuantumGraph`
|
|
105
|
-
Execution graph.
|
|
106
|
-
"""
|
|
107
|
-
raise NotImplementedError()
|
|
108
|
-
|
|
109
|
-
def getReport(self) -> Report | None:
|
|
110
|
-
"""Return execution report from last call to `execute`.
|
|
111
|
-
|
|
112
|
-
Returns
|
|
113
|
-
-------
|
|
114
|
-
report : `~lsst.ctrl.mpexec.Report`, optional
|
|
115
|
-
Structure describing the status of the execution of a quantum
|
|
116
|
-
graph. `None` is returned if implementation does not support
|
|
117
|
-
this feature.
|
|
118
|
-
|
|
119
|
-
Raises
|
|
120
|
-
------
|
|
121
|
-
RuntimeError
|
|
122
|
-
Raised if this method is called before `execute`.
|
|
123
|
-
"""
|
|
124
|
-
return None
|
|
47
|
+
@deprecated(
|
|
48
|
+
"The QuantumGraphExecutor class has moved to lsst.pipe.base.quantum_graph_executor. "
|
|
49
|
+
"This forwarding shim will be removed after v30.",
|
|
50
|
+
version="v30",
|
|
51
|
+
category=FutureWarning,
|
|
52
|
+
)
|
|
53
|
+
class QuantumGraphExecutor(lsst.pipe.base.quantum_graph_executor.QuantumGraphExecutor): # noqa: D101
|
|
54
|
+
pass
|
lsst/ctrl/mpexec/reports.py
CHANGED
|
@@ -25,219 +25,43 @@
|
|
|
25
25
|
# You should have received a copy of the GNU General Public License
|
|
26
26
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
__all__ = ("ExceptionInfo", "ExecutionStatus", "QuantumReport", "Report")
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
from deprecated.sphinx import deprecated
|
|
31
31
|
|
|
32
|
-
import
|
|
33
|
-
import sys
|
|
34
|
-
from typing import Any
|
|
32
|
+
import lsst.pipe.base.quantum_reports
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
# TODO[DM-51962]: Remove this module.
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
from lsst.utils.introspection import get_full_type_name
|
|
36
|
+
# We can't make this shim warn because enums can't be subclassed.
|
|
37
|
+
ExecutionStatus = lsst.pipe.base.quantum_reports.ExecutionStatus
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
@deprecated(
|
|
41
|
+
"The ExceptionInfo class has moved to lsst.pipe.base.quantum_reports. "
|
|
42
|
+
"This forwarding shim will be removed after v30.",
|
|
43
|
+
version="v30",
|
|
44
|
+
category=FutureWarning,
|
|
45
|
+
)
|
|
46
|
+
class ExceptionInfo(lsst.pipe.base.quantum_reports.ExceptionInfo): # noqa: D101
|
|
47
|
+
pass
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
"
|
|
50
|
+
@deprecated(
|
|
51
|
+
"The QuantumReport class has moved to lsst.pipe.base.quantum_reports. "
|
|
52
|
+
"This forwarding shim will be removed after v30.",
|
|
53
|
+
version="v30",
|
|
54
|
+
category=FutureWarning,
|
|
55
|
+
)
|
|
56
|
+
class QuantumReport(lsst.pipe.base.quantum_reports.QuantumReport): # noqa: D101
|
|
57
|
+
pass
|
|
52
58
|
|
|
53
|
-
Status `FAILURE` is set if one or more tasks failed. Status `TIMEOUT` is
|
|
54
|
-
set if there are no failures but one or more tasks timed out. Timeouts can
|
|
55
|
-
only be detected in multi-process mode, child task is killed on timeout
|
|
56
|
-
and usually should have non-zero exit code.
|
|
57
|
-
"""
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
className: str
|
|
69
|
-
"""Name of the exception class if exception was raised."""
|
|
70
|
-
|
|
71
|
-
message: str
|
|
72
|
-
"""Exception message for in-process quantum execution, None if
|
|
73
|
-
quantum was executed in sub-process.
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
@classmethod
|
|
77
|
-
def from_exception(cls, exception: Exception) -> ExceptionInfo:
|
|
78
|
-
"""Construct instance from an exception.
|
|
79
|
-
|
|
80
|
-
Parameters
|
|
81
|
-
----------
|
|
82
|
-
exception : `Exception`
|
|
83
|
-
Exception to wrap.
|
|
84
|
-
|
|
85
|
-
Returns
|
|
86
|
-
-------
|
|
87
|
-
info : `ExceptionInfo`
|
|
88
|
-
Information about the exception.
|
|
89
|
-
"""
|
|
90
|
-
return cls(className=get_full_type_name(exception), message=str(exception))
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class QuantumReport(pydantic.BaseModel):
|
|
94
|
-
"""Task execution report for a single Quantum.
|
|
95
|
-
|
|
96
|
-
Parameters
|
|
97
|
-
----------
|
|
98
|
-
dataId : `~lsst.daf.butler.DataId`
|
|
99
|
-
Quantum data ID.
|
|
100
|
-
taskLabel : `str`
|
|
101
|
-
Label for task executing this Quantum.
|
|
102
|
-
status : `ExecutionStatus`
|
|
103
|
-
Status of this quantum execution.
|
|
104
|
-
exitCode : `int` or `None`, optional
|
|
105
|
-
Exit code for sub-process executing this Quantum. `None` for
|
|
106
|
-
in-process execution. Negative if process was killed by a signal.
|
|
107
|
-
exceptionInfo : `ExceptionInfo` or `None`, optional
|
|
108
|
-
Exception information if an exception was raised.
|
|
109
|
-
"""
|
|
110
|
-
|
|
111
|
-
status: ExecutionStatus = ExecutionStatus.SUCCESS
|
|
112
|
-
"""Execution status, one of the values in `ExecutionStatus` enum."""
|
|
113
|
-
|
|
114
|
-
dataId: dict[str, DataIdValue]
|
|
115
|
-
"""Quantum DataId."""
|
|
116
|
-
|
|
117
|
-
taskLabel: str | None
|
|
118
|
-
"""Label for a task executing this Quantum."""
|
|
119
|
-
|
|
120
|
-
exitCode: int | None = None
|
|
121
|
-
"""Exit code for a sub-process executing Quantum, None for in-process
|
|
122
|
-
Quantum execution. Negative if process was killed by a signal.
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
exceptionInfo: ExceptionInfo | None = None
|
|
126
|
-
"""Exception information if exception was raised."""
|
|
127
|
-
|
|
128
|
-
def __init__(
|
|
129
|
-
self,
|
|
130
|
-
dataId: DataId,
|
|
131
|
-
taskLabel: str,
|
|
132
|
-
status: ExecutionStatus = ExecutionStatus.SUCCESS,
|
|
133
|
-
exitCode: int | None = None,
|
|
134
|
-
exceptionInfo: ExceptionInfo | None = None,
|
|
135
|
-
):
|
|
136
|
-
super().__init__(
|
|
137
|
-
status=status,
|
|
138
|
-
dataId=_serializeDataId(dataId),
|
|
139
|
-
taskLabel=taskLabel,
|
|
140
|
-
exitCode=exitCode,
|
|
141
|
-
exceptionInfo=exceptionInfo,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
@classmethod
|
|
145
|
-
def from_exception(
|
|
146
|
-
cls,
|
|
147
|
-
exception: Exception,
|
|
148
|
-
dataId: DataId,
|
|
149
|
-
taskLabel: str,
|
|
150
|
-
*,
|
|
151
|
-
exitCode: int | None = None,
|
|
152
|
-
) -> QuantumReport:
|
|
153
|
-
"""Construct report instance from an exception and other pieces of
|
|
154
|
-
data.
|
|
155
|
-
|
|
156
|
-
Parameters
|
|
157
|
-
----------
|
|
158
|
-
exception : `Exception`
|
|
159
|
-
Exception caught from processing quantum.
|
|
160
|
-
dataId : `~lsst.daf.butler.DataId`
|
|
161
|
-
Data ID of quantum.
|
|
162
|
-
taskLabel : `str`
|
|
163
|
-
Label of task.
|
|
164
|
-
exitCode : `int`, optional
|
|
165
|
-
Exit code for the process, used when it is known that the process
|
|
166
|
-
will exit with that exit code.
|
|
167
|
-
"""
|
|
168
|
-
return cls(
|
|
169
|
-
status=ExecutionStatus.FAILURE,
|
|
170
|
-
dataId=dataId,
|
|
171
|
-
taskLabel=taskLabel,
|
|
172
|
-
exitCode=exitCode,
|
|
173
|
-
exceptionInfo=ExceptionInfo.from_exception(exception),
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
@classmethod
|
|
177
|
-
def from_exit_code(
|
|
178
|
-
cls,
|
|
179
|
-
exitCode: int,
|
|
180
|
-
dataId: DataId,
|
|
181
|
-
taskLabel: str,
|
|
182
|
-
) -> QuantumReport:
|
|
183
|
-
"""Construct report instance from an exit code and other pieces of
|
|
184
|
-
data.
|
|
185
|
-
|
|
186
|
-
Parameters
|
|
187
|
-
----------
|
|
188
|
-
exitCode : `int`
|
|
189
|
-
The exit code of the subprocess.
|
|
190
|
-
dataId : `~lsst.daf.butler.DataId`
|
|
191
|
-
The quantum Data ID.
|
|
192
|
-
taskLabel : `str`
|
|
193
|
-
The task label.
|
|
194
|
-
"""
|
|
195
|
-
return cls(
|
|
196
|
-
status=ExecutionStatus.SUCCESS if exitCode == 0 else ExecutionStatus.FAILURE,
|
|
197
|
-
dataId=dataId,
|
|
198
|
-
taskLabel=taskLabel,
|
|
199
|
-
exitCode=exitCode,
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
class Report(pydantic.BaseModel):
|
|
204
|
-
"""Execution report for the whole job with one or few quanta."""
|
|
205
|
-
|
|
206
|
-
qgraphSummary: QgraphSummary
|
|
207
|
-
"""Summary report about QuantumGraph."""
|
|
208
|
-
|
|
209
|
-
status: ExecutionStatus = ExecutionStatus.SUCCESS
|
|
210
|
-
"""Job status."""
|
|
211
|
-
|
|
212
|
-
cmdLine: list[str] | None = None
|
|
213
|
-
"""Command line for the whole job."""
|
|
214
|
-
|
|
215
|
-
exitCode: int | None = None
|
|
216
|
-
"""Job exit code, this obviously cannot be set in pipetask."""
|
|
217
|
-
|
|
218
|
-
exceptionInfo: ExceptionInfo | None = None
|
|
219
|
-
"""Exception information if exception was raised."""
|
|
220
|
-
|
|
221
|
-
quantaReports: list[QuantumReport] = []
|
|
222
|
-
"""List of per-quantum reports, ordering is not specified. Some or all
|
|
223
|
-
quanta may not produce a report.
|
|
224
|
-
"""
|
|
225
|
-
|
|
226
|
-
# Always want to validate the default value for cmdLine so
|
|
227
|
-
# use a model_validator.
|
|
228
|
-
@pydantic.model_validator(mode="before")
|
|
229
|
-
@classmethod
|
|
230
|
-
def _set_cmdLine(cls, data: Any) -> Any:
|
|
231
|
-
if data.get("cmdLine") is None:
|
|
232
|
-
data["cmdLine"] = sys.argv
|
|
233
|
-
return data
|
|
234
|
-
|
|
235
|
-
def set_exception(self, exception: Exception) -> None:
|
|
236
|
-
"""Update exception information from an exception object.
|
|
237
|
-
|
|
238
|
-
Parameters
|
|
239
|
-
----------
|
|
240
|
-
exception : `Exception`
|
|
241
|
-
Exception to use to extract information from.
|
|
242
|
-
"""
|
|
243
|
-
self.exceptionInfo = ExceptionInfo.from_exception(exception)
|
|
60
|
+
@deprecated(
|
|
61
|
+
"The Report class has moved to lsst.pipe.base.quantum_reports. "
|
|
62
|
+
"This forwarding shim will be removed after v30.",
|
|
63
|
+
version="v30",
|
|
64
|
+
category=FutureWarning,
|
|
65
|
+
)
|
|
66
|
+
class Report(lsst.pipe.base.quantum_reports.Report): # noqa: D101
|
|
67
|
+
pass
|
|
@@ -25,271 +25,20 @@
|
|
|
25
25
|
# You should have received a copy of the GNU General Public License
|
|
26
26
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
27
|
|
|
28
|
+
__all__ = ("SeparablePipelineExecutor",)
|
|
28
29
|
|
|
29
|
-
from
|
|
30
|
+
from deprecated.sphinx import deprecated
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
"SeparablePipelineExecutor",
|
|
33
|
-
]
|
|
32
|
+
import lsst.pipe.base.separable_pipeline_executor
|
|
34
33
|
|
|
34
|
+
# TODO[DM-51962]: Remove this module.
|
|
35
35
|
|
|
36
|
-
import datetime
|
|
37
|
-
import getpass
|
|
38
|
-
import logging
|
|
39
|
-
from collections.abc import Iterable
|
|
40
|
-
from typing import Any
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
from .quantumGraphExecutor import QuantumGraphExecutor
|
|
51
|
-
from .singleQuantumExecutor import SingleQuantumExecutor
|
|
52
|
-
from .taskFactory import TaskFactory
|
|
53
|
-
|
|
54
|
-
_LOG = logging.getLogger(__name__)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class SeparablePipelineExecutor:
|
|
58
|
-
"""An executor that allows each step of pipeline execution to be
|
|
59
|
-
run independently.
|
|
60
|
-
|
|
61
|
-
The executor can run any or all of the following steps:
|
|
62
|
-
|
|
63
|
-
* pre-execution initialization
|
|
64
|
-
* pipeline building
|
|
65
|
-
* quantum graph generation
|
|
66
|
-
* quantum graph execution
|
|
67
|
-
|
|
68
|
-
Any of these steps can also be handed off to external code without
|
|
69
|
-
compromising the remaining ones.
|
|
70
|
-
|
|
71
|
-
Parameters
|
|
72
|
-
----------
|
|
73
|
-
butler : `lsst.daf.butler.Butler`
|
|
74
|
-
A Butler whose ``collections`` and ``run`` attributes contain the input
|
|
75
|
-
and output collections to use for processing.
|
|
76
|
-
clobber_output : `bool`, optional
|
|
77
|
-
If set, the pipeline execution overwrites existing output files.
|
|
78
|
-
Otherwise, any conflict between existing and new outputs is an error.
|
|
79
|
-
skip_existing_in : iterable [`str`], optional
|
|
80
|
-
If not empty, the pipeline execution searches the listed collections
|
|
81
|
-
for existing outputs, and skips any quanta that have run to completion
|
|
82
|
-
(or have no work to do). Otherwise, all tasks are attempted (subject to
|
|
83
|
-
``clobber_output``).
|
|
84
|
-
task_factory : `lsst.pipe.base.TaskFactory`, optional
|
|
85
|
-
A custom task factory for use in pre-execution and execution. By
|
|
86
|
-
default, a new instance of `lsst.ctrl.mpexec.TaskFactory` is used.
|
|
87
|
-
resources : `~lsst.pipe.base.ExecutionResources`
|
|
88
|
-
The resources available to each quantum being executed.
|
|
89
|
-
raise_on_partial_outputs : `bool`, optional
|
|
90
|
-
If `True` raise exceptions chained by
|
|
91
|
-
`lsst.pipe.base.AnnotatedPartialOutputError` immediately, instead of
|
|
92
|
-
considering the partial result a success and continuing to run
|
|
93
|
-
downstream tasks.
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
def __init__(
|
|
97
|
-
self,
|
|
98
|
-
butler: Butler,
|
|
99
|
-
clobber_output: bool = False,
|
|
100
|
-
skip_existing_in: Iterable[str] | None = None,
|
|
101
|
-
task_factory: lsst.pipe.base.TaskFactory | None = None,
|
|
102
|
-
resources: lsst.pipe.base.ExecutionResources | None = None,
|
|
103
|
-
raise_on_partial_outputs: bool = True,
|
|
104
|
-
):
|
|
105
|
-
self._butler = Butler.from_config(
|
|
106
|
-
butler=butler, collections=butler.collections.defaults, run=butler.run
|
|
107
|
-
)
|
|
108
|
-
if not self._butler.collections.defaults:
|
|
109
|
-
raise ValueError("Butler must specify input collections for pipeline.")
|
|
110
|
-
if not self._butler.run:
|
|
111
|
-
raise ValueError("Butler must specify output run for pipeline.")
|
|
112
|
-
|
|
113
|
-
self._clobber_output = clobber_output
|
|
114
|
-
self._skip_existing_in = list(skip_existing_in) if skip_existing_in else []
|
|
115
|
-
|
|
116
|
-
self._task_factory = task_factory if task_factory else TaskFactory()
|
|
117
|
-
self.resources = resources
|
|
118
|
-
self.raise_on_partial_outputs = raise_on_partial_outputs
|
|
119
|
-
|
|
120
|
-
def pre_execute_qgraph(
|
|
121
|
-
self,
|
|
122
|
-
graph: lsst.pipe.base.QuantumGraph,
|
|
123
|
-
register_dataset_types: bool = False,
|
|
124
|
-
save_init_outputs: bool = True,
|
|
125
|
-
save_versions: bool = True,
|
|
126
|
-
) -> None:
|
|
127
|
-
"""Run pre-execution initialization.
|
|
128
|
-
|
|
129
|
-
This method will be deprecated after DM-38041, to be replaced with a
|
|
130
|
-
method that takes either a `~lsst.pipe.base.Pipeline` or a
|
|
131
|
-
``ResolvedPipelineGraph`` instead of a `~lsst.pipe.base.QuantumGraph`.
|
|
132
|
-
|
|
133
|
-
Parameters
|
|
134
|
-
----------
|
|
135
|
-
graph : `lsst.pipe.base.QuantumGraph`
|
|
136
|
-
The quantum graph defining the pipeline and datasets to
|
|
137
|
-
be initialized.
|
|
138
|
-
register_dataset_types : `bool`, optional
|
|
139
|
-
If `True`, register all output dataset types from the pipeline
|
|
140
|
-
represented by ``graph``.
|
|
141
|
-
save_init_outputs : `bool`, optional
|
|
142
|
-
If `True`, create init-output datasets in this object's output run.
|
|
143
|
-
save_versions : `bool`, optional
|
|
144
|
-
If `True`, save a package versions dataset.
|
|
145
|
-
"""
|
|
146
|
-
pre_exec_init = PreExecInit(self._butler, self._task_factory, extendRun=self._clobber_output)
|
|
147
|
-
pre_exec_init.initialize(
|
|
148
|
-
graph=graph,
|
|
149
|
-
saveInitOutputs=save_init_outputs,
|
|
150
|
-
registerDatasetTypes=register_dataset_types,
|
|
151
|
-
saveVersions=save_versions,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
def make_pipeline(self, pipeline_uri: str | lsst.resources.ResourcePath) -> lsst.pipe.base.Pipeline:
|
|
155
|
-
"""Build a pipeline from pipeline and configuration information.
|
|
156
|
-
|
|
157
|
-
Parameters
|
|
158
|
-
----------
|
|
159
|
-
pipeline_uri : `str` or `lsst.resources.ResourcePath`
|
|
160
|
-
URI to a file containing a pipeline definition. A URI fragment may
|
|
161
|
-
be used to specify a subset of the pipeline, as described in
|
|
162
|
-
:ref:`pipeline-running-intro`.
|
|
163
|
-
|
|
164
|
-
Returns
|
|
165
|
-
-------
|
|
166
|
-
pipeline : `lsst.pipe.base.Pipeline`
|
|
167
|
-
The fully-built pipeline.
|
|
168
|
-
"""
|
|
169
|
-
return lsst.pipe.base.Pipeline.from_uri(pipeline_uri)
|
|
170
|
-
|
|
171
|
-
def make_quantum_graph(
|
|
172
|
-
self,
|
|
173
|
-
pipeline: lsst.pipe.base.Pipeline,
|
|
174
|
-
where: str = "",
|
|
175
|
-
*,
|
|
176
|
-
builder_class: type[QuantumGraphBuilder] = AllDimensionsQuantumGraphBuilder,
|
|
177
|
-
attach_datastore_records: bool = False,
|
|
178
|
-
**kwargs: Any,
|
|
179
|
-
) -> lsst.pipe.base.QuantumGraph:
|
|
180
|
-
"""Build a quantum graph from a pipeline and input datasets.
|
|
181
|
-
|
|
182
|
-
Parameters
|
|
183
|
-
----------
|
|
184
|
-
pipeline : `lsst.pipe.base.Pipeline`
|
|
185
|
-
The pipeline for which to generate a quantum graph.
|
|
186
|
-
where : `str`, optional
|
|
187
|
-
A data ID query that constrains the quanta generated. Must not be
|
|
188
|
-
provided if a custom ``builder_class`` is given and that class does
|
|
189
|
-
not accept ``where`` as a construction argument.
|
|
190
|
-
builder_class : `type` [ \
|
|
191
|
-
`lsst.pipe.base.quantum_graph_builder.QuantumGraphBuilder` ], \
|
|
192
|
-
optional
|
|
193
|
-
Quantum graph builder implementation. Ignored if ``builder`` is
|
|
194
|
-
provided.
|
|
195
|
-
attach_datastore_records : `bool`, optional
|
|
196
|
-
Whether to attach datastore records. These are currently used only
|
|
197
|
-
by `lsst.daf.butler.QuantumBackedButler`, which is not used by
|
|
198
|
-
`SeparablePipelineExecutor` for execution.
|
|
199
|
-
**kwargs
|
|
200
|
-
Additional keyword arguments are forwarded to ``builder_class``
|
|
201
|
-
when a quantum graph builder instance is constructed. All
|
|
202
|
-
arguments accepted by the
|
|
203
|
-
`~lsst.pipe.base.quantum_graph_builder.QuantumGraphBuilder` base
|
|
204
|
-
class are provided automatically (from explicit arguments to this
|
|
205
|
-
method and executor attributes) and do not need to be included
|
|
206
|
-
as keyword arguments.
|
|
207
|
-
|
|
208
|
-
Returns
|
|
209
|
-
-------
|
|
210
|
-
graph : `lsst.pipe.base.QuantumGraph`
|
|
211
|
-
The quantum graph for ``pipeline`` as run on the datasets
|
|
212
|
-
identified by ``where``.
|
|
213
|
-
|
|
214
|
-
Notes
|
|
215
|
-
-----
|
|
216
|
-
This method does no special handling of empty quantum graphs. If
|
|
217
|
-
needed, clients can use `len` to test if the returned graph is empty.
|
|
218
|
-
"""
|
|
219
|
-
metadata = {
|
|
220
|
-
"input": self._butler.collections.defaults,
|
|
221
|
-
"output_run": self._butler.run,
|
|
222
|
-
"skip_existing_in": self._skip_existing_in,
|
|
223
|
-
"skip_existing": bool(self._skip_existing_in),
|
|
224
|
-
"data_query": where,
|
|
225
|
-
"user": getpass.getuser(),
|
|
226
|
-
"time": str(datetime.datetime.now()),
|
|
227
|
-
}
|
|
228
|
-
if where:
|
|
229
|
-
# Only pass 'where' if it's actually provided, since some
|
|
230
|
-
# QuantumGraphBuilder subclasses may not accept it.
|
|
231
|
-
kwargs["where"] = where
|
|
232
|
-
qg_builder = builder_class(
|
|
233
|
-
pipeline.to_graph(),
|
|
234
|
-
self._butler,
|
|
235
|
-
skip_existing_in=self._skip_existing_in,
|
|
236
|
-
clobber=self._clobber_output,
|
|
237
|
-
**kwargs,
|
|
238
|
-
)
|
|
239
|
-
graph = qg_builder.build(metadata=metadata, attach_datastore_records=attach_datastore_records)
|
|
240
|
-
_LOG.info(
|
|
241
|
-
"QuantumGraph contains %d quanta for %d tasks, graph ID: %r",
|
|
242
|
-
len(graph),
|
|
243
|
-
len(graph.taskGraph),
|
|
244
|
-
graph.graphID,
|
|
245
|
-
)
|
|
246
|
-
return graph
|
|
247
|
-
|
|
248
|
-
def run_pipeline(
|
|
249
|
-
self,
|
|
250
|
-
graph: lsst.pipe.base.QuantumGraph,
|
|
251
|
-
fail_fast: bool = False,
|
|
252
|
-
graph_executor: QuantumGraphExecutor | None = None,
|
|
253
|
-
num_proc: int = 1,
|
|
254
|
-
) -> None:
|
|
255
|
-
"""Run a pipeline in the form of a prepared quantum graph.
|
|
256
|
-
|
|
257
|
-
Pre-execution initialization must have already been run;
|
|
258
|
-
see `pre_execute_qgraph`.
|
|
259
|
-
|
|
260
|
-
Parameters
|
|
261
|
-
----------
|
|
262
|
-
graph : `lsst.pipe.base.QuantumGraph`
|
|
263
|
-
The pipeline and datasets to execute.
|
|
264
|
-
fail_fast : `bool`, optional
|
|
265
|
-
If `True`, abort all execution if any task fails when
|
|
266
|
-
running with multiple processes. Only used with the default graph
|
|
267
|
-
executor).
|
|
268
|
-
graph_executor : `lsst.ctrl.mpexec.QuantumGraphExecutor`, optional
|
|
269
|
-
A custom graph executor. By default, a new instance of
|
|
270
|
-
`lsst.ctrl.mpexec.MPGraphExecutor` is used.
|
|
271
|
-
num_proc : `int`, optional
|
|
272
|
-
The number of processes that can be used to run the pipeline. The
|
|
273
|
-
default value ensures that no subprocess is created. Only used with
|
|
274
|
-
the default graph executor.
|
|
275
|
-
"""
|
|
276
|
-
if not graph_executor:
|
|
277
|
-
quantum_executor = SingleQuantumExecutor(
|
|
278
|
-
self._butler,
|
|
279
|
-
self._task_factory,
|
|
280
|
-
skipExistingIn=self._skip_existing_in,
|
|
281
|
-
clobberOutputs=self._clobber_output,
|
|
282
|
-
resources=self.resources,
|
|
283
|
-
raise_on_partial_outputs=self.raise_on_partial_outputs,
|
|
284
|
-
)
|
|
285
|
-
graph_executor = MPGraphExecutor(
|
|
286
|
-
numProc=num_proc,
|
|
287
|
-
timeout=2_592_000.0, # In practice, timeout is never helpful; set to 30 days.
|
|
288
|
-
quantumExecutor=quantum_executor,
|
|
289
|
-
failFast=fail_fast,
|
|
290
|
-
)
|
|
291
|
-
# Have to reset connection pool to avoid sharing connections with
|
|
292
|
-
# forked processes.
|
|
293
|
-
self._butler.registry.resetConnectionPool()
|
|
294
|
-
|
|
295
|
-
graph_executor.execute(graph)
|
|
37
|
+
@deprecated(
|
|
38
|
+
"The SeparablePipelineExecutor class has moved to lsst.pipe.base.separable_pipeline_executor. "
|
|
39
|
+
"This forwarding shim will be removed after v30.",
|
|
40
|
+
version="v30",
|
|
41
|
+
category=FutureWarning,
|
|
42
|
+
)
|
|
43
|
+
class SeparablePipelineExecutor(lsst.pipe.base.separable_pipeline_executor.SeparablePipelineExecutor): # noqa: D101
|
|
44
|
+
pass
|
lsst/ctrl/mpexec/showInfo.py
CHANGED
|
@@ -46,7 +46,7 @@ from lsst.pipe.base.pipeline_graph import visualization
|
|
|
46
46
|
|
|
47
47
|
from . import util
|
|
48
48
|
from ._pipeline_graph_factory import PipelineGraphFactory
|
|
49
|
-
from .
|
|
49
|
+
from .cli.butler_factory import ButlerFactory
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class _FilteredStream:
|
|
@@ -385,7 +385,7 @@ class ShowInfo:
|
|
|
385
385
|
for compName, compUri in components.items():
|
|
386
386
|
print(f" {compName}: {compUri}", file=self.stream)
|
|
387
387
|
|
|
388
|
-
butler =
|
|
388
|
+
butler = ButlerFactory.make_read_butler(args)
|
|
389
389
|
for node in graph:
|
|
390
390
|
print(f"Quantum {node.nodeId}: {node.taskDef.taskName}", file=self.stream)
|
|
391
391
|
print(" inputs:", file=self.stream)
|