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

Fix for Serialization errors with JSON adapter

This patch aims to fix the issue#222,
where json data in request can not be serialized
because of TypeError in py3
This commit is contained in:
Aliaksandr Buhayeu
2015-12-02 16:33:58 +03:00
committed by Kevin McCarthy
parent 528c9e7b1a
commit 9daf301deb
4 changed files with 96 additions and 19 deletions

View File

@@ -2,7 +2,8 @@
import pytest import pytest
from vcr.compat import mock from vcr.compat import mock
from vcr.serialize import deserialize from vcr.request import Request
from vcr.serialize import deserialize, serialize
from vcr.serializers import yamlserializer, jsonserializer from vcr.serializers import yamlserializer, jsonserializer
@@ -83,3 +84,50 @@ def test_deserialize_py2py3_yaml_cassette(tmpdir, req_body, expect):
def test_serialize_constructs_UnicodeDecodeError(mock_dumps): def test_serialize_constructs_UnicodeDecodeError(mock_dumps):
with pytest.raises(UnicodeDecodeError): with pytest.raises(UnicodeDecodeError):
jsonserializer.serialize({}) jsonserializer.serialize({})
def test_serialize_empty_request():
request = Request(
method='POST',
uri='http://localhost/',
body='',
headers={},
)
serialize(
{'requests': [request], 'responses': [{}]},
jsonserializer
)
def test_serialize_json_request():
request = Request(
method='POST',
uri='http://localhost/',
body="{'hello': 'world'}",
headers={},
)
serialize(
{'requests': [request], 'responses': [{}]},
jsonserializer
)
def test_serialize_binary_request():
msg = "Does this HTTP interaction contain binary data?"
request = Request(
method='POST',
uri='http://localhost/',
body=b'\x8c',
headers={},
)
try:
serialize(
{'requests': [request], 'responses': [{}]},
jsonserializer
)
except (UnicodeDecodeError, TypeError) as exc:
assert msg in str(exc)

View File

@@ -50,7 +50,7 @@ def deserialize(cassette_string, serializer):
def serialize(cassette_dict, serializer): def serialize(cassette_dict, serializer):
interactions = ([{ interactions = ([{
'request': request._to_dict(), 'request': compat.convert_to_unicode(request._to_dict()),
'response': compat.convert_to_unicode(response), 'response': compat.convert_to_unicode(response),
} for request, response in zip( } for request, response in zip(
cassette_dict['requests'], cassette_dict['requests'],

View File

@@ -17,7 +17,7 @@ def convert_body_to_bytes(resp):
By default yaml serializes to utf-8 encoded bytestrings. By default yaml serializes to utf-8 encoded bytestrings.
When this cassette is loaded by python3, it's automatically decoded When this cassette is loaded by python3, it's automatically decoded
into unicode strings. This makes sure that it stays a bytestring, since into unicode strings. This makes sure that it stays a bytestring, since
that's what all the internal httplib machinery is expecting. that's what all the internal httplib machinery is expecting.
For more info on py3 yaml: For more info on py3 yaml:
@@ -37,19 +37,43 @@ def convert_body_to_bytes(resp):
return resp return resp
def _convert_string_to_unicode(string):
"""
If the string is bytes, decode it to a string (for python3 support)
"""
result = string
try:
if string is not None and not isinstance(string, six.text_type):
result = string.decode('utf-8')
except (TypeError, UnicodeDecodeError, AttributeError):
# Sometimes the string actually is binary or StringIO object,
# so if you can't decode it, just give up.
pass
return result
def convert_body_to_unicode(resp): def convert_body_to_unicode(resp):
""" """
If the request body is bytes, decode it to a string (for python3 support) If the request or responses body is bytes, decode it to a string
(for python3 support)
""" """
try: if type(resp) is not dict:
if not isinstance(resp['body']['string'], six.text_type): # Some of the tests just serialize and deserialize a string.
resp['body']['string'] = resp['body']['string'].decode('utf-8') return _convert_string_to_unicode(resp)
except (KeyError, TypeError, UnicodeDecodeError): else:
# The thing we were converting either wasn't a dictionary or didn't body = resp.get('body')
# have the keys we were expecting. Some of the tests just serialize
# and deserialize a string. if body is not None:
try:
body['string'] = _convert_string_to_unicode(
body['string']
)
except (KeyError, TypeError, AttributeError):
# The thing we were converting either wasn't a dictionary or
# didn't have the keys we were expecting.
# For example request object has no 'string' key.
resp['body'] = _convert_string_to_unicode(body)
# Also, sometimes the thing actually is binary, so if you can't decode
# it, just give up.
pass
return resp return resp

View File

@@ -9,16 +9,21 @@ def deserialize(cassette_string):
def serialize(cassette_dict): def serialize(cassette_dict):
error_message = (
"Does this HTTP interaction contain binary data? "
"If so, use a different serializer (like the yaml serializer) "
"for this request?"
)
try: try:
return json.dumps(cassette_dict, indent=4) return json.dumps(cassette_dict, indent=4)
except UnicodeDecodeError as original: except UnicodeDecodeError as original: # py2
raise UnicodeDecodeError( raise UnicodeDecodeError(
original.encoding, original.encoding,
b"Error serializing cassette to JSON", b"Error serializing cassette to JSON",
original.start, original.start,
original.end, original.end,
original.args[-1] + original.args[-1] + error_message
("Does this HTTP interaction contain binary data? "
"If so, use a different serializer (like the yaml serializer) "
"for this request?")
) )
except TypeError as original: # py3
raise TypeError(error_message)