Coverage for python/lsst/daf/butler/_exceptions.py: 84%
53 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 08:33 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 08:33 +0000
1# This file is part of daf_butler.
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/>.
28"""Specialized Butler exceptions."""
30__all__ = (
31 "ButlerUserError",
32 "CalibrationLookupError",
33 "CollectionCycleError",
34 "CollectionTypeError",
35 "DataIdValueError",
36 "DatasetNotFoundError",
37 "DatasetTypeNotSupportedError",
38 "DimensionNameError",
39 "EmptyQueryResultError",
40 "InconsistentDataIdError",
41 "InconsistentUniverseError",
42 "InvalidQueryError",
43 "MissingCollectionError",
44 "MissingDatasetTypeError",
45 "UnknownComponentError",
46 "ValidationError",
47)
49from ._exceptions_legacy import CollectionError, DataIdError, DatasetTypeError
52class ButlerUserError(Exception):
53 """Base class for Butler exceptions that contain a user-facing error
54 message.
56 Parameters
57 ----------
58 detail : `str`
59 Details about the error that occurred.
60 """
62 # When used with Butler server, exceptions inheriting from
63 # this class will be sent to the client side and re-raised by RemoteButler
64 # there. Be careful that error messages do not contain security-sensitive
65 # information.
66 #
67 # This should only be used for "expected" errors that occur because of
68 # errors in user-supplied data passed to Butler methods. It should not be
69 # used for any issues caused by the Butler configuration file, errors in
70 # the library code itself or the underlying databases.
71 #
72 # When you create a new subclass of this type, add it to the list in
73 # _USER_ERROR_TYPES below.
75 error_type: str
76 """Unique name for this error type, used to identify it when sending
77 information about the error to the client.
78 """
80 def __init__(self, detail: str):
81 return super().__init__(detail)
84class CalibrationLookupError(LookupError, ButlerUserError):
85 """Exception raised for failures to look up a calibration dataset.
87 For a find-first query involving a calibration dataset to work, either the
88 query's result rows need to include a temporal dimension or needs to be
89 constrained temporally, such that each result row corresponds to a unique
90 calibration dataset. This exception can be raised if those dimensions or
91 constraint are missing, or if a temporal dimension timespan overlaps
92 multiple validity ranges (e.g. the recommended bias changes in the middle
93 of an exposure).
94 """
96 error_type = "calibration_lookup"
99class CollectionCycleError(ValueError, ButlerUserError):
100 """Raised when an operation would cause a chained collection to be a child
101 of itself.
102 """
104 error_type = "collection_cycle"
107class CollectionTypeError(CollectionError, ButlerUserError):
108 """Exception raised when type of a collection is incorrect."""
110 error_type = "collection_type"
113class DataIdValueError(DataIdError, ButlerUserError):
114 """Exception raised when a value specified in a data ID does not exist."""
116 error_type = "data_id_value"
119class DatasetNotFoundError(LookupError, ButlerUserError):
120 """The requested dataset could not be found."""
122 error_type = "dataset_not_found"
125class DimensionNameError(KeyError, DataIdError, ButlerUserError):
126 """Exception raised when a dimension specified in a data ID does not exist
127 or required dimension is not provided.
128 """
130 error_type = "dimension_name"
133class DimensionValueError(ValueError, ButlerUserError):
134 """Exception raised for issues with dimension values in a data ID."""
136 error_type = "dimension_value"
139class InconsistentDataIdError(DataIdError, ButlerUserError):
140 """Exception raised when a data ID contains contradictory key-value pairs,
141 according to dimension relationships.
142 """
144 error_type = "inconsistent_data_id"
147class InvalidQueryError(ButlerUserError):
148 """Exception raised when a query is not valid."""
150 error_type = "invalid_query"
153class MissingCollectionError(CollectionError, ButlerUserError):
154 """Exception raised when an operation attempts to use a collection that
155 does not exist.
156 """
158 error_type = "missing_collection"
161class UnimplementedQueryError(NotImplementedError, ButlerUserError):
162 """Exception raised when the query system does not support the query
163 specified by the user.
164 """
166 error_type = "unimplemented_query"
169class MissingDatasetTypeError(DatasetTypeError, KeyError, ButlerUserError):
170 """Exception raised when a dataset type does not exist."""
172 error_type = "missing_dataset_type"
175class UnknownComponentError(KeyError, ButlerUserError):
176 """Exception raised when the requested component of a DatasetType is not
177 known.
178 """
180 error_type = "unknown_component"
183class DatasetTypeNotSupportedError(RuntimeError):
184 """A `DatasetType` is not handled by this routine.
186 This can happen in a `Datastore` when a particular `DatasetType`
187 has no formatters associated with it.
188 """
190 pass
193class ValidationError(RuntimeError):
194 """Some sort of validation error has occurred."""
196 pass
199class EmptyQueryResultError(Exception):
200 """Exception raised when query methods return an empty result and
201 ``explain`` flag is set.
203 Parameters
204 ----------
205 reasons : `list` [`str`]
206 List of possible reasons for an empty query result.
207 """
209 def __init__(self, reasons: list[str]):
210 self.reasons = reasons
212 def __str__(self) -> str:
213 # There may be multiple reasons, format them into multiple lines.
214 return "Possible reasons for empty result:\n" + "\n".join(self.reasons)
217class UnknownButlerUserError(ButlerUserError):
218 """Raised when the server sends an ``error_type`` for which we don't know
219 the corresponding exception type. (This may happen if an old version of
220 the Butler client library connects to a new server).
221 """
223 error_type = "unknown"
226class InconsistentUniverseError(Exception):
227 """Raised when an imported dataset has a dimension universe that is
228 incompatible with the target butler universe.
229 """
232_USER_ERROR_TYPES: tuple[type[ButlerUserError], ...] = (
233 CalibrationLookupError,
234 CollectionCycleError,
235 CollectionTypeError,
236 DimensionNameError,
237 DimensionValueError,
238 DataIdValueError,
239 DatasetNotFoundError,
240 InconsistentDataIdError,
241 InvalidQueryError,
242 MissingCollectionError,
243 MissingDatasetTypeError,
244 UnimplementedQueryError,
245 UnknownButlerUserError,
246 UnknownComponentError,
247)
248_USER_ERROR_MAPPING = {e.error_type: e for e in _USER_ERROR_TYPES}
249assert len(_USER_ERROR_MAPPING) == len(_USER_ERROR_TYPES), (
250 "Subclasses of ButlerUserError must have unique 'error_type' property"
251)
254def create_butler_user_error(error_type: str, message: str) -> ButlerUserError:
255 """Instantiate one of the subclasses of `ButlerUserError` based on its
256 ``error_type`` string.
258 Parameters
259 ----------
260 error_type : `str`
261 The value from the ``error_type`` class attribute on the exception
262 subclass you wish to instantiate.
263 message : `str`
264 Detailed error message passed to the exception constructor.
265 """
266 cls = _USER_ERROR_MAPPING.get(error_type)
267 if cls is None:
268 raise UnknownButlerUserError(f"Unknown exception type '{error_type}': {message}")
269 return cls(message)