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:
parent
9e88f2e2fb
commit
56c8edaf66
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user