From d0ab30b17aca0c1780cd62cd66eaa2d175534bf1 Mon Sep 17 00:00:00 2001 From: Yakov Pechersky Date: Sat, 22 Mar 2025 23:26:36 -0400 Subject: [PATCH] Remove simplejson dependency When simplejson is installed, `requests` behaves differently https://github.com/psf/requests/blob/1764cc938efc3cc9720188dfa6c3852c45211aa0/src/requests/compat.py#L59-L64 which affects custom JSONEncode/Decoders Instead of relying on simplejson for decimal support implement decimal support directly Remove the `internal_json` vs `stock_json` test comparison --- THIRD-PARTY-LICENSES | 24 ----------------------- awslambdaric/lambda_runtime_marshaller.py | 22 ++++++++++++++------- requirements/base.txt | 1 - tests/test_lambda_runtime_marshaller.py | 17 ---------------- 4 files changed, 15 insertions(+), 49 deletions(-) diff --git a/THIRD-PARTY-LICENSES b/THIRD-PARTY-LICENSES index 41cde9b..69ab255 100644 --- a/THIRD-PARTY-LICENSES +++ b/THIRD-PARTY-LICENSES @@ -204,30 +204,6 @@ limitations under the License. ------ -** python-simplejson; version 3.17.2 -- -https://github.com/simplejson/simplejson -Copyright (c) 2006 Bob Ippolito - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ------- - ** libcurl; version 7.83.1 -- https://github.com/curl/curl Copyright (c) 1996 - 2022, Daniel Stenberg, daniel@haxx.se, and many contributors, see the THANKS file. diff --git a/awslambdaric/lambda_runtime_marshaller.py b/awslambdaric/lambda_runtime_marshaller.py index 4256066..decb96d 100644 --- a/awslambdaric/lambda_runtime_marshaller.py +++ b/awslambdaric/lambda_runtime_marshaller.py @@ -5,29 +5,37 @@ import decimal import math import os -import simplejson as json +import json from .lambda_runtime_exception import FaultException -# simplejson's Decimal encoding allows '-NaN' as an output, which is a parse error for json.loads -# to get the good parts of Decimal support, we'll special-case NaN decimals and otherwise duplicate the encoding for decimals the same way simplejson does -# We also set 'ensure_ascii=False' so that the encoded json contains unicode characters instead of unicode escape sequences +# We force a serialization of a Decimal's string as raw instead of wrapped by quotes +# by mocking a float, and overriding the __repr__ method to return the string +class DecimalStr(float): + def __init__(self, value: decimal.Decimal): + self._value = value + def __repr__(self): + return str(self._value) + class Encoder(json.JSONEncoder): def __init__(self): if os.environ.get("AWS_EXECUTION_ENV") in { "AWS_Lambda_python3.12", "AWS_Lambda_python3.13", }: - super().__init__(use_decimal=False, ensure_ascii=False, allow_nan=True) + # We also set 'ensure_ascii=False' so that the encoded json contains unicode characters instead of unicode escape sequences + super().__init__(ensure_ascii=False, allow_nan=True) else: - super().__init__(use_decimal=False, allow_nan=True) + super().__init__(allow_nan=True) + # simplejson's Decimal encoding allows '-NaN' as an output, which is a parse error for json.loads + # to get the good parts of Decimal support, we'll special-case NaN decimals and otherwise duplicate the encoding for decimals the same way simplejson does def default(self, obj): if isinstance(obj, decimal.Decimal): if obj.is_nan(): return math.nan - return json.raw_json.RawJSON(str(obj)) + return DecimalStr(obj) return super().default(obj) diff --git a/requirements/base.txt b/requirements/base.txt index 4bb251e..9ba9713 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,2 +1 @@ -simplejson>=3.20.1 snapshot-restore-py>=1.0.0 diff --git a/tests/test_lambda_runtime_marshaller.py b/tests/test_lambda_runtime_marshaller.py index 843bcee..e54a17c 100644 --- a/tests/test_lambda_runtime_marshaller.py +++ b/tests/test_lambda_runtime_marshaller.py @@ -48,23 +48,6 @@ def test_to_json_decimal_encoding_negative_nan(self): response = to_json({"pi": decimal.Decimal("-nan")}) self.assertEqual('{"pi": NaN}', response) - def test_json_serializer_is_not_default_json(self): - from awslambdaric.lambda_runtime_marshaller import ( - json as internal_json, - ) - import simplejson as simplejson - import json as stock_json - import json - - self.assertEqual(json, stock_json) - self.assertNotEqual(stock_json, internal_json) - self.assertNotEqual(stock_json, simplejson) - - internal_json.YOLO = "bello" - self.assertTrue(hasattr(internal_json, "YOLO")) - self.assertFalse(hasattr(stock_json, "YOLO")) - self.assertTrue(hasattr(simplejson, "YOLO")) - @parameterized.expand(execution_envs_lambda_marshaller_ensure_ascii_false) def test_to_json_unicode_not_escaped_encoding(self, execution_env): os.environ = {"AWS_EXECUTION_ENV": execution_env}