| 245 | pass |
| 246 | |
| 247 | async def parse(self) -> FormData: |
| 248 | class="cm"># Parse the Content-Type header to get the multipart boundary. |
| 249 | _, params = parse_options_header(self.headers[class="st">"Content-Type"]) |
| 250 | charset = params.get(bclass="st">"charset", class="st">"utf-8") |
| 251 | if isinstance(charset, bytes): |
| 252 | charset = charset.decode(class="st">"latin-1") |
| 253 | self._charset = charset |
| 254 | try: |
| 255 | boundary = params[bclass="st">"boundary"] |
| 256 | except KeyError: |
| 257 | raise MultiPartException(class="st">"Missing boundary in multipart.") |
| 258 | |
| 259 | class="cm"># Callbacks dictionary. |
| 260 | callbacks: MultipartCallbacks = { |
| 261 | class="st">"on_part_begin": self.on_part_begin, |
| 262 | class="st">"on_part_data": self.on_part_data, |
| 263 | class="st">"on_part_end": self.on_part_end, |
| 264 | class="st">"on_header_field": self.on_header_field, |
| 265 | class="st">"on_header_value": self.on_header_value, |
| 266 | class="st">"on_header_end": self.on_header_end, |
| 267 | class="st">"on_headers_finished": self.on_headers_finished, |
| 268 | class="st">"on_end": self.on_end, |
| 269 | } |
| 270 | |
| 271 | class="cm"># Create the parser. |
| 272 | parser = multipart.MultipartParser(boundary, callbacks) |
| 273 | try: |
| 274 | class="cm"># Feed the parser with data from the request. |
| 275 | async for chunk in self.stream: |
| 276 | parser.write(chunk) |
| 277 | class="cm"># Write file data, it needs to use await with the UploadFile methods |
| 278 | class="cm"># that call the corresponding file methods *in a threadpool*, |
| 279 | class="cm"># otherwise, if they were called directly in the callback methods above |
| 280 | class="cm"># (regular, non-async functions), that would block the event loop in |
| 281 | class="cm"># the main thread. |
| 282 | for part, data in self._file_parts_to_write: |
| 283 | assert part.file class="cm"># for type checkers |
| 284 | await part.file.write(data) |
| 285 | for part in self._file_parts_to_finish: |
| 286 | assert part.file class="cm"># for type checkers |
| 287 | await part.file.seek(0) |
| 288 | self._file_parts_to_write.clear() |
| 289 | self._file_parts_to_finish.clear() |
| 290 | parser.finalize() |
| 291 | except (MultiPartException, OSError) as exc: |
| 292 | class="cm"># Close all the files if there was an error. |
| 293 | for file in self._files_to_close_on_error: |
| 294 | file.close() |
| 295 | raise exc |
| 296 | |
| 297 | return FormData(self.items) |