lsst-ctrl-mpexec 29.2025.3400__py3-none-any.whl → 29.2025.3600__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 +0 -1
- lsst/ctrl/mpexec/cli/butler_factory.py +253 -95
- lsst/ctrl/mpexec/cli/cmd/commands.py +2 -2
- lsst/ctrl/mpexec/cli/opt/optionGroups.py +0 -1
- lsst/ctrl/mpexec/cli/opt/options.py +0 -7
- lsst/ctrl/mpexec/cli/script/pre_exec_init_qbb.py +25 -12
- lsst/ctrl/mpexec/cli/script/qgraph.py +177 -89
- lsst/ctrl/mpexec/cli/script/run.py +211 -99
- lsst/ctrl/mpexec/cli/script/run_qbb.py +166 -31
- lsst/ctrl/mpexec/cli/utils.py +49 -0
- lsst/ctrl/mpexec/showInfo.py +17 -15
- lsst/ctrl/mpexec/version.py +1 -1
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/METADATA +1 -1
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/RECORD +22 -23
- lsst/ctrl/mpexec/cmdLineFwk.py +0 -534
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/WHEEL +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/entry_points.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/licenses/COPYRIGHT +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/licenses/LICENSE +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/licenses/bsd_license.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/licenses/gpl-v3.0.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/top_level.txt +0 -0
- {lsst_ctrl_mpexec-29.2025.3400.dist-info → lsst_ctrl_mpexec-29.2025.3600.dist-info}/zip-safe +0 -0
|
@@ -25,47 +25,68 @@
|
|
|
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
|
-
import
|
|
29
|
-
from types import SimpleNamespace
|
|
28
|
+
from __future__ import annotations
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
__all__ = ("qgraph",)
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
import uuid
|
|
33
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
34
|
+
from typing import TYPE_CHECKING
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
from astropy.table import Table
|
|
36
37
|
|
|
38
|
+
from lsst.pipe.base import BuildId, QuantumGraph
|
|
39
|
+
from lsst.pipe.base.all_dimensions_quantum_graph_builder import (
|
|
40
|
+
AllDimensionsQuantumGraphBuilder,
|
|
41
|
+
DatasetQueryConstraintVariant,
|
|
42
|
+
)
|
|
43
|
+
from lsst.pipe.base.dot_tools import graph2dot
|
|
44
|
+
from lsst.pipe.base.mermaid_tools import graph2mermaid
|
|
45
|
+
from lsst.resources import ResourcePath, ResourcePathExpression
|
|
46
|
+
from lsst.utils.iteration import ensure_iterable
|
|
47
|
+
from lsst.utils.logging import getLogger
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
from ..._pipeline_graph_factory import PipelineGraphFactory
|
|
50
|
+
from ...showInfo import ShowInfo
|
|
51
|
+
from ..butler_factory import ButlerFactory
|
|
52
|
+
from ..utils import summarize_quantum_graph
|
|
53
|
+
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from lsst.pipe.base.tests.mocks import ForcedFailure # this monkey patches; only import for annotation!
|
|
56
|
+
|
|
57
|
+
_LOG = getLogger(__name__)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def qgraph(
|
|
61
|
+
pipeline_graph_factory: PipelineGraphFactory | None,
|
|
40
62
|
*,
|
|
41
|
-
qgraph,
|
|
42
|
-
qgraph_id,
|
|
43
|
-
qgraph_node_id,
|
|
44
|
-
qgraph_datastore_records,
|
|
45
|
-
skip_existing_in,
|
|
46
|
-
skip_existing,
|
|
47
|
-
save_qgraph,
|
|
48
|
-
qgraph_dot,
|
|
49
|
-
qgraph_mermaid,
|
|
50
|
-
butler_config,
|
|
51
|
-
input,
|
|
52
|
-
output,
|
|
53
|
-
output_run,
|
|
54
|
-
extend_run,
|
|
55
|
-
replace_run,
|
|
56
|
-
prune_replaced,
|
|
57
|
-
data_query,
|
|
58
|
-
data_id_table
|
|
59
|
-
show,
|
|
60
|
-
clobber_outputs,
|
|
61
|
-
dataset_query_constraint,
|
|
62
|
-
rebase,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
):
|
|
63
|
+
qgraph: QuantumGraph | ResourcePathExpression | None,
|
|
64
|
+
qgraph_id: str | None,
|
|
65
|
+
qgraph_node_id: Iterable[uuid.UUID | str] | None,
|
|
66
|
+
qgraph_datastore_records: bool,
|
|
67
|
+
skip_existing_in: Iterable[str] | None,
|
|
68
|
+
skip_existing: bool,
|
|
69
|
+
save_qgraph: ResourcePathExpression | None,
|
|
70
|
+
qgraph_dot: str | None,
|
|
71
|
+
qgraph_mermaid: str | None,
|
|
72
|
+
butler_config: ResourcePathExpression,
|
|
73
|
+
input: Iterable[str] | str,
|
|
74
|
+
output: str | None,
|
|
75
|
+
output_run: str | None,
|
|
76
|
+
extend_run: bool,
|
|
77
|
+
replace_run: bool,
|
|
78
|
+
prune_replaced: str | None,
|
|
79
|
+
data_query: str | None,
|
|
80
|
+
data_id_table: Iterable[ResourcePathExpression],
|
|
81
|
+
show: ShowInfo,
|
|
82
|
+
clobber_outputs: bool,
|
|
83
|
+
dataset_query_constraint: str,
|
|
84
|
+
rebase: bool,
|
|
85
|
+
mock: bool = False,
|
|
86
|
+
unmocked_dataset_types: Sequence[str],
|
|
87
|
+
mock_failure: Mapping[str, ForcedFailure],
|
|
88
|
+
**kwargs: object,
|
|
89
|
+
) -> QuantumGraph | None:
|
|
69
90
|
"""Implement the command line interface `pipetask qgraph` subcommand.
|
|
70
91
|
|
|
71
92
|
Should only be called by command line tools and unit test code that test
|
|
@@ -73,50 +94,47 @@ def qgraph( # type: ignore
|
|
|
73
94
|
|
|
74
95
|
Parameters
|
|
75
96
|
----------
|
|
76
|
-
pipeline_graph_factory : `..PipelineGraphFactory` or None
|
|
97
|
+
pipeline_graph_factory : `..PipelineGraphFactory` or `None`
|
|
77
98
|
A factory that holds the pipeline and can produce a pipeline graph.
|
|
78
99
|
If this is not `None` then `qgraph` should be `None`.
|
|
79
|
-
qgraph : `
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
be `None`.
|
|
100
|
+
qgraph : `lsst.pipe.base.QuantumGraph`, convertible to \
|
|
101
|
+
`lsst.resources.ResourcePath` or `None`
|
|
102
|
+
URI location for a serialized quantum graph definition. If this option
|
|
103
|
+
is not `None` then ``pipeline_graph_factory`` should be `None`.
|
|
83
104
|
qgraph_id : `str` or `None`
|
|
84
105
|
Quantum graph identifier, if specified must match the identifier of the
|
|
85
106
|
graph loaded from a file. Ignored if graph is not loaded from a file.
|
|
86
|
-
qgraph_node_id : `
|
|
107
|
+
qgraph_node_id : `~collections.abc.Iterable` [`str` | `uuid.UUID`] or \
|
|
108
|
+
`None`
|
|
87
109
|
Only load a specified set of nodes if graph is loaded from a file,
|
|
88
110
|
nodes are identified by integer IDs.
|
|
89
111
|
qgraph_datastore_records : `bool`
|
|
90
|
-
If True then include datastore records into generated quanta.
|
|
91
|
-
skip_existing_in : `
|
|
112
|
+
If `True` then include datastore records into generated quanta.
|
|
113
|
+
skip_existing_in : `~collections.abc.Iterable` [ `str` ] or `None`
|
|
92
114
|
Accepts list of collections, if all Quantum outputs already exist in
|
|
93
115
|
the specified list of collections then that Quantum will be excluded
|
|
94
116
|
from the QuantumGraph.
|
|
95
117
|
skip_existing : `bool`
|
|
96
118
|
Appends output RUN collection to the ``skip_existing_in`` list.
|
|
97
|
-
save_qgraph : `
|
|
98
|
-
URI location for
|
|
99
|
-
pickle file.
|
|
119
|
+
save_qgraph : convertible to `lsst.resources.ResourcePath` or `None`
|
|
120
|
+
URI location for saving the quantum graph.
|
|
100
121
|
qgraph_dot : `str` or `None`
|
|
101
122
|
Path location for storing GraphViz DOT representation of a quantum
|
|
102
123
|
graph.
|
|
103
124
|
qgraph_mermaid : `str` or `None`
|
|
104
125
|
Path location for storing Mermaid representation of a quantum graph.
|
|
105
|
-
butler_config :
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
pairs used to init or update the `lsst.daf.butler.Config` instance. If
|
|
109
|
-
`Config`, it is the object used to configure a Butler.
|
|
110
|
-
input : `list` [ `str` ]
|
|
126
|
+
butler_config : convertible to `lsst.resources.ResourcePath`
|
|
127
|
+
Path to butler repository configuration.
|
|
128
|
+
input : `~collections.abc.Iterable` [ `str` ] or `None`
|
|
111
129
|
List of names of the input collection(s).
|
|
112
|
-
output : `str`
|
|
130
|
+
output : `str` or `None`
|
|
113
131
|
Name of the output CHAINED collection. This may either be an existing
|
|
114
132
|
CHAINED collection to use as both input and output (if `input` is
|
|
115
133
|
`None`), or a new CHAINED collection created to include all inputs
|
|
116
134
|
(if `input` is not `None`). In both cases, the collection's children
|
|
117
135
|
will start with an output RUN collection that directly holds all new
|
|
118
136
|
datasets (see `output_run`).
|
|
119
|
-
output_run : `str`
|
|
137
|
+
output_run : `str` or `None`
|
|
120
138
|
Name of the new output RUN collection. If not provided then `output`
|
|
121
139
|
must be provided and a new RUN collection will be created by appending
|
|
122
140
|
a timestamp to the value passed with `output`. If this collection
|
|
@@ -139,7 +157,8 @@ def qgraph( # type: ignore
|
|
|
139
157
|
``replace_run`` to be `True`.
|
|
140
158
|
data_query : `str`
|
|
141
159
|
User query selection expression.
|
|
142
|
-
data_id_table : `~collections.abc.Iterable` [
|
|
160
|
+
data_id_table : `~collections.abc.Iterable` [convertible to \
|
|
161
|
+
`lsst.resources.ResourcePath`]
|
|
143
162
|
Paths to data ID tables to join in.
|
|
144
163
|
show : `lsst.ctrl.mpexec.showInfo.ShowInfo`
|
|
145
164
|
Descriptions of what to dump to stdout.
|
|
@@ -154,61 +173,130 @@ def qgraph( # type: ignore
|
|
|
154
173
|
rebase : `bool`
|
|
155
174
|
If `True` then reset output collection chain if it is inconsistent with
|
|
156
175
|
the ``inputs``.
|
|
157
|
-
|
|
158
|
-
Controls if the headerData of a QuantumGraph should be printed to the
|
|
159
|
-
terminal. Defaults to False.
|
|
160
|
-
mock : `bool`, optional
|
|
176
|
+
mock : `bool`
|
|
161
177
|
If True, use a mocked version of the pipeline.
|
|
162
178
|
unmocked_dataset_types : `collections.abc.Sequence` [ `str` ], optional
|
|
163
179
|
List of overall-input dataset types that should not be mocked.
|
|
164
|
-
mock_failure : `~collections.abc.
|
|
165
|
-
|
|
166
|
-
**kwargs : `
|
|
180
|
+
mock_failure : `~collections.abc.Mapping`
|
|
181
|
+
Quanta that should raise exceptions.
|
|
182
|
+
**kwargs : `object`
|
|
167
183
|
Ignored; click commands may accept options for more than one script
|
|
168
184
|
function and pass all the option kwargs to each of the script functions
|
|
169
185
|
which ignore these unused kwargs.
|
|
170
186
|
|
|
171
187
|
Returns
|
|
172
188
|
-------
|
|
173
|
-
qgraph : `lsst.pipe.base.QuantumGraph`
|
|
189
|
+
qgraph : `lsst.pipe.base.QuantumGraph` or `None`
|
|
174
190
|
The qgraph object that was created.
|
|
175
191
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
192
|
+
# make sure that --extend-run always enables --skip-existing
|
|
193
|
+
if extend_run:
|
|
194
|
+
skip_existing = True
|
|
195
|
+
|
|
196
|
+
skip_existing_in = tuple(skip_existing_in) if skip_existing_in is not None else ()
|
|
197
|
+
if data_query is None:
|
|
198
|
+
data_query = ""
|
|
199
|
+
inputs = list(ensure_iterable(input)) if input else []
|
|
200
|
+
del input
|
|
201
|
+
|
|
202
|
+
butler, collections, run = ButlerFactory.make_butler_and_collections(
|
|
203
|
+
butler_config,
|
|
187
204
|
output=output,
|
|
188
205
|
output_run=output_run,
|
|
206
|
+
inputs=inputs,
|
|
189
207
|
extend_run=extend_run,
|
|
208
|
+
rebase=rebase,
|
|
190
209
|
replace_run=replace_run,
|
|
191
210
|
prune_replaced=prune_replaced,
|
|
192
|
-
data_query=data_query,
|
|
193
|
-
data_id_table=data_id_table,
|
|
194
|
-
skip_existing_in=skip_existing_in,
|
|
195
|
-
skip_existing=skip_existing,
|
|
196
|
-
clobber_outputs=clobber_outputs,
|
|
197
|
-
dataset_query_constraint=dataset_query_constraint,
|
|
198
|
-
rebase=rebase,
|
|
199
|
-
show_qgraph_header=show_qgraph_header,
|
|
200
|
-
mock=mock,
|
|
201
|
-
unmocked_dataset_types=list(unmocked_dataset_types),
|
|
202
|
-
mock_failure=mock_failure,
|
|
203
211
|
)
|
|
204
212
|
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
if skip_existing and run:
|
|
214
|
+
skip_existing_in += (run,)
|
|
215
|
+
|
|
216
|
+
if qgraph is not None:
|
|
217
|
+
if not isinstance(qgraph, QuantumGraph):
|
|
218
|
+
# click passes empty tuple as default value for qgraph_node_id
|
|
219
|
+
nodes = qgraph_node_id or None
|
|
220
|
+
qgraph = QuantumGraph.loadUri(
|
|
221
|
+
qgraph,
|
|
222
|
+
butler.dimensions,
|
|
223
|
+
nodes=nodes,
|
|
224
|
+
graphID=BuildId(qgraph_id) if qgraph_id is not None else None,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# pipeline can not be provided in this case
|
|
228
|
+
if pipeline_graph_factory:
|
|
229
|
+
raise ValueError(
|
|
230
|
+
"Pipeline must not be given when quantum graph is read from "
|
|
231
|
+
f"file: {bool(pipeline_graph_factory)}"
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
if pipeline_graph_factory is None:
|
|
235
|
+
raise ValueError("Pipeline must be given when quantum graph is not read from file.")
|
|
236
|
+
# We can't resolve the pipeline graph if we're mocking until after
|
|
237
|
+
# we've done the mocking (and the QG build will resolve on its own
|
|
238
|
+
# anyway).
|
|
239
|
+
pipeline_graph = pipeline_graph_factory(resolve=False)
|
|
240
|
+
if mock:
|
|
241
|
+
from lsst.pipe.base.tests.mocks import mock_pipeline_graph
|
|
242
|
+
|
|
243
|
+
pipeline_graph = mock_pipeline_graph(
|
|
244
|
+
pipeline_graph,
|
|
245
|
+
unmocked_dataset_types=unmocked_dataset_types,
|
|
246
|
+
force_failures=mock_failure,
|
|
247
|
+
)
|
|
248
|
+
data_id_tables = []
|
|
249
|
+
for table_file in data_id_table:
|
|
250
|
+
with ResourcePath(table_file).as_local() as local_path:
|
|
251
|
+
table = Table.read(local_path.ospath)
|
|
252
|
+
# Add the filename to the metadata for more logging
|
|
253
|
+
# information down in the QG builder.
|
|
254
|
+
table.meta["filename"] = table_file
|
|
255
|
+
data_id_tables.append(table)
|
|
256
|
+
# make execution plan (a.k.a. DAG) for pipeline
|
|
257
|
+
graph_builder = AllDimensionsQuantumGraphBuilder(
|
|
258
|
+
pipeline_graph,
|
|
259
|
+
butler,
|
|
260
|
+
where=data_query,
|
|
261
|
+
skip_existing_in=skip_existing_in,
|
|
262
|
+
clobber=clobber_outputs,
|
|
263
|
+
dataset_query_constraint=DatasetQueryConstraintVariant.fromExpression(dataset_query_constraint),
|
|
264
|
+
input_collections=collections,
|
|
265
|
+
output_run=run,
|
|
266
|
+
data_id_tables=data_id_tables,
|
|
267
|
+
)
|
|
268
|
+
# accumulate metadata
|
|
269
|
+
metadata = {
|
|
270
|
+
"input": inputs,
|
|
271
|
+
"output": output,
|
|
272
|
+
"butler_argument": str(butler_config),
|
|
273
|
+
"output_run": run,
|
|
274
|
+
"extend_run": extend_run,
|
|
275
|
+
"skip_existing_in": skip_existing_in,
|
|
276
|
+
"skip_existing": skip_existing,
|
|
277
|
+
"data_query": data_query,
|
|
278
|
+
}
|
|
279
|
+
assert run is not None, "Butler output run collection must be defined"
|
|
280
|
+
qgraph = graph_builder.build(metadata, attach_datastore_records=qgraph_datastore_records)
|
|
207
281
|
|
|
208
|
-
if qgraph
|
|
282
|
+
if len(qgraph) == 0:
|
|
283
|
+
# Nothing to do.
|
|
209
284
|
return None
|
|
285
|
+
summarize_quantum_graph(qgraph)
|
|
286
|
+
|
|
287
|
+
if save_qgraph:
|
|
288
|
+
_LOG.verbose("Writing QuantumGraph to %r.", save_qgraph)
|
|
289
|
+
qgraph.saveUri(save_qgraph)
|
|
290
|
+
|
|
291
|
+
if qgraph_dot:
|
|
292
|
+
_LOG.verbose("Writing quantum graph DOT visualization to %r.", qgraph_dot)
|
|
293
|
+
graph2dot(qgraph, qgraph_dot)
|
|
294
|
+
|
|
295
|
+
if qgraph_mermaid:
|
|
296
|
+
_LOG.verbose("Writing quantum graph Mermaid visualization to %r.", qgraph_mermaid)
|
|
297
|
+
graph2mermaid(qgraph, qgraph_mermaid)
|
|
210
298
|
|
|
211
299
|
# optionally dump some info.
|
|
212
|
-
show.show_graph_info(qgraph,
|
|
300
|
+
show.show_graph_info(qgraph, butler_config)
|
|
213
301
|
|
|
214
302
|
return qgraph
|