lsst-pipe-base 29.2025.3900__py3-none-any.whl → 29.2025.4100__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/_task_metadata.py +15 -0
- lsst/pipe/base/dot_tools.py +14 -152
- lsst/pipe/base/exec_fixup_data_id.py +17 -44
- lsst/pipe/base/execution_graph_fixup.py +49 -18
- lsst/pipe/base/graph/_versionDeserializers.py +6 -5
- lsst/pipe/base/graph/graph.py +30 -10
- lsst/pipe/base/graph/graphSummary.py +30 -0
- lsst/pipe/base/graph_walker.py +119 -0
- lsst/pipe/base/log_capture.py +5 -2
- lsst/pipe/base/mermaid_tools.py +11 -64
- lsst/pipe/base/mp_graph_executor.py +298 -236
- lsst/pipe/base/pipeline_graph/io.py +1 -1
- lsst/pipe/base/quantum_graph/__init__.py +32 -0
- lsst/pipe/base/quantum_graph/_common.py +632 -0
- lsst/pipe/base/quantum_graph/_multiblock.py +808 -0
- lsst/pipe/base/quantum_graph/_predicted.py +1950 -0
- lsst/pipe/base/quantum_graph/visualization.py +302 -0
- lsst/pipe/base/quantum_graph_builder.py +292 -34
- lsst/pipe/base/quantum_graph_executor.py +2 -1
- lsst/pipe/base/quantum_provenance_graph.py +16 -7
- lsst/pipe/base/quantum_reports.py +45 -0
- lsst/pipe/base/separable_pipeline_executor.py +126 -15
- lsst/pipe/base/simple_pipeline_executor.py +44 -43
- lsst/pipe/base/single_quantum_executor.py +1 -40
- lsst/pipe/base/tests/mocks/__init__.py +1 -1
- lsst/pipe/base/tests/mocks/_pipeline_task.py +16 -1
- lsst/pipe/base/tests/mocks/{_in_memory_repo.py → _repo.py} +324 -45
- lsst/pipe/base/tests/mocks/_storage_class.py +51 -0
- lsst/pipe/base/tests/simpleQGraph.py +11 -5
- lsst/pipe/base/version.py +1 -1
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/METADATA +2 -1
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/RECORD +40 -34
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/WHEEL +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/entry_points.txt +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/COPYRIGHT +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/LICENSE +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/bsd_license.txt +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/gpl-v3.0.txt +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/top_level.txt +0 -0
- {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/zip-safe +0 -0
lsst/pipe/base/log_capture.py
CHANGED
|
@@ -41,6 +41,7 @@ from lsst.daf.butler import Butler, FileDataset, LimitedButler, Quantum
|
|
|
41
41
|
from lsst.daf.butler.logging import ButlerLogRecordHandler, ButlerLogRecords, ButlerMDC, JsonLogFormatter
|
|
42
42
|
|
|
43
43
|
from ._status import InvalidQuantumError
|
|
44
|
+
from .automatic_connection_constants import METADATA_OUTPUT_TEMPLATE
|
|
44
45
|
from .pipeline_graph import TaskNode
|
|
45
46
|
|
|
46
47
|
_LOG = logging.getLogger(__name__)
|
|
@@ -116,8 +117,10 @@ class LogCapture:
|
|
|
116
117
|
mdc = {"LABEL": task_node.label, "RUN": ""}
|
|
117
118
|
if quantum.dataId:
|
|
118
119
|
mdc["LABEL"] += f":{quantum.dataId}"
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
|
|
121
|
+
metadata_ref = quantum.outputs[METADATA_OUTPUT_TEMPLATE.format(label=task_node.label)][0]
|
|
122
|
+
mdc["RUN"] = metadata_ref.run
|
|
123
|
+
|
|
121
124
|
ctx = _LogCaptureFlag()
|
|
122
125
|
log_dataset_name = (
|
|
123
126
|
task_node.log_output.dataset_type_name if task_node.log_output is not None else None
|
lsst/pipe/base/mermaid_tools.py
CHANGED
|
@@ -39,39 +39,12 @@ from typing import TYPE_CHECKING, Any, Literal
|
|
|
39
39
|
from .pipeline import Pipeline
|
|
40
40
|
|
|
41
41
|
if TYPE_CHECKING:
|
|
42
|
-
from
|
|
43
|
-
from
|
|
42
|
+
from .graph import QuantumGraph
|
|
43
|
+
from .pipeline import TaskDef
|
|
44
|
+
from .quantum_graph import PredictedQuantumGraph
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
def
|
|
47
|
-
"""Make a unique identifier string for a dataset ref based on its name and
|
|
48
|
-
dataId.
|
|
49
|
-
"""
|
|
50
|
-
dsIdParts = [dsRef.datasetType.name]
|
|
51
|
-
dsIdParts.extend(f"{key}_{dsRef.dataId[key]}" for key in sorted(dsRef.dataId.required.keys()))
|
|
52
|
-
return "_".join(dsIdParts)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _makeDatasetNode(dsRef: DatasetRef, allDatasetRefs: dict[str, str], file: Any) -> str:
|
|
56
|
-
"""Create a Mermaid node for a dataset if it doesn't exist, and return its
|
|
57
|
-
node ID.
|
|
58
|
-
"""
|
|
59
|
-
dsId = _datasetRefId(dsRef)
|
|
60
|
-
nodeName = allDatasetRefs.get(dsId)
|
|
61
|
-
if nodeName is None:
|
|
62
|
-
nodeName = f"DATASET_{len(allDatasetRefs)}"
|
|
63
|
-
allDatasetRefs[dsId] = nodeName
|
|
64
|
-
# Simple label: datasetType name and run.
|
|
65
|
-
label_lines = [f"**{dsRef.datasetType.name}**", f"run: {dsRef.run}"]
|
|
66
|
-
# Add dataId info.
|
|
67
|
-
for k in sorted(dsRef.dataId.required.keys()):
|
|
68
|
-
label_lines.append(f"{k}={dsRef.dataId[k]}")
|
|
69
|
-
label = "<br>".join(label_lines)
|
|
70
|
-
print(f'{nodeName}["{label}"]', file=file)
|
|
71
|
-
return nodeName
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def graph2mermaid(qgraph: QuantumGraph, file: Any) -> None:
|
|
47
|
+
def graph2mermaid(qgraph: QuantumGraph | PredictedQuantumGraph, file: Any) -> None:
|
|
75
48
|
"""Convert QuantumGraph into a Mermaid flowchart (top-down).
|
|
76
49
|
|
|
77
50
|
This method is mostly for documentation/presentation purposes.
|
|
@@ -91,45 +64,19 @@ def graph2mermaid(qgraph: QuantumGraph, file: Any) -> None:
|
|
|
91
64
|
ImportError
|
|
92
65
|
Raised if the task class cannot be imported.
|
|
93
66
|
"""
|
|
67
|
+
from .quantum_graph import PredictedQuantumGraph, visualization
|
|
68
|
+
|
|
69
|
+
if not isinstance(qgraph, PredictedQuantumGraph):
|
|
70
|
+
qgraph = PredictedQuantumGraph.from_old_quantum_graph(qgraph)
|
|
71
|
+
|
|
94
72
|
# Open a file if needed.
|
|
95
73
|
close = False
|
|
96
74
|
if not hasattr(file, "write"):
|
|
97
75
|
file = open(file, "w")
|
|
98
76
|
close = True
|
|
99
77
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# To avoid duplicating dataset nodes, we track them.
|
|
104
|
-
allDatasetRefs: dict[str, str] = {}
|
|
105
|
-
|
|
106
|
-
# Process each task/quantum.
|
|
107
|
-
for taskId, taskDef in enumerate(qgraph.taskGraph):
|
|
108
|
-
quanta = qgraph.getNodesForTask(taskDef)
|
|
109
|
-
for qId, quantumNode in enumerate(quanta):
|
|
110
|
-
# Create quantum node.
|
|
111
|
-
taskNodeName = f"TASK_{taskId}_{qId}"
|
|
112
|
-
taskLabelLines = [f"**{taskDef.label}**", f"Node ID: {quantumNode.nodeId}"]
|
|
113
|
-
dataId = quantumNode.quantum.dataId
|
|
114
|
-
if dataId is not None:
|
|
115
|
-
for k in sorted(dataId.required.keys()):
|
|
116
|
-
taskLabelLines.append(f"{k}={dataId[k]}")
|
|
117
|
-
else:
|
|
118
|
-
raise ValueError("Quantum DataId cannot be None")
|
|
119
|
-
taskLabel = "<br>".join(taskLabelLines)
|
|
120
|
-
print(f'{taskNodeName}["{taskLabel}"]', file=file)
|
|
121
|
-
|
|
122
|
-
# Quantum inputs: datasets --> tasks
|
|
123
|
-
for dsRefs in quantumNode.quantum.inputs.values():
|
|
124
|
-
for dsRef in dsRefs:
|
|
125
|
-
dsNode = _makeDatasetNode(dsRef, allDatasetRefs, file)
|
|
126
|
-
print(f"{dsNode} --> {taskNodeName}", file=file)
|
|
127
|
-
|
|
128
|
-
# Quantum outputs: tasks --> datasets
|
|
129
|
-
for dsRefs in quantumNode.quantum.outputs.values():
|
|
130
|
-
for dsRef in dsRefs:
|
|
131
|
-
dsNode = _makeDatasetNode(dsRef, allDatasetRefs, file)
|
|
132
|
-
print(f"{taskNodeName} --> {dsNode}", file=file)
|
|
78
|
+
v = visualization.QuantumGraphMermaidVisualizer()
|
|
79
|
+
v.write_bipartite(qgraph, file)
|
|
133
80
|
|
|
134
81
|
if close:
|
|
135
82
|
file.close()
|