diff --git a/.travis.yml b/.travis.yml index 2a59d29..4ff2f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,18 @@ language: python matrix: include: - - python: 2.6 - dist: trusty - sudo: false - - python: 2.7 - dist: trusty - sudo: false - - python: 3.3 - dist: trusty - sudo: false - - python: 3.4 - dist: trusty - sudo: false - - python: 3.5 - dist: trusty - sudo: false - python: 3.6 dist: trusty sudo: false - python: 3.7 dist: xenial sudo: true + - python: 3.8 + dist: xenial + sudo: true + - python: 3.9 + dist: xenial + sudo: true cache: pip before_install: diff --git a/placebo/serializer.py b/placebo/serializer.py index d6c4651..67d9555 100644 --- a/placebo/serializer.py +++ b/placebo/serializer.py @@ -15,8 +15,10 @@ import json import pickle import datetime +import base64 +from io import BytesIO + from botocore.response import StreamingBody -from six import StringIO class UTC(datetime.tzinfo): @@ -78,7 +80,9 @@ def deserialize(obj): if class_name == 'datetime': return datetime.datetime(tzinfo=utc, **target) if class_name == 'StreamingBody': - return StringIO(target['body']) + b64_body = obj['body'] + decoded_body = base64.b64decode(b64_body) + return BytesIO(decoded_body) # Return unrecognized structures as-is return obj @@ -103,9 +107,12 @@ def serialize(obj): return result if isinstance(obj, StreamingBody): result['body'] = obj.read() - obj._raw_stream = StringIO(result['body']) + obj._raw_stream = BytesIO(result['body']) obj._amount_read = 0 return result + if isinstance(obj, bytes): + encoded = base64.b64encode(obj) + return encoded.decode('utf-8') # Raise a TypeError if the object isn't recognized raise TypeError("Type not serializable") diff --git a/setup.py b/setup.py index ffd0b3d..cf6d130 100755 --- a/setup.py +++ b/setup.py @@ -20,14 +20,10 @@ 'Intended Audience :: System Administrators', 'Natural Language :: English', 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7' + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9' ], ) diff --git a/tests/unit/test_serializers.py b/tests/unit/test_serializers.py index b279901..3b219d8 100644 --- a/tests/unit/test_serializers.py +++ b/tests/unit/test_serializers.py @@ -16,7 +16,9 @@ import unittest import json -from six import StringIO, BytesIO +from io import StringIO, BytesIO + +from botocore.response import StreamingBody from placebo.serializer import serialize, deserialize, utc, Format from placebo.serializer import get_serializer, get_deserializer @@ -33,6 +35,15 @@ date_json = """{"LoginProfile": {"CreateDate": {"__class__": "datetime", "day": 4, "hour": 9, "microsecond": 0, "minute": 1, "month": 1, "second": 2, "year": 2015}, "UserName": "baz"}}""" +content = b'this is a test' +stream = BytesIO(content) + +streaming_body_sample = { + 'Body': StreamingBody(stream, len(content)) +} + +streaming_body_json = """{"Body": {"__class__": "StreamingBody", "__module__": "botocore.response", "body": "dGhpcyBpcyBhIHRlc3Q="}}""" + class TestSerializers(unittest.TestCase): @@ -44,6 +55,15 @@ def test_datetime_from_json(self): response = json.loads(date_json, object_hook=deserialize) self.assertEqual(response, date_sample) + def test_streaming_body_to_json(self): + result = json.dumps( + streaming_body_sample, default=serialize, sort_keys=True) + self.assertEqual(result, streaming_body_json) + + def test_streaming_body_from_json(self): + response = json.loads(streaming_body_json, object_hook=deserialize) + self.assertEqual(response['Body'].read(), content) + def test_roundtrip_json(self): ser = get_serializer(Format.JSON) deser = get_deserializer(Format.JSON)