From c048ea20254f549b2a68e66a22bd548946570824 Mon Sep 17 00:00:00 2001 From: monkut Date: Thu, 15 Sep 2022 11:12:04 +0900 Subject: [PATCH] add feature (#704) Check if args/kwargs are JSON Serializable while running locally (#1154) * :sparkles: add feature (#705) - JSON unserializable content passed to asyncronous task during local develop will raise exception on purpose to allow developers to catch JSON unserializable errors during development. :white_check_mark: add `test_async_call_arg_not_json_seralizable` testcase :fire: remove unnecessary coding comment in `test_async.py` * :art: fix flake8 * :pencil: fix typo :wrench: add specific `UnserializableJsonError` exception for clarity * :white_check_mark: expand testcase to include custome exception, `UnserializableJsonError`. * :art: run isort/black * :fire: remove unnecessary assert Co-authored-by: javulticat <31746850+javulticat@users.noreply.github.com> * :recycle: simplify `validate_json_serializable()` to accept *args, **kwargs as per review comment * :fire: remove unnecessary import and use `object()` as unserializable_object sample. Co-authored-by: javulticat <31746850+javulticat@users.noreply.github.com> --- tests/tests_async.py | 9 ++++++++- zappa/asynchronous.py | 3 ++- zappa/utilities.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/tests_async.py b/tests/tests_async.py index 7b4f58d19..987667bd2 100644 --- a/tests/tests_async.py +++ b/tests/tests_async.py @@ -1,4 +1,3 @@ -# -*- coding: utf8 -*- import os import unittest @@ -17,6 +16,7 @@ get_func_task_path, import_and_get_task, ) +from zappa.utilities import UnserializableJsonError class TestZappa(unittest.TestCase): @@ -94,3 +94,10 @@ def test_async_call_with_defaults(self): lambda_function_name="MyLambda", ) lambda_async_mock.return_value.send.assert_called_with(get_func_task_path(async_me), ("qux",), {}) + + def test_async_call_arg_not_json_serializable(self): + """Exception is raised when calling an async function locally (not on aws)""" + async_me = import_and_get_task("tests.test_app.async_me") + unserializable_object = object() + with self.assertRaises(UnserializableJsonError): + async_me(unserializable_object) diff --git a/zappa/asynchronous.py b/zappa/asynchronous.py index 7c231a750..51dc72318 100644 --- a/zappa/asynchronous.py +++ b/zappa/asynchronous.py @@ -96,7 +96,7 @@ def my_async_func(*args, **kwargs): import boto3 import botocore -from .utilities import get_topic_name +from .utilities import get_topic_name, validate_json_serializable try: from zappa_settings import ASYNC_RESPONSE_TABLE @@ -430,6 +430,7 @@ def _run_async(*args, **kwargs): ).send(task_path, args, kwargs) return send_result else: + validate_json_serializable(*args, **kwargs) return func(*args, **kwargs) update_wrapper(_run_async, func) diff --git a/zappa/utilities.py b/zappa/utilities.py index 72ad9f0f7..15da07c1c 100644 --- a/zappa/utilities.py +++ b/zappa/utilities.py @@ -9,6 +9,7 @@ import shutil import stat import sys +from typing import Any from urllib.parse import urlparse import botocore @@ -16,6 +17,13 @@ LOG = logging.getLogger(__name__) + +class UnserializableJsonError(TypeError): + """Exception class for JSON encoding errors""" + + pass + + ## # Settings / Packaging ## @@ -592,3 +600,10 @@ def merge_headers(event): for h in multi_headers.keys(): multi_headers[h] = ", ".join(multi_headers[h]) return multi_headers + + +def validate_json_serializable(*args: Any, **kwargs: Any) -> None: + try: + json.dumps((args, kwargs)) + except (TypeError, OverflowError): + raise UnserializableJsonError("Arguments to an asynchronous.task must be JSON serializable!")