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.
Files changed (40) hide show
  1. lsst/pipe/base/_task_metadata.py +15 -0
  2. lsst/pipe/base/dot_tools.py +14 -152
  3. lsst/pipe/base/exec_fixup_data_id.py +17 -44
  4. lsst/pipe/base/execution_graph_fixup.py +49 -18
  5. lsst/pipe/base/graph/_versionDeserializers.py +6 -5
  6. lsst/pipe/base/graph/graph.py +30 -10
  7. lsst/pipe/base/graph/graphSummary.py +30 -0
  8. lsst/pipe/base/graph_walker.py +119 -0
  9. lsst/pipe/base/log_capture.py +5 -2
  10. lsst/pipe/base/mermaid_tools.py +11 -64
  11. lsst/pipe/base/mp_graph_executor.py +298 -236
  12. lsst/pipe/base/pipeline_graph/io.py +1 -1
  13. lsst/pipe/base/quantum_graph/__init__.py +32 -0
  14. lsst/pipe/base/quantum_graph/_common.py +632 -0
  15. lsst/pipe/base/quantum_graph/_multiblock.py +808 -0
  16. lsst/pipe/base/quantum_graph/_predicted.py +1950 -0
  17. lsst/pipe/base/quantum_graph/visualization.py +302 -0
  18. lsst/pipe/base/quantum_graph_builder.py +292 -34
  19. lsst/pipe/base/quantum_graph_executor.py +2 -1
  20. lsst/pipe/base/quantum_provenance_graph.py +16 -7
  21. lsst/pipe/base/quantum_reports.py +45 -0
  22. lsst/pipe/base/separable_pipeline_executor.py +126 -15
  23. lsst/pipe/base/simple_pipeline_executor.py +44 -43
  24. lsst/pipe/base/single_quantum_executor.py +1 -40
  25. lsst/pipe/base/tests/mocks/__init__.py +1 -1
  26. lsst/pipe/base/tests/mocks/_pipeline_task.py +16 -1
  27. lsst/pipe/base/tests/mocks/{_in_memory_repo.py → _repo.py} +324 -45
  28. lsst/pipe/base/tests/mocks/_storage_class.py +51 -0
  29. lsst/pipe/base/tests/simpleQGraph.py +11 -5
  30. lsst/pipe/base/version.py +1 -1
  31. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/METADATA +2 -1
  32. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/RECORD +40 -34
  33. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/WHEEL +0 -0
  34. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/entry_points.txt +0 -0
  35. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/COPYRIGHT +0 -0
  36. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/LICENSE +0 -0
  37. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/bsd_license.txt +0 -0
  38. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/licenses/gpl-v3.0.txt +0 -0
  39. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/top_level.txt +0 -0
  40. {lsst_pipe_base-29.2025.3900.dist-info → lsst_pipe_base-29.2025.4100.dist-info}/zip-safe +0 -0
@@ -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
- if self.full_butler is not None:
120
- mdc["RUN"] = self.full_butler.run or ""
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
@@ -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 lsst.daf.butler import DatasetRef
43
- from lsst.pipe.base import QuantumGraph, TaskDef
42
+ from .graph import QuantumGraph
43
+ from .pipeline import TaskDef
44
+ from .quantum_graph import PredictedQuantumGraph
44
45
 
45
46
 
46
- def _datasetRefId(dsRef: DatasetRef) -> str:
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
- # Start Mermaid code block with flowchart.
101
- print("flowchart TD", file=file)
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()