A streaming HTTP response class with an iterator as content. This should only be iterated once, when the response is streamed to the client. However, it can be appended to or replaced with a new iterator that wraps the original content (or yields entirely new content).
| 441 | |
| 442 | |
| 443 | class StreamingHttpResponse(HttpResponseBase): |
| 444 | """ |
| 445 | A streaming HTTP response class with an iterator as content. |
| 446 | |
| 447 | This should only be iterated once, when the response is streamed to the |
| 448 | client. However, it can be appended to or replaced with a new iterator |
| 449 | that wraps the original content (or yields entirely new content). |
| 450 | """ |
| 451 | |
| 452 | streaming = True |
| 453 | |
| 454 | def __init__(self, streaming_content=(), *args, **kwargs): |
| 455 | super().__init__(*args, **kwargs) |
| 456 | # `streaming_content` should be an iterable of bytestrings. |
| 457 | # See the `streaming_content` property methods. |
| 458 | self.streaming_content = streaming_content |
| 459 | |
| 460 | def __repr__(self): |
| 461 | return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % { |
| 462 | "cls": self.__class__.__qualname__, |
| 463 | "status_code": self.status_code, |
| 464 | "content_type": self._content_type_for_repr, |
| 465 | } |
| 466 | |
| 467 | @property |
| 468 | def content(self): |
| 469 | raise AttributeError( |
| 470 | "This %s instance has no `content` attribute. Use " |
| 471 | "`streaming_content` instead." % self.__class__.__name__ |
| 472 | ) |
| 473 | |
| 474 | @property |
| 475 | def text(self): |
| 476 | raise AttributeError( |
| 477 | "This %s instance has no `text` attribute." % self.__class__.__name__ |
| 478 | ) |
| 479 | |
| 480 | @property |
| 481 | def streaming_content(self): |
| 482 | if self.is_async: |
| 483 | # pull to lexical scope to capture fixed reference in case |
| 484 | # streaming_content is set again later. |
| 485 | _iterator = self._iterator |
| 486 | |
| 487 | async def awrapper(): |
| 488 | async for part in _iterator: |
| 489 | yield self.make_bytes(part) |
| 490 | |
| 491 | return awrapper() |
| 492 | else: |
| 493 | return map(self.make_bytes, self._iterator) |
| 494 | |
| 495 | @streaming_content.setter |
| 496 | def streaming_content(self, value): |
| 497 | self._set_streaming_content(value) |
| 498 | |
| 499 | def _set_streaming_content(self, value): |
| 500 | # Ensure we can never iterate on "value" more than once. |
no outgoing calls
searching dependent graphs…