Repository: sanic-org/sanic
Pull request #2729 (2023-03-30 08:46:16)

Handle case when `headers` argument of `ResponseStream` constructor is `None`
The default value for the `headers` argument is `None`. This leads to an Exception when `headers` is passed to the `Header` constructor, which is expecting a Mapping type. 

codecov[bot]: ## [Codecov](https://codecov.io/gh/sanic-org/sanic/pull/2729?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) Report
Patch coverage has no change and project coverage change: **`-0.098`** :warning:
> Comparison is base [(`6eaab2a`)](https://codecov.io/gh/sanic-org/sanic/commit/6eaab2a7e5be418385856371fdaebe4701f8c4fc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) 88.938% compared to head [(`07caa2c`)](https://codecov.io/gh/sanic-org/sanic/pull/2729?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) 88.840%.

<details><summary>Additional details and impacted files</summary>


```diff
@@              Coverage Diff              @@
##              main     #2729       +/-   ##
=============================================
- Coverage   88.938%   88.840%   -0.098%     
=============================================
  Files           92        92               
  Lines         6952      6954        +2     
  Branches      1185      1186        +1     
=============================================
- Hits          6183      6178        -5     
- Misses         527       531        +4     
- Partials       242       245        +3     
```


| [Impacted Files](https://codecov.io/gh/sanic-org/sanic/pull/2729?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org) | Coverage Δ | |
|---|---|---|
| [sanic/response/types.py](https://codecov.io/gh/sanic-org/sanic/pull/2729?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org#diff-c2FuaWMvcmVzcG9uc2UvdHlwZXMucHk=) | `83.888% <0.000%> (-0.943%)` | :arrow_down: |

... and [2 files with indirect coverage changes](https://codecov.io/gh/sanic-org/sanic/pull/2729/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/sanic-org/sanic/pull/2729?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sanic-org).

liamcoatman: @ahopkins I've added a test, which was failing prior to my fix and is now passing. I've probably put it in the wrong place, so let me know if you want me to move it. 
ahopkins: It's a long file, but I'd dump it here: https://github.com/sanic-org/sanic/blob/main/tests/test_response.py
liamcoatman: In case you missed it, I moved the test to `test_response.py` as suggested
QylinFly: ResponseStream is not stream,bug?
ahopkins: > ResponseStream is not stream,bug?

Sorry, I don't understand your question.

Patch:
diff --git a/sanic/response/types.py b/sanic/response/types.py
index 3c897935..73a997da 100644
--- a/sanic/response/types.py
+++ b/sanic/response/types.py
@@ -522,7 +522,9 @@ class ResponseStream:
         headers: Optional[Union[Header, Dict[str, str]]] = None,
         content_type: Optional[str] = None,
     ):
-        if not isinstance(headers, Header):
+        if headers is None:
+            headers = Header()
+        elif not isinstance(headers, Header):
             headers = Header(headers)
         self.streaming_fn = streaming_fn
         self.status = status


Test patch:
diff --git a/tests/test_response.py b/tests/test_response.py
index 8d2d9e08..65a875cd 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -23,6 +23,7 @@ from sanic.compat import Header
 from sanic.cookies import CookieJar
 from sanic.response import (
     HTTPResponse,
+    ResponseStream,
     empty,
     file,
     file_stream,
@@ -943,3 +944,17 @@ def test_file_validating_304_response(
     )
     assert response.status == 304
     assert response.body == b""
+
+
+def test_stream_response_with_default_headers(app: Sanic):
+    async def sample_streaming_fn(response_):
+        await response_.write("foo")
+
+    @app.route("/")
+    async def test(request: Request):
+        return ResponseStream(sample_streaming_fn, content_type="text/csv")
+
+    _, response = app.test_client.get("/")
+    assert response.text == "foo"
+    assert response.headers["Transfer-Encoding"] == "chunked"
+    assert response.headers["Content-Type"] == "text/csv"