Make 'request' non-optional on responses (#666)

* Make 'request' non-optional on Response

* Lint

* Remove remaining mention to null request
This commit is contained in:
Florimond Manca 2019-12-21 15:38:25 +01:00 committed by GitHub
parent 9e88f2e2fb
commit 56c8edaf66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 44 deletions

View File

@ -661,11 +661,11 @@ class Response:
self,
status_code: int,
*,
request: Request,
http_version: str = None,
headers: HeaderTypes = None,
stream: ContentStream = None,
content: bytes = None,
request: Request = None,
history: typing.List["Response"] = None,
elapsed: datetime.timedelta = None,
):
@ -696,10 +696,8 @@ class Response:
def url(self) -> typing.Optional[URL]:
"""
Returns the URL for which the request was made.
Requires that `request` was provided when instantiating the response.
"""
return None if self.request is None else self.request.url
return self.request.url
@property
def content(self) -> bytes:
@ -831,7 +829,6 @@ class Response:
@property
def cookies(self) -> "Cookies":
if not hasattr(self, "_cookies"):
assert self.request is not None
self._cookies = Cookies()
self._cookies.extract_cookies(self)
return self._cookies
@ -967,7 +964,6 @@ class Cookies(MutableMapping):
"""
Loads any cookies based on the response `Set-Cookie` headers.
"""
assert response.request is not None
urlib_response = self._CookieCompatResponse(response)
urllib_request = self._CookieCompatRequest(response.request)

View File

@ -19,7 +19,7 @@ async def app(request):
{"method": method, "path": path, "body": body.decode()}
).encode()
headers = {"Content-Length": str(len(content))}
return Response(200, headers=headers, content=content)
return Response(200, headers=headers, content=content, request=request)
@pytest.mark.asyncio

View File

