Coverage for /Users/davegaeddert/Development/dropseed/plain/plain/plain/exceptions.py: 50%

109 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-16 22:04 -0500

1""" 

2Global Plain exception and warning classes. 

3""" 

4import operator 

5 

6from plain.utils.hashable import make_hashable 

7 

8 

9class FieldDoesNotExist(Exception): 

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

11 

12 pass 

13 

14 

15class PackageRegistryNotReady(Exception): 

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

17 

18 pass 

19 

20 

21class ObjectDoesNotExist(Exception): 

22 """The requested object does not exist""" 

23 

24 silent_variable_failure = True 

25 

26 

27class MultipleObjectsReturned(Exception): 

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

29 

30 pass 

31 

32 

33class SuspiciousOperation(Exception): 

34 """The user did something suspicious""" 

35 

36 

37class SuspiciousMultipartForm(SuspiciousOperation): 

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

39 

40 pass 

41 

42 

43class SuspiciousFileOperation(SuspiciousOperation): 

44 """A Suspicious filesystem operation was attempted""" 

45 

46 pass 

47 

48 

49class DisallowedHost(SuspiciousOperation): 

50 """HTTP_HOST header contains invalid value""" 

51 

52 pass 

53 

54 

55class DisallowedRedirect(SuspiciousOperation): 

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

57 

58 pass 

59 

60 

61class TooManyFieldsSent(SuspiciousOperation): 

62 """ 

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

64 settings.DATA_UPLOAD_MAX_NUMBER_FIELDS. 

65 """ 

66 

67 pass 

68 

69 

70class TooManyFilesSent(SuspiciousOperation): 

71 """ 

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

73 settings.DATA_UPLOAD_MAX_NUMBER_FILES. 

74 """ 

75 

76 pass 

77 

78 

79class RequestDataTooBig(SuspiciousOperation): 

80 """ 

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

82 settings.DATA_UPLOAD_MAX_MEMORY_SIZE. 

83 """ 

84 

85 pass 

86 

87 

88class RequestAborted(Exception): 

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

90 

91 pass 

92 

93 

94class BadRequest(Exception): 

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

96 

97 pass 

98 

99 

100class PermissionDenied(Exception): 

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

102 

103 pass 

104 

105 

106class ViewDoesNotExist(Exception): 

107 """The requested view does not exist""" 

108 

109 pass 

110 

111 

112class ImproperlyConfigured(Exception): 

113 """Plain is somehow improperly configured""" 

114 

115 pass 

116 

117 

118class FieldError(Exception): 

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

120 

121 pass 

122 

123 

124NON_FIELD_ERRORS = "__all__" 

125 

126 

127class ValidationError(Exception): 

128 """An error while validating data.""" 

129 

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

131 """ 

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

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

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

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

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

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

138 """ 

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

140 

141 if isinstance(message, ValidationError): 

142 if hasattr(message, "error_dict"): 

143 message = message.error_dict 

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

145 message = message.error_list 

146 else: 

147 message, code, params = message.message, message.code, message.params 

148 

149 if isinstance(message, dict): 

150 self.error_dict = {} 

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

152 if not isinstance(messages, ValidationError): 

153 messages = ValidationError(messages) 

154 self.error_dict[field] = messages.error_list 

155 

156 elif isinstance(message, list): 

157 self.error_list = [] 

158 for message in message: 

159 # Normalize plain strings to instances of ValidationError. 

160 if not isinstance(message, ValidationError): 

161 message = ValidationError(message) 

162 if hasattr(message, "error_dict"): 

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

164 else: 

165 self.error_list.extend(message.error_list) 

166 

167 else: 

168 self.message = message 

169 self.code = code 

170 self.params = params 

171 self.error_list = [self] 

172 

173 @property 

174 def message_dict(self): 

175 # Trigger an AttributeError if this ValidationError 

176 # doesn't have an error_dict. 

177 getattr(self, "error_dict") 

178 

179 return dict(self) 

180 

181 @property 

182 def messages(self): 

183 if hasattr(self, "error_dict"): 

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

185 return list(self) 

186 

187 def update_error_dict(self, error_dict): 

188 if hasattr(self, "error_dict"): 

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

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

191 else: 

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

193 return error_dict 

194 

195 def __iter__(self): 

196 if hasattr(self, "error_dict"): 

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

198 yield field, list(ValidationError(errors)) 

199 else: 

200 for error in self.error_list: 

201 message = error.message 

202 if error.params: 

203 message %= error.params 

204 yield str(message) 

205 

206 def __str__(self): 

207 if hasattr(self, "error_dict"): 

208 return repr(dict(self)) 

209 return repr(list(self)) 

210 

211 def __repr__(self): 

212 return "ValidationError(%s)" % self 

213 

214 def __eq__(self, other): 

215 if not isinstance(other, ValidationError): 

216 return NotImplemented 

217 return hash(self) == hash(other) 

218 

219 def __hash__(self): 

220 if hasattr(self, "message"): 

221 return hash( 

222 ( 

223 self.message, 

224 self.code, 

225 make_hashable(self.params), 

226 ) 

227 ) 

228 if hasattr(self, "error_dict"): 

229 return hash(make_hashable(self.error_dict)) 

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

231 

232 

233class EmptyResultSet(Exception): 

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

235 

236 pass 

237 

238 

239class FullResultSet(Exception): 

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

241 

242 pass