Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/core/files/uploadhandler.py: 76%

78 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Base file upload handler classes, and the built-in concrete subclasses 

3""" 

4import os 

5from io import BytesIO 

6 

7from django.conf import settings 

8from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile 

9from django.utils.module_loading import import_string 

10 

11__all__ = [ 

12 "UploadFileException", 

13 "StopUpload", 

14 "SkipFile", 

15 "FileUploadHandler", 

16 "TemporaryFileUploadHandler", 

17 "MemoryFileUploadHandler", 

18 "load_handler", 

19 "StopFutureHandlers", 

20] 

21 

22 

23class UploadFileException(Exception): 

24 """ 

25 Any error having to do with uploading files. 

26 """ 

27 

28 pass 

29 

30 

31class StopUpload(UploadFileException): 

32 """ 

33 This exception is raised when an upload must abort. 

34 """ 

35 

36 def __init__(self, connection_reset=False): 

37 """ 

38 If ``connection_reset`` is ``True``, Django knows will halt the upload 

39 without consuming the rest of the upload. This will cause the browser to 

40 show a "connection reset" error. 

41 """ 

42 self.connection_reset = connection_reset 

43 

44 def __str__(self): 

45 if self.connection_reset: 

46 return "StopUpload: Halt current upload." 

47 else: 

48 return "StopUpload: Consume request data, then halt." 

49 

50 

51class SkipFile(UploadFileException): 

52 """ 

53 This exception is raised by an upload handler that wants to skip a given file. 

54 """ 

55 

56 pass 

57 

58 

59class StopFutureHandlers(UploadFileException): 

60 """ 

61 Upload handlers that have handled a file and do not want future handlers to 

62 run should raise this exception instead of returning None. 

63 """ 

64 

65 pass 

66 

67 

68class FileUploadHandler: 

69 """ 

70 Base class for streaming upload handlers. 

71 """ 

72 

73 chunk_size = 64 * 2**10 # : The default chunk size is 64 KB. 

74 

75 def __init__(self, request=None): 

76 self.file_name = None 

77 self.content_type = None 

78 self.content_length = None 

79 self.charset = None 

80 self.content_type_extra = None 

81 self.request = request 

82 

83 def handle_raw_input( 

84 self, input_data, META, content_length, boundary, encoding=None 

85 ): 

86 """ 

87 Handle the raw input from the client. 

88 

89 Parameters: 

90 

91 :input_data: 

92 An object that supports reading via .read(). 

93 :META: 

94 ``request.META``. 

95 :content_length: 

96 The (integer) value of the Content-Length header from the 

97 client. 

98 :boundary: The boundary from the Content-Type header. Be sure to 

99 prepend two '--'. 

100 """ 

101 pass 

102 

103 def new_file( 

104 self, 

105 field_name, 

106 file_name, 

107 content_type, 

108 content_length, 

109 charset=None, 

110 content_type_extra=None, 

111 ): 

112 """ 

113 Signal that a new file has been started. 

114 

115 Warning: As with any data from the client, you should not trust 

116 content_length (and sometimes won't even get it). 

117 """ 

118 self.field_name = field_name 

119 self.file_name = file_name 

120 self.content_type = content_type 

121 self.content_length = content_length 

122 self.charset = charset 

123 self.content_type_extra = content_type_extra 

124 

125 def receive_data_chunk(self, raw_data, start): 

126 """ 

127 Receive data from the streamed upload parser. ``start`` is the position 

128 in the file of the chunk. 

129 """ 

130 raise NotImplementedError( 

131 "subclasses of FileUploadHandler must provide a receive_data_chunk() method" 

132 ) 

133 

134 def file_complete(self, file_size): 

135 """ 

136 Signal that a file has completed. File size corresponds to the actual 

137 size accumulated by all the chunks. 

138 

139 Subclasses should return a valid ``UploadedFile`` object. 

140 """ 

141 raise NotImplementedError( 

142 "subclasses of FileUploadHandler must provide a file_complete() method" 

143 ) 

144 

145 def upload_complete(self): 

146 """ 

147 Signal that the upload is complete. Subclasses should perform cleanup 

148 that is necessary for this handler. 

149 """ 

150 pass 

151 

152 def upload_interrupted(self): 

153 """ 

154 Signal that the upload was interrupted. Subclasses should perform 

155 cleanup that is necessary for this handler. 

156 """ 

157 pass 

158 

159 

160class TemporaryFileUploadHandler(FileUploadHandler): 

161 """ 

162 Upload handler that streams data into a temporary file. 

163 """ 

164 

165 def new_file(self, *args, **kwargs): 

166 """ 

167 Create the file object to append to as data is coming in. 

168 """ 

169 super().new_file(*args, **kwargs) 

170 self.file = TemporaryUploadedFile( 

171 self.file_name, self.content_type, 0, self.charset, self.content_type_extra 

172 ) 

173 

174 def receive_data_chunk(self, raw_data, start): 

175 self.file.write(raw_data) 

176 

177 def file_complete(self, file_size): 

178 self.file.seek(0) 

179 self.file.size = file_size 

180 return self.file 

181 

182 def upload_interrupted(self): 

183 if hasattr(self, "file"): 

184 temp_location = self.file.temporary_file_path() 

185 try: 

186 self.file.close() 

187 os.remove(temp_location) 

188 except FileNotFoundError: 

189 pass 

190 

191 

192class MemoryFileUploadHandler(FileUploadHandler): 

193 """ 

194 File upload handler to stream uploads into memory (used for small files). 

195 """ 

196 

197 def handle_raw_input( 

198 self, input_data, META, content_length, boundary, encoding=None 

199 ): 

200 """ 

201 Use the content_length to signal whether or not this handler should be 

202 used. 

203 """ 

204 # Check the content-length header to see if we should 

205 # If the post is too large, we cannot use the Memory handler. 

206 self.activated = content_length <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE 

207 

208 def new_file(self, *args, **kwargs): 

209 super().new_file(*args, **kwargs) 

210 if self.activated: 210 ↛ exitline 210 didn't return from function 'new_file', because the condition on line 210 was never false

211 self.file = BytesIO() 

212 raise StopFutureHandlers() 

213 

214 def receive_data_chunk(self, raw_data, start): 

215 """Add the data to the BytesIO file.""" 

216 if self.activated: 216 ↛ 219line 216 didn't jump to line 219, because the condition on line 216 was never false

217 self.file.write(raw_data) 

218 else: 

219 return raw_data 

220 

221 def file_complete(self, file_size): 

222 """Return a file object if this handler is activated.""" 

223 if not self.activated: 223 ↛ 224line 223 didn't jump to line 224, because the condition on line 223 was never true

224 return 

225 

226 self.file.seek(0) 

227 return InMemoryUploadedFile( 

228 file=self.file, 

229 field_name=self.field_name, 

230 name=self.file_name, 

231 content_type=self.content_type, 

232 size=file_size, 

233 charset=self.charset, 

234 content_type_extra=self.content_type_extra, 

235 ) 

236 

237 

238def load_handler(path, *args, **kwargs): 

239 """ 

240 Given a path to a handler, return an instance of that handler. 

241 

242 E.g.:: 

243 >>> from django.http import HttpRequest 

244 >>> request = HttpRequest() 

245 >>> load_handler( 

246 ... 'django.core.files.uploadhandler.TemporaryFileUploadHandler', 

247 ... request, 

248 ... ) 

249 <TemporaryFileUploadHandler object at 0x...> 

250 """ 

251 return import_string(path)(*args, **kwargs)