1
0
mirror of https://github.com/kevin1024/vcrpy.git synced 2025-12-08 16:53:23 +00:00

Merge branch 'master' of github.com:kevin1024/vcrpy into fix-resource-warning-2

This commit is contained in:
Thomas Grainger
2024-01-23 11:03:49 +00:00
14 changed files with 283 additions and 207 deletions

View File

@@ -1,3 +1,4 @@
import asyncio
import functools
import inspect
import logging
@@ -6,7 +7,9 @@ from unittest.mock import MagicMock, patch
import httpx
from vcr.errors import CannotOverwriteExistingCassetteException
from vcr.filters import decode_response
from vcr.request import Request as VcrRequest
from vcr.serializers.compat import convert_body_to_bytes
_httpx_signature = inspect.signature(httpx.Client.request)
@@ -33,19 +36,29 @@ def _transform_headers(httpx_response):
return out
def _to_serialized_response(httpx_response):
try:
content = httpx_response.content.decode("utf-8")
except UnicodeDecodeError:
content = httpx_response.content
async def _to_serialized_response(resp, aread):
# The content shouldn't already have been read in by HTTPX.
assert not hasattr(resp, "_decoder")
return {
"status_code": httpx_response.status_code,
"http_version": httpx_response.http_version,
"headers": _transform_headers(httpx_response),
"content": content,
# Retrieve the content, but without decoding it.
with patch.dict(resp.headers, {"Content-Encoding": ""}):
if aread:
await resp.aread()
else:
resp.read()
result = {
"status": {"code": resp.status_code, "message": resp.reason_phrase},
"headers": _transform_headers(resp),
"body": {"string": resp.content},
}
# As the content wasn't decoded, we restore the response to a state which
# will be capable of decoding the content for the consumer.
del resp._decoder
resp._content = resp._get_content_decoder().decode(resp.content)
return result
def _from_serialized_headers(headers):
"""
@@ -62,17 +75,32 @@ def _from_serialized_headers(headers):
@patch("httpx.Response.close", MagicMock())
@patch("httpx.Response.read", MagicMock())
def _from_serialized_response(request, serialized_response, history=None):
content = serialized_response.get("content")
if isinstance(content, str):
content = content.encode("utf-8")
# Cassette format generated for HTTPX requests by older versions of
# vcrpy. We restructure the content to resemble what a regular
# cassette looks like.
if "status_code" in serialized_response:
serialized_response = decode_response(
convert_body_to_bytes(
{
"headers": serialized_response["headers"],
"body": {"string": serialized_response["content"]},
"status": {"code": serialized_response["status_code"]},
},
),
)
extensions = None
else:
extensions = {"reason_phrase": serialized_response["status"]["message"].encode()}
response = httpx.Response(
status_code=serialized_response.get("status_code"),
status_code=serialized_response["status"]["code"],
request=request,
headers=_from_serialized_headers(serialized_response.get("headers")),
content=content,
headers=_from_serialized_headers(serialized_response["headers"]),
content=serialized_response["body"]["string"],
history=history or [],
extensions=extensions,
)
response._content = content
return response
@@ -98,17 +126,17 @@ def _shared_vcr_send(cassette, real_send, *args, **kwargs):
return vcr_request, None
def _record_responses(cassette, vcr_request, real_response):
async def _record_responses(cassette, vcr_request, real_response, aread):
for past_real_response in real_response.history:
past_vcr_request = _make_vcr_request(past_real_response.request)
cassette.append(past_vcr_request, _to_serialized_response(past_real_response))
cassette.append(past_vcr_request, await _to_serialized_response(past_real_response, aread))
if real_response.history:
# If there was a redirection keep we want the request which will hold the
# final redirect value
vcr_request = _make_vcr_request(real_response.request)
cassette.append(vcr_request, _to_serialized_response(real_response))
cassette.append(vcr_request, await _to_serialized_response(real_response, aread))
return real_response
@@ -126,8 +154,8 @@ async def _async_vcr_send(cassette, real_send, *args, **kwargs):
return response
real_response = await real_send(*args, **kwargs)
await real_response.aread()
return _record_responses(cassette, vcr_request, real_response)
await _record_responses(cassette, vcr_request, real_response, aread=True)
return real_response
def async_vcr_send(cassette, real_send):
@@ -146,8 +174,8 @@ def _sync_vcr_send(cassette, real_send, *args, **kwargs):
return response
real_response = real_send(*args, **kwargs)
real_response.read()
return _record_responses(cassette, vcr_request, real_response)
asyncio.run(_record_responses(cassette, vcr_request, real_response, aread=False))
return real_response
def sync_vcr_send(cassette, real_send):