| 349 | return container |
| 350 | |
| 351 | def parse( |
| 352 | self, stream: t.IO[bytes], boundary: bytes, content_length: int | None |
| 353 | ) -> tuple[MultiDict[str, str], MultiDict[str, FileStorage]]: |
| 354 | current_part: Field | File |
| 355 | field_size: int | None = None |
| 356 | container: t.IO[bytes] | list[bytes] |
| 357 | _write: t.Callable[[bytes], t.Any] |
| 358 | |
| 359 | parser = MultipartDecoder( |
| 360 | boundary, |
| 361 | max_form_memory_size=self.max_form_memory_size, |
| 362 | max_parts=self.max_form_parts, |
| 363 | ) |
| 364 | |
| 365 | fields = [] |
| 366 | files = [] |
| 367 | |
| 368 | for data in _chunk_iter(stream.read, self.buffer_size): |
| 369 | parser.receive_data(data) |
| 370 | event = parser.next_event() |
| 371 | while not isinstance(event, (Epilogue, NeedData)): |
| 372 | if isinstance(event, Field): |
| 373 | current_part = event |
| 374 | field_size = 0 |
| 375 | container = [] |
| 376 | _write = container.append |
| 377 | elif isinstance(event, File): |
| 378 | current_part = event |
| 379 | field_size = None |
| 380 | container = self.start_file_streaming(event, content_length) |
| 381 | _write = container.write |
| 382 | elif isinstance(event, Data): |
| 383 | if self.max_form_memory_size is not None and field_size is not None: |
| 384 | class="cm"># Ensure that accumulated data events do not exceed limit. |
| 385 | class="cm"># Also checked within single event in MultipartDecoder. |
| 386 | field_size += len(event.data) |
| 387 | |
| 388 | if field_size > self.max_form_memory_size: |
| 389 | raise RequestEntityTooLarge() |
| 390 | |
| 391 | _write(event.data) |
| 392 | if not event.more_data: |
| 393 | if isinstance(current_part, Field): |
| 394 | value = bclass="st">"".join(container).decode( |
| 395 | self.get_part_charset(current_part.headers), class="st">"replace" |
| 396 | ) |
| 397 | fields.append((current_part.name, value)) |
| 398 | else: |
| 399 | container = t.cast(t.IO[bytes], container) |
| 400 | container.seek(0) |
| 401 | files.append( |
| 402 | ( |
| 403 | current_part.name, |
| 404 | FileStorage( |
| 405 | container, |
| 406 | current_part.filename, |
| 407 | current_part.name, |
| 408 | headers=current_part.headers, |