Coverage for python/lsst/daf/butler/_exceptions.py: 74%

31 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-12 10:05 +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/>. 

27 

28"""Specialized Butler exceptions.""" 

29__all__ = ( 

30 "DatasetNotFoundError", 

31 "ButlerUserError", 

32 "DatasetTypeNotSupportedError", 

33 "EmptyQueryResultError", 

34 "MissingDatasetTypeError", 

35 "ValidationError", 

36) 

37 

38from ._exceptions_legacy import DatasetTypeError 

39 

40 

41class ButlerUserError(Exception): 

42 """Base class for Butler exceptions that contain a user-facing error 

43 message. 

44 

45 Parameters 

46 ---------- 

47 detail : `str` 

48 Details about the error that occurred. 

49 """ 

50 

51 # When used with Butler server, exceptions inheriting from 

52 # this class will be sent to the client side and re-raised by RemoteButler 

53 # there. Be careful that error messages do not contain security-sensitive 

54 # information. 

55 # 

56 # This should only be used for "expected" errors that occur because of 

57 # errors in user-supplied data passed to Butler methods. It should not be 

58 # used for any issues caused by the Butler configuration file, errors in 

59 # the library code itself or the underlying databases. 

60 # 

61 # When you create a new subclass of this type, add it to the list in 

62 # _USER_ERROR_TYPES below. 

63 

64 error_type: str 

65 """Unique name for this error type, used to identify it when sending 

66 information about the error to the client. 

67 """ 

68 

69 def __init__(self, detail: str): 

70 return super().__init__(detail) 

71 

72 

73class DatasetNotFoundError(LookupError, ButlerUserError): 

74 """The requested dataset could not be found.""" 

75 

76 error_type = "dataset_not_found" 

77 

78 

79class MissingDatasetTypeError(DatasetTypeError, KeyError, ButlerUserError): 

80 """Exception raised when a dataset type does not exist.""" 

81 

82 error_type = "missing_dataset_type" 

83 

84 

85class DatasetTypeNotSupportedError(RuntimeError): 

86 """A `DatasetType` is not handled by this routine. 

87 

88 This can happen in a `Datastore` when a particular `DatasetType` 

89 has no formatters associated with it. 

90 """ 

91 

92 pass 

93 

94 

95class ValidationError(RuntimeError): 

96 """Some sort of validation error has occurred.""" 

97 

98 pass 

99 

100 

101class EmptyQueryResultError(Exception): 

102 """Exception raised when query methods return an empty result and `explain` 

103 flag is set. 

104 

105 Parameters 

106 ---------- 

107 reasons : `list` [`str`] 

108 List of possible reasons for an empty query result. 

109 """ 

110 

111 def __init__(self, reasons: list[str]): 

112 self.reasons = reasons 

113 

114 def __str__(self) -> str: 

115 # There may be multiple reasons, format them into multiple lines. 

116 return "Possible reasons for empty result:\n" + "\n".join(self.reasons) 

117 

118 

119class UnknownButlerUserError(ButlerUserError): 

120 """Raised when the server sends an ``error_type`` for which we don't know 

121 the corresponding exception type. (This may happen if an old version of 

122 the Butler client library connects to a new server). 

123 """ 

124 

125 error_type = "unknown" 

126 

127 

128_USER_ERROR_TYPES: tuple[type[ButlerUserError], ...] = ( 

129 DatasetNotFoundError, 

130 MissingDatasetTypeError, 

131 UnknownButlerUserError, 

132) 

133_USER_ERROR_MAPPING = {e.error_type: e for e in _USER_ERROR_TYPES} 

134assert len(_USER_ERROR_MAPPING) == len( 

135 _USER_ERROR_TYPES 

136), "Subclasses of ButlerUserError must have unique 'error_type' property" 

137 

138 

139def create_butler_user_error(error_type: str, message: str) -> ButlerUserError: 

140 """Instantiate one of the subclasses of `ButlerUserError` based on its 

141 ``error_type`` string. 

142 

143 Parameters 

144 ---------- 

145 error_type : `str` 

146 The value from the ``error_type`` class attribute on the exception 

147 subclass you wish to instantiate. 

148 message : `str` 

149 Detailed error message passed to the exception constructor. 

150 """ 

151 cls = _USER_ERROR_MAPPING.get(error_type) 

152 if cls is None: 

153 raise UnknownButlerUserError(f"Unknown exception type '{error_type}': {message}") 

154 return cls(message)