From 2fb3ce52ca0e1f97979a6bf67e970346d0165eb3 Mon Sep 17 00:00:00 2001 From: declaresub Date: Wed, 18 Aug 2021 14:55:56 -0400 Subject: [PATCH 1/7] Implement EmptyResponse class (#1178). --- docs/responses.md | 23 ++++++++++++++++++++ starlette/responses.py | 48 +++++++++++++++++++++++++++++++++++++++++ tests/test_responses.py | 32 +++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/docs/responses.md b/docs/responses.md index c4cd84ed3..2717f6bb4 100644 --- a/docs/responses.md +++ b/docs/responses.md @@ -182,6 +182,29 @@ async def app(scope, receive, send): await response(scope, receive, send) ``` +### EmptyResponse + +Supplies an empty message body as the response. + +In particular, use an EmptyResponse object for 1xx, 204, 205, 304 responses as it sets or omits a Content-Length header as appropriate. + +Signature: `Response(status_code:int, headers: typing.Optional[typing.Dict[str, str]] = None, background: typing.Optional[BackgroundTask] = None)` + +* `status_code` - An integer HTTP status code. +* `headers` - A dictionary of strings. +* `background` - A BackgroundTask to be executed when the response is sent. + + +```python +from starlette.responses import EmptyResponse + + +async def app(scope, receive, send): + assert scope['type'] == 'http' + response = EmptyResponse(status_code=204) + await response(scope, receive, send) +``` + ## Third party middleware ### [SSEResponse(EventSourceResponse)](https://github.com/sysid/sse-starlette) diff --git a/starlette/responses.py b/starlette/responses.py index d03df2329..b12f6696e 100644 --- a/starlette/responses.py +++ b/starlette/responses.py @@ -311,3 +311,51 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: ) if self.background is not None: await self.background() + + +class EmptyResponse(Response): + """Response to be sent with status code 1xx, 204, 205, 304, or whenever + an empty response is intended.""" + + def __init__( + self, + status_code: int, + headers: typing.Optional[typing.Dict[str, str]] = None, + background: typing.Optional[BackgroundTask] = None, + ) -> None: + super().__init__( + content=b"", status_code=status_code, headers=headers, background=background + ) + + def init_headers(self, headers: typing.Mapping[str, str] = None) -> None: + byte_headers: typing.Dict[bytes, bytes] = ( + { + k.lower().encode("latin-1"): v.encode("latin-1") + for k, v in headers.items() + } + if headers + else {} + ) + + if self.status_code < 200 or self.status_code == 204: + # Response must not have a content-length header. See + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 + if b"content-length" in byte_headers: + del byte_headers[b"content-length"] + elif self.status_code == 205: + # Response can either have a content-length header or a + # transfer-encoding: chunked header. + # We choose to ensure a content-length header. + # https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.6 + byte_headers[b"content-length"] = b"0" + elif self.status_code == 304: + # A 304 Not Modfied response may contain a content-length header + # whose value is the length of + # message that would have been sent in a 200 OK response. + # So we leave the headers as is. + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 + pass + else: + byte_headers[b"content-length"] = b"0" + + self.raw_headers = [(k, v) for k, v in byte_headers.items()] diff --git a/tests/test_responses.py b/tests/test_responses.py index baba549ba..56c0a4a3a 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -7,6 +7,7 @@ from starlette.background import BackgroundTask from starlette.requests import Request from starlette.responses import ( + EmptyResponse, FileResponse, JSONResponse, RedirectResponse, @@ -309,3 +310,34 @@ def test_head_method(test_client_factory): client = test_client_factory(app) response = client.head("/") assert response.text == "" + + +def test_empty_response_100(): + response = EmptyResponse(status_code=100) + assert "content-length" not in response.headers + + +def test_empty_response_200(): + response = EmptyResponse(status_code=200) + assert response.headers["content-length"] == "0" + + +def test_empty_response_204(): + response = EmptyResponse(status_code=204) + assert "content-length" not in response.headers + + +def test_empty_response_204_removing_header(): + response = EmptyResponse(status_code=204, headers={"content-length": "0"}) + assert "content-length" not in response.headers + + +def test_empty_response_205(): + response = EmptyResponse(status_code=205) + assert response.headers["content-length"] == "0" + + +def test_empty_response_304(): + headers = {"content-length": "43"} + response = EmptyResponse(status_code=304, headers=headers) + assert response.headers["content-length"] == "43" From 7e21c3a1404209c0500d0da02eb44d647c46b700 Mon Sep 17 00:00:00 2001 From: declaresub Date: Wed, 18 Aug 2021 17:30:00 -0400 Subject: [PATCH 2/7] Fix cut-and-paste error in EmptyResponse.__init__ signature. --- docs/responses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/responses.md b/docs/responses.md index 2717f6bb4..ad14cfd85 100644 --- a/docs/responses.md +++ b/docs/responses.md @@ -188,7 +188,7 @@ Supplies an empty message body as the response. In particular, use an EmptyResponse object for 1xx, 204, 205, 304 responses as it sets or omits a Content-Length header as appropriate. -Signature: `Response(status_code:int, headers: typing.Optional[typing.Dict[str, str]] = None, background: typing.Optional[BackgroundTask] = None)` +Signature: `EmptyResponse(status_code:int, headers: typing.Optional[typing.Dict[str, str]] = None, background: typing.Optional[BackgroundTask] = None)` * `status_code` - An integer HTTP status code. * `headers` - A dictionary of strings. From 2947c5d8eda141e621ee970785ca252f96ac58ed Mon Sep 17 00:00:00 2001 From: declaresub Date: Thu, 16 Dec 2021 13:27:00 -0500 Subject: [PATCH 3/7] Remove EmptyResponse class in favor of setting header/body in Response.__call__. --- docs/responses.md | 23 ------- starlette/responses.py | 92 +++++++++++++-------------- tests/test_responses.py | 134 +++++++++++++++++++++++++++++++--------- 3 files changed, 146 insertions(+), 103 deletions(-) diff --git a/docs/responses.md b/docs/responses.md index ad14cfd85..c4cd84ed3 100644 --- a/docs/responses.md +++ b/docs/responses.md @@ -182,29 +182,6 @@ async def app(scope, receive, send): await response(scope, receive, send) ``` -### EmptyResponse - -Supplies an empty message body as the response. - -In particular, use an EmptyResponse object for 1xx, 204, 205, 304 responses as it sets or omits a Content-Length header as appropriate. - -Signature: `EmptyResponse(status_code:int, headers: typing.Optional[typing.Dict[str, str]] = None, background: typing.Optional[BackgroundTask] = None)` - -* `status_code` - An integer HTTP status code. -* `headers` - A dictionary of strings. -* `background` - A BackgroundTask to be executed when the response is sent. - - -```python -from starlette.responses import EmptyResponse - - -async def app(scope, receive, send): - assert scope['type'] == 'http' - response = EmptyResponse(status_code=204) - await response(scope, receive, send) -``` - ## Third party middleware ### [SSEResponse(EventSourceResponse)](https://github.com/sysid/sse-starlette) diff --git a/starlette/responses.py b/starlette/responses.py index b12f6696e..a94db0e55 100644 --- a/starlette/responses.py +++ b/starlette/responses.py @@ -129,14 +129,54 @@ def delete_cookie(self, key: str, path: str = "/", domain: str = None) -> None: self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain) async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if self.status_code < 200: + # Response is terminated after the status line. So no headers and no body. + # https://datatracker.ietf.org/doc/html/rfc7231#section-6.2 + raw_headers = [] + body = b'' + elif self.status_code == 204: + # Response must not have a content-length header. See + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 + # http spec does not appear to say whether or not there can be a content-type header. Some clients + # will attempt to parse the message body if there is a content-type header, so we ensure that there isn't one in + # the response. + raw_headers = [header for header in self.raw_headers if header[0] not in (b'content-length', b'content-type')] + body = b'' + elif self.status_code == 205: + # Response must not include a body. + # Response can either have a content-length: 0 header or a + # transfer-encoding: chunked header. + # We check for a transfer-encoding header. If not found, we ensure the presence of + # the content-length header. + # https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.6 + raw_headers = [header for header in self.raw_headers if header[0] not in (b'content-length', b'content-type')] + for header in self.raw_headers: + if header[0] == b'transfer-encoding': + break + else: + raw_headers.append((b'content-length', b'0')) + body = b'' + elif self.status_code == 304: + # A 304 Not Modfied response may contain a transfer-encoding header, or content-length header + # whose value is the length of + # message that would have been sent in a 200 OK response. + # So we leave the headers as is. + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1 + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 + raw_headers = self.raw_headers + body = b'' + else: + raw_headers = self.raw_headers + body = self.body + await send( { "type": "http.response.start", "status": self.status_code, - "headers": self.raw_headers, + "headers": raw_headers, } ) - await send({"type": "http.response.body", "body": self.body}) + await send({"type": "http.response.body", "body": body}) if self.background is not None: await self.background() @@ -311,51 +351,3 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: ) if self.background is not None: await self.background() - - -class EmptyResponse(Response): - """Response to be sent with status code 1xx, 204, 205, 304, or whenever - an empty response is intended.""" - - def __init__( - self, - status_code: int, - headers: typing.Optional[typing.Dict[str, str]] = None, - background: typing.Optional[BackgroundTask] = None, - ) -> None: - super().__init__( - content=b"", status_code=status_code, headers=headers, background=background - ) - - def init_headers(self, headers: typing.Mapping[str, str] = None) -> None: - byte_headers: typing.Dict[bytes, bytes] = ( - { - k.lower().encode("latin-1"): v.encode("latin-1") - for k, v in headers.items() - } - if headers - else {} - ) - - if self.status_code < 200 or self.status_code == 204: - # Response must not have a content-length header. See - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 - if b"content-length" in byte_headers: - del byte_headers[b"content-length"] - elif self.status_code == 205: - # Response can either have a content-length header or a - # transfer-encoding: chunked header. - # We choose to ensure a content-length header. - # https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.6 - byte_headers[b"content-length"] = b"0" - elif self.status_code == 304: - # A 304 Not Modfied response may contain a content-length header - # whose value is the length of - # message that would have been sent in a 200 OK response. - # So we leave the headers as is. - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 - pass - else: - byte_headers[b"content-length"] = b"0" - - self.raw_headers = [(k, v) for k, v in byte_headers.items()] diff --git a/tests/test_responses.py b/tests/test_responses.py index 56c0a4a3a..eb028e13f 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -1,4 +1,5 @@ import os +import itertools import anyio import pytest @@ -7,13 +8,13 @@ from starlette.background import BackgroundTask from starlette.requests import Request from starlette.responses import ( - EmptyResponse, FileResponse, JSONResponse, RedirectResponse, Response, StreamingResponse, ) +from starlette.types import Message def test_text_response(test_client_factory): @@ -311,33 +312,106 @@ def test_head_method(test_client_factory): response = client.head("/") assert response.text == "" +@pytest.mark.anyio +@pytest.mark.parametrize('response_cls, status_code', list(itertools.product(*[[Response, JSONResponse], [100, 101, 102]]))) +async def test_response_1xx(response_cls, status_code): + scope = {} + async def receive(): + return {} + async def send(message: dict): + if message['type'] == "http.response.start": + # also ensures that self.raw_headers is not None + assert len(message['headers']) == 0 + elif message['type'] == "http.response.body": + # per ASGI, if body key is missing, default is False + assert "body" not in message or message["body"] == b'' + assert "more_body" not in message or message["more_body"] is False + else: + pass + + response = response_cls(status_code=status_code) + await response.__call__(scope, receive, send) + +@pytest.mark.anyio +@pytest.mark.parametrize('response_cls, content', itertools.product(*[[Response, JSONResponse], [None, 'test']])) +async def test_response_204(response_cls, content): + scope = {} + async def receive(): + return {} + async def send(message: dict): + if message['type'] == "http.response.start": + header_map = dict(message['headers']) + assert b"content-length" not in header_map + assert b"content-type" not in header_map + elif message['type'] == "http.response.body": + # per ASGI, if body key is missing, default is False + assert "body" not in message or message["body"] == b'' + assert "more_body" not in message or message["more_body"] is False + else: + pass + + response = response_cls(status_code=204, content=content) + await response.__call__(scope, receive, send) + + +@pytest.mark.anyio +@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +async def test_response_205_with_te_header(response_cls): + scope = {} + async def receive(): + return {} + async def send(message: dict): + if message['type'] == "http.response.start": + header_map = dict(message['headers']) + assert header_map[b'transfer-encoding'] == b'chunked' + assert b"content-length" not in header_map + assert b"content-type" not in header_map + elif message['type'] == "http.response.body": + # per ASGI, if body key is missing, default is False + assert "body" not in message or message["body"] == b'' + assert "more_body" not in message or message["more_body"] is False + else: + pass + + response = response_cls(status_code=205, headers={'transfer-encoding': 'chunked'}) + await response.__call__(scope, receive, send) + +@pytest.mark.anyio +@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +async def test_response_205_with_cl_header(response_cls): + scope = {} + async def receive(): + return {} + async def send(message: dict): + if message['type'] == "http.response.start": + header_map = dict(message['headers']) + assert header_map[b'content-length'] == b'0' + assert b"content-type" not in header_map + elif message['type'] == "http.response.body": + # per ASGI, if body key is missing, default is False + assert "body" not in message or message["body"] == b'' + assert "more_body" not in message or message["more_body"] is False + else: + pass + + response = response_cls(status_code=205) + await response.__call__(scope, receive, send) + +@pytest.mark.anyio +@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +async def test_response_304(response_cls): + scope = {} + async def receive(): + return {} + async def send(message: dict): + if message['type'] == "http.response.start": + pass + elif message['type'] == "http.response.body": + # per ASGI, 'body', 'more_body' are optional. + assert "body" not in message or message["body"] == b'' + assert "more_body" not in message or message["more_body"] is False + else: + pass -def test_empty_response_100(): - response = EmptyResponse(status_code=100) - assert "content-length" not in response.headers - - -def test_empty_response_200(): - response = EmptyResponse(status_code=200) - assert response.headers["content-length"] == "0" - - -def test_empty_response_204(): - response = EmptyResponse(status_code=204) - assert "content-length" not in response.headers - - -def test_empty_response_204_removing_header(): - response = EmptyResponse(status_code=204, headers={"content-length": "0"}) - assert "content-length" not in response.headers - - -def test_empty_response_205(): - response = EmptyResponse(status_code=205) - assert response.headers["content-length"] == "0" - - -def test_empty_response_304(): - headers = {"content-length": "43"} - response = EmptyResponse(status_code=304, headers=headers) - assert response.headers["content-length"] == "43" + response = response_cls(status_code=304) + await response.__call__(scope, receive, send) From 1d9b4b0e48fa87b9e548d420676719fe159c419d Mon Sep 17 00:00:00 2001 From: declaresub Date: Thu, 16 Dec 2021 13:40:18 -0500 Subject: [PATCH 4/7] Run check, lint scripts more than once. --- starlette/responses.py | 38 ++++++++++++-------- tests/test_responses.py | 79 +++++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/starlette/responses.py b/starlette/responses.py index a94db0e55..c9ac2f22b 100644 --- a/starlette/responses.py +++ b/starlette/responses.py @@ -133,38 +133,48 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: # Response is terminated after the status line. So no headers and no body. # https://datatracker.ietf.org/doc/html/rfc7231#section-6.2 raw_headers = [] - body = b'' + body = b"" elif self.status_code == 204: # Response must not have a content-length header. See # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 - # http spec does not appear to say whether or not there can be a content-type header. Some clients - # will attempt to parse the message body if there is a content-type header, so we ensure that there isn't one in + # http spec does not appear to say whether or not there can be + # a content-type header. Some clients + # will attempt to parse the message body if there is a content-type + # header, so we ensure that there isn't one in # the response. - raw_headers = [header for header in self.raw_headers if header[0] not in (b'content-length', b'content-type')] - body = b'' + raw_headers = [ + header + for header in self.raw_headers + if header[0] not in (b"content-length", b"content-type") + ] + body = b"" elif self.status_code == 205: # Response must not include a body. # Response can either have a content-length: 0 header or a # transfer-encoding: chunked header. - # We check for a transfer-encoding header. If not found, we ensure the presence of - # the content-length header. + # We check for a transfer-encoding header. If not found, + # we ensure the presence of the content-length header. # https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.6 - raw_headers = [header for header in self.raw_headers if header[0] not in (b'content-length', b'content-type')] + raw_headers = [ + header + for header in self.raw_headers + if header[0] not in (b"content-length", b"content-type") + ] for header in self.raw_headers: - if header[0] == b'transfer-encoding': + if header[0] == b"transfer-encoding": break else: - raw_headers.append((b'content-length', b'0')) - body = b'' + raw_headers.append((b"content-length", b"0")) + body = b"" elif self.status_code == 304: - # A 304 Not Modfied response may contain a transfer-encoding header, or content-length header - # whose value is the length of + # A 304 Not Modfied response may contain a transfer-encoding header, + # or content-length header whose value is the length of # message that would have been sent in a 200 OK response. # So we leave the headers as is. # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1 # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 raw_headers = self.raw_headers - body = b'' + body = b"" else: raw_headers = self.raw_headers body = self.body diff --git a/tests/test_responses.py b/tests/test_responses.py index eb028e13f..4a824650d 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -1,5 +1,5 @@ -import os import itertools +import os import anyio import pytest @@ -14,7 +14,6 @@ Response, StreamingResponse, ) -from starlette.types import Message def test_text_response(test_client_factory): @@ -312,40 +311,52 @@ def test_head_method(test_client_factory): response = client.head("/") assert response.text == "" + @pytest.mark.anyio -@pytest.mark.parametrize('response_cls, status_code', list(itertools.product(*[[Response, JSONResponse], [100, 101, 102]]))) +@pytest.mark.parametrize( + "response_cls, status_code", + list(itertools.product(*[[Response, JSONResponse], [100, 101, 102]])), +) async def test_response_1xx(response_cls, status_code): scope = {} + async def receive(): return {} + async def send(message: dict): - if message['type'] == "http.response.start": + if message["type"] == "http.response.start": # also ensures that self.raw_headers is not None - assert len(message['headers']) == 0 - elif message['type'] == "http.response.body": + assert len(message["headers"]) == 0 + elif message["type"] == "http.response.body": # per ASGI, if body key is missing, default is False - assert "body" not in message or message["body"] == b'' + assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: pass response = response_cls(status_code=status_code) await response.__call__(scope, receive, send) - + + @pytest.mark.anyio -@pytest.mark.parametrize('response_cls, content', itertools.product(*[[Response, JSONResponse], [None, 'test']])) +@pytest.mark.parametrize( + "response_cls, content", + itertools.product(*[[Response, JSONResponse], [None, "test"]]), +) async def test_response_204(response_cls, content): scope = {} + async def receive(): return {} + async def send(message: dict): - if message['type'] == "http.response.start": - header_map = dict(message['headers']) + if message["type"] == "http.response.start": + header_map = dict(message["headers"]) assert b"content-length" not in header_map assert b"content-type" not in header_map - elif message['type'] == "http.response.body": + elif message["type"] == "http.response.body": # per ASGI, if body key is missing, default is False - assert "body" not in message or message["body"] == b'' + assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: pass @@ -355,41 +366,46 @@ async def send(message: dict): @pytest.mark.anyio -@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +@pytest.mark.parametrize("response_cls", [Response, JSONResponse]) async def test_response_205_with_te_header(response_cls): scope = {} + async def receive(): return {} + async def send(message: dict): - if message['type'] == "http.response.start": - header_map = dict(message['headers']) - assert header_map[b'transfer-encoding'] == b'chunked' + if message["type"] == "http.response.start": + header_map = dict(message["headers"]) + assert header_map[b"transfer-encoding"] == b"chunked" assert b"content-length" not in header_map assert b"content-type" not in header_map - elif message['type'] == "http.response.body": + elif message["type"] == "http.response.body": # per ASGI, if body key is missing, default is False - assert "body" not in message or message["body"] == b'' + assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: pass - response = response_cls(status_code=205, headers={'transfer-encoding': 'chunked'}) + response = response_cls(status_code=205, headers={"transfer-encoding": "chunked"}) await response.__call__(scope, receive, send) + @pytest.mark.anyio -@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +@pytest.mark.parametrize("response_cls", [Response, JSONResponse]) async def test_response_205_with_cl_header(response_cls): scope = {} + async def receive(): return {} + async def send(message: dict): - if message['type'] == "http.response.start": - header_map = dict(message['headers']) - assert header_map[b'content-length'] == b'0' + if message["type"] == "http.response.start": + header_map = dict(message["headers"]) + assert header_map[b"content-length"] == b"0" assert b"content-type" not in header_map - elif message['type'] == "http.response.body": + elif message["type"] == "http.response.body": # per ASGI, if body key is missing, default is False - assert "body" not in message or message["body"] == b'' + assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: pass @@ -397,18 +413,21 @@ async def send(message: dict): response = response_cls(status_code=205) await response.__call__(scope, receive, send) + @pytest.mark.anyio -@pytest.mark.parametrize('response_cls', [Response, JSONResponse]) +@pytest.mark.parametrize("response_cls", [Response, JSONResponse]) async def test_response_304(response_cls): scope = {} + async def receive(): return {} + async def send(message: dict): - if message['type'] == "http.response.start": + if message["type"] == "http.response.start": pass - elif message['type'] == "http.response.body": + elif message["type"] == "http.response.body": # per ASGI, 'body', 'more_body' are optional. - assert "body" not in message or message["body"] == b'' + assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: pass From 4b7603d0ce3106370410627a82976c4e6120c100 Mon Sep 17 00:00:00 2001 From: declaresub Date: Thu, 16 Dec 2021 13:53:02 -0500 Subject: [PATCH 5/7] Tweak expressions to help mypy. --- tests/test_responses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_responses.py b/tests/test_responses.py index 4a824650d..fbb4463a0 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -315,7 +315,7 @@ def test_head_method(test_client_factory): @pytest.mark.anyio @pytest.mark.parametrize( "response_cls, status_code", - list(itertools.product(*[[Response, JSONResponse], [100, 101, 102]])), + list(itertools.product([Response, JSONResponse], [100, 101, 102])), ) async def test_response_1xx(response_cls, status_code): scope = {} @@ -341,7 +341,7 @@ async def send(message: dict): @pytest.mark.anyio @pytest.mark.parametrize( "response_cls, content", - itertools.product(*[[Response, JSONResponse], [None, "test"]]), + itertools.product([Response, JSONResponse], [None, "test"]), ) async def test_response_204(response_cls, content): scope = {} From 07d159f10174ad8db131a0c3a245716b7fe5c3db Mon Sep 17 00:00:00 2001 From: declaresub Date: Thu, 16 Dec 2021 14:12:24 -0500 Subject: [PATCH 6/7] Add # pragma: no cover where appropriate. --- tests/test_responses.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_responses.py b/tests/test_responses.py index fbb4463a0..b892a8415 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -321,7 +321,7 @@ async def test_response_1xx(response_cls, status_code): scope = {} async def receive(): - return {} + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -332,7 +332,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass + pass # pragma: no cover response = response_cls(status_code=status_code) await response.__call__(scope, receive, send) @@ -347,7 +347,7 @@ async def test_response_204(response_cls, content): scope = {} async def receive(): - return {} + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -359,7 +359,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass + pass # pragma: no cover response = response_cls(status_code=204, content=content) await response.__call__(scope, receive, send) @@ -371,7 +371,7 @@ async def test_response_205_with_te_header(response_cls): scope = {} async def receive(): - return {} + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -384,7 +384,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass + pass # pragma: no cover response = response_cls(status_code=205, headers={"transfer-encoding": "chunked"}) await response.__call__(scope, receive, send) @@ -396,7 +396,7 @@ async def test_response_205_with_cl_header(response_cls): scope = {} async def receive(): - return {} + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -408,7 +408,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass + pass # pragma: no cover response = response_cls(status_code=205) await response.__call__(scope, receive, send) @@ -420,7 +420,7 @@ async def test_response_304(response_cls): scope = {} async def receive(): - return {} + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -430,7 +430,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass + pass # pragma: no cover response = response_cls(status_code=304) await response.__call__(scope, receive, send) From 1ad0504e8ac201abd00d5a7afd42f800605eaa80 Mon Sep 17 00:00:00 2001 From: declaresub Date: Thu, 16 Dec 2021 14:13:53 -0500 Subject: [PATCH 7/7] Run scripts/lint yet again to reformat. --- tests/test_responses.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_responses.py b/tests/test_responses.py index b892a8415..44ecae827 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -321,7 +321,7 @@ async def test_response_1xx(response_cls, status_code): scope = {} async def receive(): - return {} # pragma: no cover + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -332,7 +332,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass # pragma: no cover + pass # pragma: no cover response = response_cls(status_code=status_code) await response.__call__(scope, receive, send) @@ -347,7 +347,7 @@ async def test_response_204(response_cls, content): scope = {} async def receive(): - return {} # pragma: no cover + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -359,7 +359,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass # pragma: no cover + pass # pragma: no cover response = response_cls(status_code=204, content=content) await response.__call__(scope, receive, send) @@ -371,7 +371,7 @@ async def test_response_205_with_te_header(response_cls): scope = {} async def receive(): - return {} # pragma: no cover + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -384,7 +384,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass # pragma: no cover + pass # pragma: no cover response = response_cls(status_code=205, headers={"transfer-encoding": "chunked"}) await response.__call__(scope, receive, send) @@ -396,7 +396,7 @@ async def test_response_205_with_cl_header(response_cls): scope = {} async def receive(): - return {} # pragma: no cover + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -408,7 +408,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass # pragma: no cover + pass # pragma: no cover response = response_cls(status_code=205) await response.__call__(scope, receive, send) @@ -420,7 +420,7 @@ async def test_response_304(response_cls): scope = {} async def receive(): - return {} # pragma: no cover + return {} # pragma: no cover async def send(message: dict): if message["type"] == "http.response.start": @@ -430,7 +430,7 @@ async def send(message: dict): assert "body" not in message or message["body"] == b"" assert "more_body" not in message or message["more_body"] is False else: - pass # pragma: no cover + pass # pragma: no cover response = response_cls(status_code=304) await response.__call__(scope, receive, send)