Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/exceptions.py: 70%

109 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1""" 

2Global Plain exception and warning classes. 

3""" 

4 

5import operator 

6 

7from plain.utils.hashable import make_hashable 

8 

9 

10class FieldDoesNotExist(Exception): 

11 """The requested model field does not exist""" 

12 

13 pass 

14 

15 

16class PackageRegistryNotReady(Exception): 

17 """The plain.packages registry is not populated yet""" 

18 

19 pass 

20 

21 

22class ObjectDoesNotExist(Exception): 

23 """The requested object does not exist""" 

24 

25 silent_variable_failure = True 

26 

27 

28class MultipleObjectsReturned(Exception): 

29 """The query returned multiple objects when only one was expected.""" 

30 

31 pass 

32 

33 

34class SuspiciousOperation(Exception): 

35 """The user did something suspicious""" 

36 

37 

38class SuspiciousMultipartForm(SuspiciousOperation): 

39 """Suspect MIME request in multipart form data""" 

40 

41 pass 

42 

43 

44class SuspiciousFileOperation(SuspiciousOperation): 

45 """A Suspicious filesystem operation was attempted""" 

46 

47 pass 

48 

49 

50class DisallowedHost(SuspiciousOperation): 

51 """HTTP_HOST header contains invalid value""" 

52 

53 pass 

54 

55 

56class DisallowedRedirect(SuspiciousOperation): 

57 """Redirect to scheme not in allowed list""" 

58 

59 pass 

60 

61 

62class TooManyFieldsSent(SuspiciousOperation): 

63 """ 

64 The number of fields in a GET or POST request exceeded 

65 settings.DATA_UPLOAD_MAX_NUMBER_FIELDS. 

66 """ 

67 

68 pass 

69 

70 

71class TooManyFilesSent(SuspiciousOperation): 

72 """ 

73 The number of fields in a GET or POST request exceeded 

74 settings.DATA_UPLOAD_MAX_NUMBER_FILES. 

75 """ 

76 

77 pass 

78 

79 

80class RequestDataTooBig(SuspiciousOperation): 

81 """ 

82 The size of the request (excluding any file uploads) exceeded 

83 settings.DATA_UPLOAD_MAX_MEMORY_SIZE. 

84 """ 

85 

86 pass 

87 

88 

89class RequestAborted(Exception): 

90 """The request was closed before it was completed, or timed out.""" 

91 

92 pass 

93 

94 

95class BadRequest(Exception): 

96 """The request is malformed and cannot be processed.""" 

97 

98 pass 

99 

100 

101class PermissionDenied(Exception): 

102 """The user did not have permission to do that""" 

103 

104 pass 

105 

106 

107class ViewDoesNotExist(Exception): 

108 """The requested view does not exist""" 

109 

110 pass 

111 

112 

113class ImproperlyConfigured(Exception): 

114 """Plain is somehow improperly configured""" 

115 

116 pass 

117 

118 

119class FieldError(Exception): 

120 """Some kind of problem with a model field.""" 

121 

122 pass 

123 

124 

125NON_FIELD_ERRORS = "__all__" 

126 

127 

128class ValidationError(Exception): 

129 """An error while validating data.""" 

130 

131 def __init__(self, message, code=None, params=None): 

132 """ 

133 The `message` argument can be a single error, a list of errors, or a 

134 dictionary that maps field names to lists of errors. What we define as 

135 an "error" can be either a simple string or an instance of 

136 ValidationError with its message attribute set, and what we define as 

137 list or dictionary can be an actual `list` or `dict` or an instance 

138 of ValidationError with its `error_list` or `error_dict` attribute set. 

139 """ 

140 super().__init__(message, code, params) 

141 

142 if isinstance(message, ValidationError): 

143 if hasattr(message, "error_dict"): 

144 message = message.error_dict 

145 elif not hasattr(message, "message"): 

146 message = message.error_list 

147 else: 

148 message, code, params = message.message, message.code, message.params 

149 

150 if isinstance(message, dict): 

151 self.error_dict = {} 

152 for field, messages in message.items(): 

153 if not isinstance(messages, ValidationError): 

154 messages = ValidationError(messages) 

155 self.error_dict[field] = messages.error_list 

156 

157 elif isinstance(message, list): 

158 self.error_list = [] 

159 for message in message: 

160 # Normalize plain strings to instances of ValidationError. 

161 if not isinstance(message, ValidationError): 

162 message = ValidationError(message) 

163 if hasattr(message, "error_dict"): 

164 self.error_list.extend(sum(message.error_dict.values(), [])) 

165 else: 

166 self.error_list.extend(message.error_list) 

167 

168 else: 

169 self.message = message 

170 self.code = code 

171 self.params = params 

172 self.error_list = [self] 

173 

174 @property 

175 def message_dict(self): 

176 # Trigger an AttributeError if this ValidationError 

177 # doesn't have an error_dict. 

178 getattr(self, "error_dict") 

179 

180 return dict(self) 

181 

182 @property 

183 def messages(self): 

184 if hasattr(self, "error_dict"): 

185 return sum(dict(self).values(), []) 

186 return list(self) 

187 

188 def update_error_dict(self, error_dict): 

189 if hasattr(self, "error_dict"): 

190 for field, error_list in self.error_dict.items(): 

191 error_dict.setdefault(field, []).extend(error_list) 

192 else: 

193 error_dict.setdefault(NON_FIELD_ERRORS, []).extend(self.error_list) 

194 return error_dict 

195 

196 def __iter__(self): 

197 if hasattr(self, "error_dict"): 

198 for field, errors in self.error_dict.items(): 

199 yield field, list(ValidationError(errors)) 

200 else: 

201 for error in self.error_list: 

202 message = error.message 

203 if error.params: 

204 message %= error.params 

205 yield str(message) 

206 

207 def __str__(self): 

208 if hasattr(self, "error_dict"): 

209 return repr(dict(self)) 

210 return repr(list(self)) 

211 

212 def __repr__(self): 

213 return f"ValidationError({self})" 

214 

215 def __eq__(self, other): 

216 if not isinstance(other, ValidationError): 

217 return NotImplemented 

218 return hash(self) == hash(other) 

219 

220 def __hash__(self): 

221 if hasattr(self, "message"): 

222 return hash( 

223 ( 

224 self.message, 

225 self.code, 

226 make_hashable(self.params), 

227 ) 

228 ) 

229 if hasattr(self, "error_dict"): 

230 return hash(make_hashable(self.error_dict)) 

231 return hash(tuple(sorted(self.error_list, key=operator.attrgetter("message")))) 

232 

233 

234class EmptyResultSet(Exception): 

235 """A database query predicate is impossible.""" 

236 

237 pass 

238 

239 

240class FullResultSet(Exception): 

241 """A database query predicate is matches everything.""" 

242 

243 pass