Coverage for python/lsst/pipe/base/quantum_graph/aggregator/_structs.py: 73%
72 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 01:47 -0800
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 01:47 -0800
1# This file is part of pipe_base.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30__all__ = (
31 "IngestRequest",
32 "ScanReport",
33 "ScanResult",
34 "ScanStatus",
35)
37import dataclasses
38import enum
39import uuid
41from lsst.daf.butler.datastore.record_data import DatastoreRecordData
43from ..._status import QuantumSuccessCaveats
44from ...quantum_provenance_graph import ExceptionInfo, QuantumRunStatus
45from ...resource_usage import QuantumResourceUsage
46from .._common import DatastoreName
47from .._predicted import PredictedDatasetModel
50class ScanStatus(enum.Enum):
51 """Status enum for quantum scanning.
53 Note that this records the status for the *scanning* which is distinct
54 from the status of the quantum's execution.
55 """
57 INCOMPLETE = enum.auto()
58 """The quantum is not necessarily done running, and cannot be scanned
59 conclusively yet.
60 """
62 ABANDONED = enum.auto()
63 """The quantum's execution appears to have failed but we cannot rule out
64 the possibility that it could be recovered, but we've also waited long
65 enough (according to `ScannerTimeConfigDict.retry_timeout`) that it's time
66 to stop trying for now.
68 This state means a later run with `ScannerConfig.assume_complete` is
69 required.
70 """
72 SUCCESSFUL = enum.auto()
73 """The quantum was conclusively scanned and was executed successfully,
74 unblocking scans for downstream quanta.
75 """
77 FAILED = enum.auto()
78 """The quantum was conclusively scanned and failed execution, blocking
79 scans for downstream quanta.
80 """
82 BLOCKED = enum.auto()
83 """A quantum upstream of this one failed."""
85 INIT = enum.auto()
86 """Init quanta need special handling, because they don't have logs and
87 metadata.
88 """
91@dataclasses.dataclass
92class ScanReport:
93 """Minimal information needed about a completed scan by the supervisor."""
95 quantum_id: uuid.UUID
96 """Unique ID of the quantum."""
98 status: ScanStatus
99 """Combined status of the scan and the execution of the quantum."""
102@dataclasses.dataclass
103class IngestRequest:
104 """A request to ingest datasets produced by a single quantum."""
106 producer_id: uuid.UUID
107 """ID of the quantum that produced these datasets."""
109 datasets: list[PredictedDatasetModel]
110 """Registry information about the datasets."""
112 records: dict[DatastoreName, DatastoreRecordData]
113 """Datastore information about the datasets."""
115 def __bool__(self) -> bool:
116 return bool(self.datasets or self.records)
119@dataclasses.dataclass
120class ScanResult:
121 """A struct that represents the result of scanning a quantum."""
123 quantum_id: uuid.UUID
124 """Unique ID for the quantum."""
126 status: ScanStatus
127 """Combined status for the scan and the execution of the quantum."""
129 caveats: QuantumSuccessCaveats | None = None
130 """Flags indicating caveats on successful quanta."""
132 exception: ExceptionInfo | None = None
133 """Information about an exception raised when the quantum was executing."""
135 resource_usage: QuantumResourceUsage | None = None
136 """Resource usage information (timing, memory use) for this quantum."""
138 existing_outputs: set[uuid.UUID] = dataclasses.field(default_factory=set)
139 """Unique IDs of the output datasets that were actually written."""
141 metadata: bytes = b""
142 """Raw content of the metadata dataset."""
144 log: bytes = b""
145 """Raw content of the log dataset."""
147 is_compressed: bool = False
148 """Whether the `metadata` and `log` attributes are compressed."""
150 def get_run_status(self) -> QuantumRunStatus:
151 """Translate the scan status and metadata/log presence into a run
152 status.
153 """
154 if self.status is ScanStatus.BLOCKED:
155 return QuantumRunStatus.BLOCKED
156 if self.status is ScanStatus.INIT:
157 return QuantumRunStatus.SUCCESSFUL
158 if self.log:
159 if self.metadata:
160 return QuantumRunStatus.SUCCESSFUL
161 else:
162 return QuantumRunStatus.FAILED
163 else:
164 if self.metadata:
165 return QuantumRunStatus.LOGS_MISSING
166 else:
167 return QuantumRunStatus.METADATA_MISSING