diff --git a/pynamodb/attributes.py b/pynamodb/attributes.py index 122ba0def..3f1706a25 100644 --- a/pynamodb/attributes.py +++ b/pynamodb/attributes.py @@ -2,7 +2,6 @@ PynamoDB attributes """ import six -import json from base64 import b64encode, b64decode from delorean import Delorean, parse from pynamodb.constants import ( @@ -10,6 +9,14 @@ DEFAULT_ENCODING ) +try: + import ujson as json + import json as python_json + HAS_UJSON = True +except ImportError: + import json + HAS_UJSON = False + class Attribute(object): """ @@ -183,7 +190,13 @@ def deserialize(self, value): """ Deserializes JSON """ - return json.loads(value, strict=False) + if HAS_UJSON: + try: + return json.loads(value) + except ValueError: + return python_json.loads(value, strict=False) + else: + return json.loads(value, strict=False) class BooleanAttribute(Attribute): @@ -230,13 +243,26 @@ def serialize(self, value): """ Encode numbers as JSON """ - return json.dumps(value) + if HAS_UJSON and isinstance(value, float): + from decimal import Decimal + value = Decimal(value) + try: + return json.dumps(value) + except OverflowError: + if HAS_UJSON: + return python_json.dumps(value) + raise def deserialize(self, value): """ Decode numbers from JSON """ - return json.loads(value) + try: + return json.loads(value) + except ValueError: + if HAS_UJSON: + return python_json.loads(value) + raise class UTCDateTimeAttribute(Attribute): diff --git a/pynamodb/connection/base.py b/pynamodb/connection/base.py index a0ec1bece..dd2f47b9e 100644 --- a/pynamodb/connection/base.py +++ b/pynamodb/connection/base.py @@ -33,6 +33,12 @@ ITEMS, DEFAULT_ENCODING, BINARY_SHORT, BINARY_SET_SHORT, LAST_EVALUATED_KEY, RESPONSES, UNPROCESSED_KEYS, UNPROCESSED_ITEMS, STREAM_SPECIFICATION, STREAM_VIEW_TYPE, STREAM_ENABLED) +try: + import ujson as json +except ImportError: + import json + + BOTOCORE_EXCEPTIONS = (BotoCoreError, ClientError) @@ -241,8 +247,8 @@ def _make_api_call(self, operation_name, operation_kwargs): ) prepared_request = self.client._endpoint.create_request(request_dict, operation_model) response = self.requests_session.send(prepared_request) + data = json.loads(response.text) if response.status_code >= 300: - data = response.json() botocore_expected_format = {"Error": {"Message": data.get("message", ""), "Code": data.get("__type", "")}} raise ClientError(botocore_expected_format, operation_name) data = response.json() diff --git a/pynamodb/models.py b/pynamodb/models.py index 80c31859f..d98085d56 100644 --- a/pynamodb/models.py +++ b/pynamodb/models.py @@ -1,7 +1,6 @@ """ DynamoDB Models for PynamoDB """ -import json import time import six import copy @@ -32,6 +31,11 @@ COUNT, ITEM_COUNT, KEY, UNPROCESSED_ITEMS, STREAM_VIEW_TYPE, STREAM_SPECIFICATION, STREAM_ENABLED) +try: + import ujson as json +except ImportError: + import json + log = logging.getLogger(__name__) log.addHandler(NullHandler()) diff --git a/pynamodb/tests/test_attributes.py b/pynamodb/tests/test_attributes.py index 202101710..72b47f8b9 100644 --- a/pynamodb/tests/test_attributes.py +++ b/pynamodb/tests/test_attributes.py @@ -2,7 +2,6 @@ pynamodb attributes tests """ import six -import json from base64 import b64encode from datetime import datetime from delorean import Delorean @@ -15,6 +14,11 @@ JSONAttribute, DEFAULT_ENCODING, NUMBER, STRING, STRING_SET, NUMBER_SET, BINARY_SET, BINARY) +try: + import ujson as json +except ImportError: + import json + class AttributeTestModel(Model):