lsst-pipe-base 30.0.0rc2__py3-none-any.whl → 30.0.1rc1__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/pipe/base/_instrument.py +31 -20
- lsst/pipe/base/_quantumContext.py +3 -3
- lsst/pipe/base/_status.py +43 -10
- lsst/pipe/base/_task_metadata.py +2 -2
- lsst/pipe/base/all_dimensions_quantum_graph_builder.py +8 -3
- lsst/pipe/base/automatic_connection_constants.py +20 -1
- lsst/pipe/base/cli/cmd/__init__.py +18 -2
- lsst/pipe/base/cli/cmd/commands.py +149 -4
- lsst/pipe/base/connectionTypes.py +72 -160
- lsst/pipe/base/connections.py +6 -9
- lsst/pipe/base/execution_reports.py +0 -5
- lsst/pipe/base/graph/graph.py +11 -10
- lsst/pipe/base/graph/quantumNode.py +4 -4
- lsst/pipe/base/graph_walker.py +8 -10
- lsst/pipe/base/log_capture.py +40 -80
- lsst/pipe/base/log_on_close.py +76 -0
- lsst/pipe/base/mp_graph_executor.py +51 -15
- lsst/pipe/base/pipeline.py +5 -6
- lsst/pipe/base/pipelineIR.py +2 -8
- lsst/pipe/base/pipelineTask.py +5 -7
- lsst/pipe/base/pipeline_graph/_dataset_types.py +2 -2
- lsst/pipe/base/pipeline_graph/_edges.py +32 -22
- lsst/pipe/base/pipeline_graph/_mapping_views.py +4 -7
- lsst/pipe/base/pipeline_graph/_pipeline_graph.py +14 -7
- lsst/pipe/base/pipeline_graph/expressions.py +2 -2
- lsst/pipe/base/pipeline_graph/io.py +7 -10
- lsst/pipe/base/pipeline_graph/visualization/_dot.py +13 -12
- lsst/pipe/base/pipeline_graph/visualization/_layout.py +16 -18
- lsst/pipe/base/pipeline_graph/visualization/_merge.py +4 -7
- lsst/pipe/base/pipeline_graph/visualization/_printer.py +10 -10
- lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +7 -0
- lsst/pipe/base/prerequisite_helpers.py +2 -1
- lsst/pipe/base/quantum_graph/_common.py +19 -20
- lsst/pipe/base/quantum_graph/_multiblock.py +37 -31
- lsst/pipe/base/quantum_graph/_predicted.py +113 -15
- lsst/pipe/base/quantum_graph/_provenance.py +1136 -45
- lsst/pipe/base/quantum_graph/aggregator/__init__.py +0 -1
- lsst/pipe/base/quantum_graph/aggregator/_communicators.py +204 -289
- lsst/pipe/base/quantum_graph/aggregator/_config.py +87 -9
- lsst/pipe/base/quantum_graph/aggregator/_ingester.py +13 -12
- lsst/pipe/base/quantum_graph/aggregator/_scanner.py +49 -235
- lsst/pipe/base/quantum_graph/aggregator/_structs.py +6 -116
- lsst/pipe/base/quantum_graph/aggregator/_supervisor.py +29 -39
- lsst/pipe/base/quantum_graph/aggregator/_workers.py +303 -0
- lsst/pipe/base/quantum_graph/aggregator/_writer.py +34 -351
- lsst/pipe/base/quantum_graph/formatter.py +171 -0
- lsst/pipe/base/quantum_graph/ingest_graph.py +413 -0
- lsst/pipe/base/quantum_graph/visualization.py +5 -1
- lsst/pipe/base/quantum_graph_builder.py +33 -9
- lsst/pipe/base/quantum_graph_executor.py +116 -13
- lsst/pipe/base/quantum_graph_skeleton.py +31 -35
- lsst/pipe/base/quantum_provenance_graph.py +29 -12
- lsst/pipe/base/separable_pipeline_executor.py +19 -3
- lsst/pipe/base/single_quantum_executor.py +67 -42
- lsst/pipe/base/struct.py +4 -0
- lsst/pipe/base/testUtils.py +3 -3
- lsst/pipe/base/tests/mocks/_storage_class.py +2 -1
- lsst/pipe/base/version.py +1 -1
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/METADATA +3 -3
- lsst_pipe_base-30.0.1rc1.dist-info/RECORD +129 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/WHEEL +1 -1
- lsst_pipe_base-30.0.0rc2.dist-info/RECORD +0 -125
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/entry_points.txt +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/COPYRIGHT +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/LICENSE +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/bsd_license.txt +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/gpl-v3.0.txt +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/top_level.txt +0 -0
- {lsst_pipe_base-30.0.0rc2.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/zip-safe +0 -0
|
@@ -27,23 +27,113 @@
|
|
|
27
27
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
|
|
30
|
-
__all__ = ["QuantumExecutor", "QuantumGraphExecutor"]
|
|
30
|
+
__all__ = ["QuantumExecutionResult", "QuantumExecutor", "QuantumGraphExecutor"]
|
|
31
31
|
|
|
32
32
|
from abc import ABC, abstractmethod
|
|
33
|
-
from typing import TYPE_CHECKING
|
|
33
|
+
from typing import TYPE_CHECKING, Self
|
|
34
|
+
|
|
35
|
+
from lsst.daf.butler import Quantum
|
|
34
36
|
|
|
35
37
|
from .quantum_reports import QuantumReport, Report
|
|
36
38
|
|
|
37
39
|
if TYPE_CHECKING:
|
|
38
40
|
import uuid
|
|
39
41
|
|
|
40
|
-
from lsst.daf.butler import
|
|
42
|
+
from lsst.daf.butler.logging import ButlerLogRecords
|
|
41
43
|
|
|
44
|
+
from ._task_metadata import TaskMetadata
|
|
42
45
|
from .graph import QuantumGraph
|
|
43
46
|
from .pipeline_graph import TaskNode
|
|
44
47
|
from .quantum_graph import PredictedQuantumGraph
|
|
45
48
|
|
|
46
49
|
|
|
50
|
+
class QuantumExecutionResult(tuple[Quantum, QuantumReport | None]):
|
|
51
|
+
"""A result struct that captures information about a single quantum's
|
|
52
|
+
execution.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
quantum : `lsst.daf.butler.Quantum`
|
|
57
|
+
Quantum that was executed.
|
|
58
|
+
report : `.quantum_reports.QuantumReport`
|
|
59
|
+
Report with basic information about the execution.
|
|
60
|
+
task_metadata : `TaskMetadata`, optional
|
|
61
|
+
Metadata saved by the task and executor during execution.
|
|
62
|
+
skipped_existing : `bool`, optional
|
|
63
|
+
If `True`, this quantum was not executed because it appeared to have
|
|
64
|
+
already been executed successfully.
|
|
65
|
+
adjusted_no_work : `bool`, optional
|
|
66
|
+
If `True`, this quantum was not executed because the
|
|
67
|
+
`PipelineTaskConnections.adjustQuanta` hook raised `NoWorkFound`.
|
|
68
|
+
|
|
69
|
+
Notes
|
|
70
|
+
-----
|
|
71
|
+
For backwards compatibility, this class is a two-element tuple that allows
|
|
72
|
+
the ``quantum`` and ``report`` attributes to be unpacked. Additional
|
|
73
|
+
regular attributes may be added by executors (but the tuple must remain
|
|
74
|
+
only two elements to enable the current unpacking interface).
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __new__(
|
|
78
|
+
cls,
|
|
79
|
+
quantum: Quantum,
|
|
80
|
+
report: QuantumReport | None,
|
|
81
|
+
*,
|
|
82
|
+
task_metadata: TaskMetadata | None = None,
|
|
83
|
+
skipped_existing: bool | None = None,
|
|
84
|
+
adjusted_no_work: bool | None = None,
|
|
85
|
+
) -> Self:
|
|
86
|
+
return super().__new__(cls, (quantum, report))
|
|
87
|
+
|
|
88
|
+
# We need to define both __init__ and __new__ because tuple inheritance
|
|
89
|
+
# requires __new__ and numpydoc requires __init__.
|
|
90
|
+
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
quantum: Quantum,
|
|
94
|
+
report: QuantumReport | None,
|
|
95
|
+
*,
|
|
96
|
+
task_metadata: TaskMetadata | None = None,
|
|
97
|
+
skipped_existing: bool | None = None,
|
|
98
|
+
adjusted_no_work: bool | None = None,
|
|
99
|
+
):
|
|
100
|
+
self._task_metadata = task_metadata
|
|
101
|
+
self._skipped_existing = skipped_existing
|
|
102
|
+
self._adjusted_no_work = adjusted_no_work
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def quantum(self) -> Quantum:
|
|
106
|
+
"""The quantum actually executed."""
|
|
107
|
+
return self[0]
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def report(self) -> QuantumReport | None:
|
|
111
|
+
"""Structure describing the status of the execution of a quantum.
|
|
112
|
+
|
|
113
|
+
This is `None` if the implementation does not support this feature.
|
|
114
|
+
"""
|
|
115
|
+
return self[1]
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def task_metadata(self) -> TaskMetadata | None:
|
|
119
|
+
"""Metadata saved by the task and executor during execution."""
|
|
120
|
+
return self._task_metadata
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def skipped_existing(self) -> bool | None:
|
|
124
|
+
"""If `True`, this quantum was not executed because it appeared to have
|
|
125
|
+
already been executed successfully.
|
|
126
|
+
"""
|
|
127
|
+
return self._skipped_existing
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def adjusted_no_work(self) -> bool | None:
|
|
131
|
+
"""If `True`, this quantum was not executed because the
|
|
132
|
+
`PipelineTaskConnections.adjustQuanta` hook raised `NoWorkFound`.
|
|
133
|
+
"""
|
|
134
|
+
return self._adjusted_no_work
|
|
135
|
+
|
|
136
|
+
|
|
47
137
|
class QuantumExecutor(ABC):
|
|
48
138
|
"""Class which abstracts execution of a single Quantum.
|
|
49
139
|
|
|
@@ -55,8 +145,14 @@ class QuantumExecutor(ABC):
|
|
|
55
145
|
|
|
56
146
|
@abstractmethod
|
|
57
147
|
def execute(
|
|
58
|
-
self,
|
|
59
|
-
|
|
148
|
+
self,
|
|
149
|
+
task_node: TaskNode,
|
|
150
|
+
/,
|
|
151
|
+
quantum: Quantum,
|
|
152
|
+
quantum_id: uuid.UUID | None = None,
|
|
153
|
+
*,
|
|
154
|
+
log_records: ButlerLogRecords | None = None,
|
|
155
|
+
) -> QuantumExecutionResult:
|
|
60
156
|
"""Execute single quantum.
|
|
61
157
|
|
|
62
158
|
Parameters
|
|
@@ -67,15 +163,18 @@ class QuantumExecutor(ABC):
|
|
|
67
163
|
Quantum for this execution.
|
|
68
164
|
quantum_id : `uuid.UUID` or `None`, optional
|
|
69
165
|
The ID of the quantum to be executed.
|
|
166
|
+
log_records : `lsst.daf.butler.ButlerLogRecords`, optional
|
|
167
|
+
Container that should be used to store logs in memory before
|
|
168
|
+
writing them to the butler. This disables streaming log (since
|
|
169
|
+
we'd have to store them in memory anyway), but it permits the
|
|
170
|
+
caller to prepend logs to be stored in the butler and allows task
|
|
171
|
+
logs to be inspected by the caller after execution is complete.
|
|
70
172
|
|
|
71
173
|
Returns
|
|
72
174
|
-------
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Structure describing the status of the execution of a quantum.
|
|
77
|
-
`None` is returned if implementation does not support this
|
|
78
|
-
feature.
|
|
175
|
+
result : `QuantumExecutionResult`
|
|
176
|
+
Result struct. May also be unpacked as a 2-tuple (see type
|
|
177
|
+
documentation).
|
|
79
178
|
|
|
80
179
|
Notes
|
|
81
180
|
-----
|
|
@@ -93,7 +192,9 @@ class QuantumGraphExecutor(ABC):
|
|
|
93
192
|
"""
|
|
94
193
|
|
|
95
194
|
@abstractmethod
|
|
96
|
-
def execute(
|
|
195
|
+
def execute(
|
|
196
|
+
self, graph: QuantumGraph | PredictedQuantumGraph, *, provenance_graph_file: str | None = None
|
|
197
|
+
) -> None:
|
|
97
198
|
"""Execute whole graph.
|
|
98
199
|
|
|
99
200
|
Implementation of this method depends on particular execution model
|
|
@@ -103,8 +204,10 @@ class QuantumGraphExecutor(ABC):
|
|
|
103
204
|
|
|
104
205
|
Parameters
|
|
105
206
|
----------
|
|
106
|
-
graph : `.QuantumGraph`
|
|
207
|
+
graph : `.QuantumGraph` or `.quantum_graph.PredictedQuantumGraph`
|
|
107
208
|
Execution graph.
|
|
209
|
+
provenance_graph_file : `str`, optional
|
|
210
|
+
A filename to write provenance to.
|
|
108
211
|
"""
|
|
109
212
|
raise NotImplementedError()
|
|
110
213
|
|
|
@@ -42,7 +42,7 @@ __all__ = (
|
|
|
42
42
|
import dataclasses
|
|
43
43
|
from collections import defaultdict
|
|
44
44
|
from collections.abc import Iterable, Iterator, MutableMapping, Set
|
|
45
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
|
45
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
|
46
46
|
|
|
47
47
|
import networkx
|
|
48
48
|
|
|
@@ -145,7 +145,7 @@ class PrerequisiteDatasetKey:
|
|
|
145
145
|
is_prerequisite: ClassVar[Literal[True]] = True
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
Key
|
|
148
|
+
type Key = QuantumKey | TaskInitKey | DatasetKey | PrerequisiteDatasetKey
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
class QuantumGraphSkeleton:
|
|
@@ -383,12 +383,6 @@ class QuantumGraphSkeleton:
|
|
|
383
383
|
The dataset ref of the prerequisite.
|
|
384
384
|
**attrs : `~typing.Any`
|
|
385
385
|
Additional attributes for the node.
|
|
386
|
-
|
|
387
|
-
Notes
|
|
388
|
-
-----
|
|
389
|
-
This automatically sets the 'existing_input' ref attribute (see
|
|
390
|
-
`set_existing_input_ref`), since prerequisites are always overall
|
|
391
|
-
inputs.
|
|
392
386
|
"""
|
|
393
387
|
key = PrerequisiteDatasetKey(ref.datasetType.name, ref.id.bytes)
|
|
394
388
|
self._xgraph.add_node(key, data_id=ref.dataId, ref=ref, **attrs)
|
|
@@ -577,12 +571,13 @@ class QuantumGraphSkeleton:
|
|
|
577
571
|
def set_dataset_ref(
|
|
578
572
|
self, ref: DatasetRef, key: DatasetKey | PrerequisiteDatasetKey | None = None
|
|
579
573
|
) -> None:
|
|
580
|
-
"""Associate a dataset node with a
|
|
574
|
+
"""Associate a dataset node with a `~lsst.daf.butler.DatasetRef`
|
|
575
|
+
instance.
|
|
581
576
|
|
|
582
577
|
Parameters
|
|
583
578
|
----------
|
|
584
|
-
ref :
|
|
585
|
-
|
|
579
|
+
ref : `~lsst.daf.butler.DatasetRef`
|
|
580
|
+
`~lsst.daf.butler.DatasetRef` to associate with the node.
|
|
586
581
|
key : `DatasetKey` or `PrerequisiteDatasetKey`, optional
|
|
587
582
|
Identifier for the graph node. If not provided, a `DatasetKey`
|
|
588
583
|
is constructed from the dataset type name and data ID of ``ref``.
|
|
@@ -592,32 +587,33 @@ class QuantumGraphSkeleton:
|
|
|
592
587
|
self._xgraph.nodes[key]["ref"] = ref
|
|
593
588
|
|
|
594
589
|
def set_output_for_skip(self, ref: DatasetRef) -> None:
|
|
595
|
-
"""Associate a dataset node with a
|
|
596
|
-
existing output in a collection where such outputs can
|
|
597
|
-
to be skipped.
|
|
590
|
+
"""Associate a dataset node with a `~lsst.daf.butler.DatasetRef` that
|
|
591
|
+
represents an existing output in a collection where such outputs can
|
|
592
|
+
cause a quantum to be skipped.
|
|
598
593
|
|
|
599
594
|
Parameters
|
|
600
595
|
----------
|
|
601
|
-
ref :
|
|
602
|
-
|
|
596
|
+
ref : `~lsst.daf.butler.DatasetRef`
|
|
597
|
+
`~lsst.daf.butler.DatasetRef` to associate with the node.
|
|
603
598
|
"""
|
|
604
599
|
key = DatasetKey(ref.datasetType.name, ref.dataId.required_values)
|
|
605
600
|
self._xgraph.nodes[key]["output_for_skip"] = ref
|
|
606
601
|
|
|
607
602
|
def set_output_in_the_way(self, ref: DatasetRef) -> None:
|
|
608
|
-
"""Associate a dataset node with a
|
|
609
|
-
existing output in the output RUN
|
|
603
|
+
"""Associate a dataset node with a `~lsst.daf.butler.DatasetRef` that
|
|
604
|
+
represents an existing output in the output RUN collection.
|
|
610
605
|
|
|
611
606
|
Parameters
|
|
612
607
|
----------
|
|
613
|
-
ref :
|
|
614
|
-
|
|
608
|
+
ref : `~lsst.daf.butler.DatasetRef`
|
|
609
|
+
`~lsst.daf.butler.DatasetRef` to associate with the node.
|
|
615
610
|
"""
|
|
616
611
|
key = DatasetKey(ref.datasetType.name, ref.dataId.required_values)
|
|
617
612
|
self._xgraph.nodes[key]["output_in_the_way"] = ref
|
|
618
613
|
|
|
619
614
|
def get_dataset_ref(self, key: DatasetKey | PrerequisiteDatasetKey) -> DatasetRef | None:
|
|
620
|
-
"""Return the
|
|
615
|
+
"""Return the `~lsst.daf.butler.DatasetRef` associated with the given
|
|
616
|
+
node.
|
|
621
617
|
|
|
622
618
|
This does not return "output for skip" and "output in the way"
|
|
623
619
|
datasets.
|
|
@@ -629,14 +625,14 @@ class QuantumGraphSkeleton:
|
|
|
629
625
|
|
|
630
626
|
Returns
|
|
631
627
|
-------
|
|
632
|
-
ref :
|
|
628
|
+
ref : `~lsst.daf.butler.DatasetRef` or `None`
|
|
633
629
|
Dataset reference associated with the node.
|
|
634
630
|
"""
|
|
635
631
|
return self._xgraph.nodes[key].get("ref")
|
|
636
632
|
|
|
637
633
|
def get_output_for_skip(self, key: DatasetKey) -> DatasetRef | None:
|
|
638
|
-
"""Return the
|
|
639
|
-
collection where it could lead to a quantum being skipped.
|
|
634
|
+
"""Return the `~lsst.daf.butler.DatasetRef` associated with the given
|
|
635
|
+
node in a collection where it could lead to a quantum being skipped.
|
|
640
636
|
|
|
641
637
|
Parameters
|
|
642
638
|
----------
|
|
@@ -645,14 +641,14 @@ class QuantumGraphSkeleton:
|
|
|
645
641
|
|
|
646
642
|
Returns
|
|
647
643
|
-------
|
|
648
|
-
ref :
|
|
644
|
+
ref : `~lsst.daf.butler.DatasetRef` or `None`
|
|
649
645
|
Dataset reference associated with the node.
|
|
650
646
|
"""
|
|
651
647
|
return self._xgraph.nodes[key].get("output_for_skip")
|
|
652
648
|
|
|
653
649
|
def get_output_in_the_way(self, key: DatasetKey) -> DatasetRef | None:
|
|
654
|
-
"""Return the
|
|
655
|
-
output RUN collection.
|
|
650
|
+
"""Return the `~lsst.daf.butler.DatasetRef` associated with the given
|
|
651
|
+
node in the output RUN collection.
|
|
656
652
|
|
|
657
653
|
Parameters
|
|
658
654
|
----------
|
|
@@ -661,16 +657,16 @@ class QuantumGraphSkeleton:
|
|
|
661
657
|
|
|
662
658
|
Returns
|
|
663
659
|
-------
|
|
664
|
-
ref :
|
|
660
|
+
ref : `~lsst.daf.butler.DatasetRef` or `None`
|
|
665
661
|
Dataset reference associated with the node.
|
|
666
662
|
"""
|
|
667
663
|
return self._xgraph.nodes[key].get("output_in_the_way")
|
|
668
664
|
|
|
669
665
|
def discard_output_in_the_way(self, key: DatasetKey) -> None:
|
|
670
|
-
"""Drop any
|
|
671
|
-
collection.
|
|
666
|
+
"""Drop any `~lsst.daf.butler.DatasetRef` associated with this node in
|
|
667
|
+
the output RUN collection.
|
|
672
668
|
|
|
673
|
-
Does nothing if there is no such
|
|
669
|
+
Does nothing if there is no such `~lsst.daf.butler.DatasetRef`.
|
|
674
670
|
|
|
675
671
|
Parameters
|
|
676
672
|
----------
|
|
@@ -682,8 +678,8 @@ class QuantumGraphSkeleton:
|
|
|
682
678
|
def set_data_id(self, key: Key, data_id: DataCoordinate) -> None:
|
|
683
679
|
"""Set the data ID associated with a node.
|
|
684
680
|
|
|
685
|
-
This updates the data ID in any
|
|
686
|
-
the node via `set_ref`, `set_output_for_skip`, or
|
|
681
|
+
This updates the data ID in any `~lsst.daf.butler.DatasetRef` objects
|
|
682
|
+
associated with the node via `set_ref`, `set_output_for_skip`, or
|
|
687
683
|
`set_output_in_the_way` as well, assuming it is an expanded version
|
|
688
684
|
of the original data ID.
|
|
689
685
|
|
|
@@ -691,7 +687,7 @@ class QuantumGraphSkeleton:
|
|
|
691
687
|
----------
|
|
692
688
|
key : `Key`
|
|
693
689
|
Identifier for the graph node.
|
|
694
|
-
data_id :
|
|
690
|
+
data_id : `~lsst.daf.butler.DataCoordinate`
|
|
695
691
|
Data ID for the node.
|
|
696
692
|
"""
|
|
697
693
|
state: MutableMapping[str, Any] = self._xgraph.nodes[key]
|
|
@@ -716,7 +712,7 @@ class QuantumGraphSkeleton:
|
|
|
716
712
|
|
|
717
713
|
Returns
|
|
718
714
|
-------
|
|
719
|
-
data_id :
|
|
715
|
+
data_id : `~lsst.daf.butler.DataCoordinate`
|
|
720
716
|
Expanded data ID for the node, if one is available.
|
|
721
717
|
|
|
722
718
|
Raises
|
|
@@ -79,6 +79,7 @@ from .automatic_connection_constants import (
|
|
|
79
79
|
METADATA_OUTPUT_CONNECTION_NAME,
|
|
80
80
|
METADATA_OUTPUT_STORAGE_CLASS,
|
|
81
81
|
METADATA_OUTPUT_TEMPLATE,
|
|
82
|
+
PROVENANCE_DATASET_TYPE_NAME,
|
|
82
83
|
)
|
|
83
84
|
from .graph import QuantumGraph, QuantumNode
|
|
84
85
|
|
|
@@ -586,8 +587,8 @@ class TaskSummary(pydantic.BaseModel):
|
|
|
586
587
|
|
|
587
588
|
Unpack the `QuantumInfo` object, sorting quanta of each status into
|
|
588
589
|
the correct place in the `TaskSummary`. If looking for error messages
|
|
589
|
-
in the `Butler` logs is desired, take special care to
|
|
590
|
-
with missing logs.
|
|
590
|
+
in the `lsst.daf.butler.Butler` logs is desired, take special care to
|
|
591
|
+
catch issues with missing logs.
|
|
591
592
|
|
|
592
593
|
Parameters
|
|
593
594
|
----------
|
|
@@ -866,7 +867,7 @@ class DatasetTypeSummary(pydantic.BaseModel):
|
|
|
866
867
|
class Summary(pydantic.BaseModel):
|
|
867
868
|
"""A summary of the contents of the QuantumProvenanceGraph, including
|
|
868
869
|
all information on the quanta for each task and the datasets of each
|
|
869
|
-
|
|
870
|
+
`~lsst.daf.butler.DatasetType`.
|
|
870
871
|
"""
|
|
871
872
|
|
|
872
873
|
tasks: dict[str, TaskSummary] = pydantic.Field(default_factory=dict)
|
|
@@ -885,7 +886,7 @@ class Summary(pydantic.BaseModel):
|
|
|
885
886
|
|
|
886
887
|
Parameters
|
|
887
888
|
----------
|
|
888
|
-
summaries : `
|
|
889
|
+
summaries : `~collections.abc.Sequence` [`Summary`]
|
|
889
890
|
Sequence of all `Summary` objects to aggregate.
|
|
890
891
|
"""
|
|
891
892
|
result = cls()
|
|
@@ -1245,8 +1246,8 @@ class QuantumProvenanceGraph:
|
|
|
1245
1246
|
Returns
|
|
1246
1247
|
-------
|
|
1247
1248
|
dataset_info : `DatasetInfo`
|
|
1248
|
-
The `TypedDict` with information about the
|
|
1249
|
-
pair across all runs.
|
|
1249
|
+
The `TypedDict` with information about the
|
|
1250
|
+
`~lsst.daf.butler.DatasetType`-dataID pair across all runs.
|
|
1250
1251
|
"""
|
|
1251
1252
|
return self._xgraph.nodes[key]
|
|
1252
1253
|
|
|
@@ -1262,6 +1263,7 @@ class QuantumProvenanceGraph:
|
|
|
1262
1263
|
do_store_logs : `bool`
|
|
1263
1264
|
Store the logs in the summary dictionary.
|
|
1264
1265
|
n_cores : `int`, optional
|
|
1266
|
+
Number of cores to use.
|
|
1265
1267
|
|
|
1266
1268
|
Returns
|
|
1267
1269
|
-------
|
|
@@ -1513,8 +1515,22 @@ class QuantumProvenanceGraph:
|
|
|
1513
1515
|
len(self._datasets.keys()),
|
|
1514
1516
|
)
|
|
1515
1517
|
if use_qbb:
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
+
provenance_graph_ref: DatasetRef | None = None
|
|
1519
|
+
try:
|
|
1520
|
+
provenance_graph_ref = butler.find_dataset(
|
|
1521
|
+
PROVENANCE_DATASET_TYPE_NAME, collections=output_run
|
|
1522
|
+
)
|
|
1523
|
+
except MissingDatasetTypeError:
|
|
1524
|
+
pass
|
|
1525
|
+
if provenance_graph_ref is not None:
|
|
1526
|
+
_LOG.warning(
|
|
1527
|
+
"Cannot use QBB for metadata/log reads after provenance has been ingested; "
|
|
1528
|
+
"falling back to full butler."
|
|
1529
|
+
)
|
|
1530
|
+
self._butler_wrappers[output_run] = _ThreadLocalButlerWrapper.wrap_full(butler)
|
|
1531
|
+
else:
|
|
1532
|
+
_LOG.verbose("Using quantum-backed butler for metadata loads.")
|
|
1533
|
+
self._butler_wrappers[output_run] = _ThreadLocalButlerWrapper.wrap_qbb(butler, qgraph)
|
|
1518
1534
|
else:
|
|
1519
1535
|
_LOG.verbose("Using full butler for metadata loads.")
|
|
1520
1536
|
self._butler_wrappers[output_run] = _ThreadLocalButlerWrapper.wrap_full(butler)
|
|
@@ -1777,9 +1793,10 @@ class QuantumProvenanceGraph:
|
|
|
1777
1793
|
successes. If "exhaustive", all metadata files will be read. If
|
|
1778
1794
|
"lazy", only metadata files where at least one predicted output is
|
|
1779
1795
|
missing will be read.
|
|
1780
|
-
|
|
1781
|
-
The
|
|
1782
|
-
|
|
1796
|
+
executor : `concurrent.futures.Executor`
|
|
1797
|
+
The futures executor to use.
|
|
1798
|
+
futures : `list` [ `concurrent.futures.Future` ]
|
|
1799
|
+
Current list of futures. Will be modified.
|
|
1783
1800
|
"""
|
|
1784
1801
|
if read_caveats == "lazy" and all(
|
|
1785
1802
|
self.get_dataset_info(dataset_key)["runs"][output_run].produced
|
|
@@ -1994,7 +2011,7 @@ class _ThreadLocalButlerWrapper:
|
|
|
1994
2011
|
full_butler : `~lsst.daf.butler.Butler`
|
|
1995
2012
|
Full butler to draw datastore and dimension configuration from.
|
|
1996
2013
|
qg : `QuantumGraph`
|
|
1997
|
-
Quantum graph
|
|
2014
|
+
Quantum graph.
|
|
1998
2015
|
|
|
1999
2016
|
Returns
|
|
2000
2017
|
-------
|
|
@@ -40,7 +40,8 @@ from collections.abc import Iterable
|
|
|
40
40
|
from typing import Any
|
|
41
41
|
|
|
42
42
|
import lsst.resources
|
|
43
|
-
from lsst.daf.butler import Butler
|
|
43
|
+
from lsst.daf.butler import Butler, DatasetRef
|
|
44
|
+
from lsst.daf.butler._rubin.temporary_for_ingest import TemporaryForIngest
|
|
44
45
|
|
|
45
46
|
from ._quantumContext import ExecutionResources
|
|
46
47
|
from .all_dimensions_quantum_graph_builder import AllDimensionsQuantumGraphBuilder
|
|
@@ -78,7 +79,7 @@ class SeparablePipelineExecutor:
|
|
|
78
79
|
clobber_output : `bool`, optional
|
|
79
80
|
If set, the pipeline execution overwrites existing output files.
|
|
80
81
|
Otherwise, any conflict between existing and new outputs is an error.
|
|
81
|
-
skip_existing_in :
|
|
82
|
+
skip_existing_in : `~collections.abc.Iterable` [`str`], optional
|
|
82
83
|
If not empty, the pipeline execution searches the listed collections
|
|
83
84
|
for existing outputs, and skips any quanta that have run to completion
|
|
84
85
|
(or have no work to do). Otherwise, all tasks are attempted (subject to
|
|
@@ -362,6 +363,8 @@ class SeparablePipelineExecutor:
|
|
|
362
363
|
fail_fast: bool = False,
|
|
363
364
|
graph_executor: QuantumGraphExecutor | None = None,
|
|
364
365
|
num_proc: int = 1,
|
|
366
|
+
*,
|
|
367
|
+
provenance_dataset_ref: DatasetRef | None = None,
|
|
365
368
|
) -> None:
|
|
366
369
|
"""Run a pipeline in the form of a prepared quantum graph.
|
|
367
370
|
|
|
@@ -384,6 +387,14 @@ class SeparablePipelineExecutor:
|
|
|
384
387
|
The number of processes that can be used to run the pipeline. The
|
|
385
388
|
default value ensures that no subprocess is created. Only used with
|
|
386
389
|
the default graph executor.
|
|
390
|
+
provenance_dataset_ref : `lsst.daf.butler.DatasetRef`, optional
|
|
391
|
+
Dataset that should be used to save provenance. Provenance is only
|
|
392
|
+
supported when running in a single process (at least for the
|
|
393
|
+
default quantum executor), and should not be used with
|
|
394
|
+
``skip_existing_in=[output_run]`` when retrying a previous
|
|
395
|
+
execution attempt. The caller is responsible for registering the
|
|
396
|
+
dataset type and for ensuring that the dimensions of this dataset
|
|
397
|
+
do not lead to uniqueness conflicts.
|
|
387
398
|
"""
|
|
388
399
|
if not graph_executor:
|
|
389
400
|
quantum_executor = SingleQuantumExecutor(
|
|
@@ -404,4 +415,9 @@ class SeparablePipelineExecutor:
|
|
|
404
415
|
# forked processes.
|
|
405
416
|
self._butler.registry.resetConnectionPool()
|
|
406
417
|
|
|
407
|
-
|
|
418
|
+
if provenance_dataset_ref is not None:
|
|
419
|
+
with TemporaryForIngest(self._butler, provenance_dataset_ref) as temporary:
|
|
420
|
+
graph_executor.execute(graph, provenance_graph_file=temporary.ospath)
|
|
421
|
+
temporary.ingest()
|
|
422
|
+
else:
|
|
423
|
+
graph_executor.execute(graph)
|