@ -7,6 +7,8 @@ import pytest
import httpx
from httpx.content_streams import AsyncIteratorStream
REQUEST = httpx.Request("GET", "https://example.org")
def streaming_body():
yield b"Hello, "
@ -19,16 +21,17 @@ async def async_streaming_body():
def test_response():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
assert response.status_code == 200
assert response.reason_phrase == "OK"
assert response.text == "Hello, world!"
assert response.request is REQUEST
assert response.elapsed == datetime.timedelta(0)
assert not response.is_error
def test_response_repr():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
assert repr(response) == "<Response [200 OK]>"
@ -38,7 +41,7 @@ def test_response_content_type_encoding():
"""
headers = {"Content-Type": "text-plain; charset=latin-1"}
content = "Latin 1: ÿ".encode("latin-1")
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.text == "Latin 1: ÿ"
assert response.encoding == "latin-1"
@ -48,7 +51,7 @@ def test_response_autodetect_encoding():
Autodetect encoding if there is no charset info in a Content-Type header.
"""
content = "おはようございます。".encode("EUC-JP")
response = httpx.Response(200, content=content)
response = httpx.Response(200, content=content, request=REQUEST)
assert response.text == "おはようございます。"
assert response.encoding == "EUC-JP"
@ -59,7 +62,7 @@ def test_response_fallback_to_autodetect():
"""
headers = {"Content-Type": "text-plain; charset=invalid-codec-name"}
content = "おはようございます。".encode("EUC-JP")
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.text == "おはようございます。"
assert response.encoding == "EUC-JP"
@ -71,7 +74,7 @@ def test_response_default_text_encoding():
"""
content = b"Hello, world!"
headers = {"Content-Type": "text/plain"}
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.status_code == 200
assert response.encoding == "iso-8859-1"
assert response.text == "Hello, world!"
@ -81,7 +84,7 @@ def test_response_default_encoding():
"""
Default to utf-8 if all else fails.
"""
response = httpx.Response(200, content=b"")
response = httpx.Response(200, content=b"", request=REQUEST)
assert response.text == ""
assert response.encoding == "utf-8"
@ -91,7 +94,7 @@ def test_response_non_text_encoding():
Default to apparent encoding for non-text content-type headers.
"""
headers = {"Content-Type": "image/png"}
response = httpx.Response(200, content=b"xyz", headers=headers)
response = httpx.Response(200, content=b"xyz", headers=headers, request=REQUEST)
assert response.text == "xyz"
assert response.encoding == "ascii"
@ -101,7 +104,7 @@ def test_response_set_explicit_encoding():
"Content-Type": "text-plain; charset=utf-8"
} # Deliberately incorrect charset
response = httpx.Response(
200, content="Latin 1: ÿ".encode("latin-1"), headers=headers
200, content="Latin 1: ÿ".encode("latin-1"), headers=headers, request=REQUEST,
)
response.encoding = "latin-1"
assert response.text == "Latin 1: ÿ"
@ -109,7 +112,9 @@ def test_response_set_explicit_encoding():
def test_response_force_encoding():
response = httpx.Response(200, content="Snowman: ☃".encode("utf-8"))
response = httpx.Response(
200, content="Snowman: ☃".encode("utf-8"), request=REQUEST
)
response.encoding = "iso-8859-1"
assert response.status_code == 200
assert response.reason_phrase == "OK"
@ -119,7 +124,7 @@ def test_response_force_encoding():
@pytest.mark.asyncio
async def test_read_response():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
assert response.status_code == 200
assert response.text == "Hello, world!"
@ -135,7 +140,7 @@ async def test_read_response():
@pytest.mark.asyncio
async def test_raw_interface():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
raw = b""
async for part in response.aiter_raw():
@ -145,7 +150,7 @@ async def test_raw_interface():
@pytest.mark.asyncio
async def test_bytes_interface():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
content = b""
async for part in response.aiter_bytes():
@ -155,7 +160,7 @@ async def test_bytes_interface():
@pytest.mark.asyncio
async def test_text_interface():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
await response.read()
@ -167,7 +172,7 @@ async def test_text_interface():
@pytest.mark.asyncio
async def test_lines_interface():
response = httpx.Response(200, content=b"Hello,\nworld!")
response = httpx.Response(200, content=b"Hello,\nworld!", request=REQUEST)
await response.read()
@ -179,7 +184,7 @@ async def test_lines_interface():
@pytest.mark.asyncio
async def test_stream_interface_after_read():
response = httpx.Response(200, content=b"Hello, world!")
response = httpx.Response(200, content=b"Hello, world!", request=REQUEST)
await response.read()
@ -192,7 +197,7 @@ async def test_stream_interface_after_read():
@pytest.mark.asyncio
async def test_streaming_response():
stream = AsyncIteratorStream(aiterator=async_streaming_body())
response = httpx.Response(200, stream=stream)
response = httpx.Response(200, stream=stream, request=REQUEST)
assert response.status_code == 200
assert not response.is_closed
@ -207,7 +212,7 @@ async def test_streaming_response():
@pytest.mark.asyncio
async def test_cannot_read_after_stream_consumed():
stream = AsyncIteratorStream(aiterator=async_streaming_body())
response = httpx.Response(200, stream=stream)
response = httpx.Response(200, stream=stream, request=REQUEST)
content = b""
async for part in response.aiter_bytes():
@ -220,7 +225,7 @@ async def test_cannot_read_after_stream_consumed():
@pytest.mark.asyncio
async def test_cannot_read_after_response_closed():
stream = AsyncIteratorStream(aiterator=async_streaming_body())
response = httpx.Response(200, stream=stream)
response = httpx.Response(200, stream=stream, request=REQUEST)
await response.close()
@ -229,7 +234,7 @@ async def test_cannot_read_after_response_closed():
def test_unknown_status_code():
response = httpx.Response(600)
response = httpx.Response(600, request=REQUEST)
assert response.status_code == 600
assert response.reason_phrase == ""
assert response.text == ""
@ -239,7 +244,7 @@ def test_json_with_specified_encoding():
data = {"greeting": "hello", "recipient": "world"}
content = json.dumps(data).encode("utf-16")
headers = {"Content-Type": "application/json, charset=utf-16"}
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.json() == data
@ -247,7 +252,7 @@ def test_json_with_options():
data = {"greeting": "hello", "recipient": "world", "amount": 1}
content = json.dumps(data).encode("utf-16")
headers = {"Content-Type": "application/json, charset=utf-16"}
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.json(parse_int=str)["amount"] == "1"
@ -255,7 +260,7 @@ def test_json_without_specified_encoding():
data = {"greeting": "hello", "recipient": "world"}
content = json.dumps(data).encode("utf-32-be")
headers = {"Content-Type": "application/json"}
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(200, content=content, headers=headers, request=REQUEST)
assert response.json() == data
@ -265,7 +270,9 @@ def test_json_without_specified_encoding_decode_error():
headers = {"Content-Type": "application/json"}
# force incorrect guess from `guess_json_utf` to trigger error
with mock.patch("httpx.models.guess_json_utf", return_value="utf-32"):
response = httpx.Response(200, content=content, headers=headers)
response = httpx.Response(
200, content=content, headers=headers, request=REQUEST
)
with pytest.raises(json.JSONDecodeError):
response.json()
@ -287,5 +294,5 @@ def test_json_without_specified_encoding_decode_error():
],
)
def test_link_headers(headers, expected):
response = httpx.Response(200, content=None, headers=headers)
response = httpx.Response(200, content=None, headers=headers, request=REQUEST)
assert response.links == expected

View File

@ -14,6 +14,8 @@ from httpx.decoders import (
TextDecoder,
)
REQUEST = httpx.Request("GET", "https://example.org")
def test_deflate():
body = b"test 123"
@ -21,7 +23,9 @@ def test_deflate():
compressed_body = compressor.compress(body) + compressor.flush()
headers = [(b"Content-Encoding", b"deflate")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
@ -31,7 +35,9 @@ def test_gzip():
compressed_body = compressor.compress(body) + compressor.flush()
headers = [(b"Content-Encoding", b"gzip")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
@ -40,7 +46,9 @@ def test_brotli():
compressed_body = brotli.compress(body)
headers = [(b"Content-Encoding", b"br")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
@ -56,7 +64,9 @@ def test_multi():
)
headers = [(b"Content-Encoding", b"deflate, gzip")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
@ -65,11 +75,15 @@ def test_multi_with_identity():
compressed_body = brotli.compress(body)
headers = [(b"Content-Encoding", b"br, identity")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
headers = [(b"Content-Encoding", b"identity, br")]
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
assert response.content == body
@ -84,7 +98,7 @@ async def test_streaming():
headers = [(b"Content-Encoding", b"gzip")]
stream = AsyncIteratorStream(aiterator=compress(body))
response = httpx.Response(200, headers=headers, stream=stream)
response = httpx.Response(200, headers=headers, stream=stream, request=REQUEST)
assert not hasattr(response, "body")
assert await response.read() == body
@ -92,7 +106,7 @@ async def test_streaming():
@pytest.mark.parametrize("header_value", (b"deflate", b"gzip", b"br", b"identity"))
def test_empty_content(header_value):
headers = [(b"Content-Encoding", header_value)]
response = httpx.Response(200, headers=headers, content=b"")
response = httpx.Response(200, headers=headers, content=b"", request=REQUEST)
assert response.content == b""
@ -111,7 +125,9 @@ def test_decoding_errors(header_value):
body = b"test 123"
compressed_body = brotli.compress(body)[3:]
with pytest.raises(httpx.DecodingError):
response = httpx.Response(200, headers=headers, content=compressed_body)
response = httpx.Response(
200, headers=headers, content=compressed_body, request=REQUEST
)
response.content
@ -140,7 +156,7 @@ async def test_text_decoder(data, encoding):
yield chunk
stream = AsyncIteratorStream(aiterator=iterator())
response = httpx.Response(200, stream=stream)
response = httpx.Response(200, stream=stream, request=REQUEST)
await response.read()
assert response.text == (b"".join(data)).decode(encoding)
@ -157,6 +173,7 @@ async def test_text_decoder_known_encoding():
200,
headers=[(b"Content-Type", b"text/html; charset=shift-jis")],
stream=stream,
request=REQUEST,
)
await response.read()
@ -218,5 +235,5 @@ def test_invalid_content_encoding_header():
headers = [(b"Content-Encoding", b"invalid-header")]
body = b"test 123"
response = httpx.Response(200, headers=headers, content=body)
response = httpx.Response(200, headers=headers, content=body, request=REQUEST)
assert response.content == body

View File

@ -22,7 +22,7 @@ class MockDispatch(Dispatcher):
timeout: TimeoutTypes = None,
) -> httpx.Response:
content = b"".join([part async for part in request.stream])
return httpx.Response(200, content=content)
return httpx.Response(200, content=content, request=request)
@pytest.mark.parametrize(("value,output"), (("abc", b"abc"), (b"abc", b"abc")))