From 401342ab9c72349cc3b2148110ab33f715bf1e1f Mon Sep 17 00:00:00 2001 From: Fu Hanxi Date: Fri, 5 Jan 2024 11:02:40 +0100 Subject: [PATCH] feat: add `json_to_app` method with support custom classes --- idf_build_apps/__init__.py | 5 +++-- idf_build_apps/app.py | 5 ++--- idf_build_apps/main.py | 42 +++++++++++++++++++++++++++++++++++++- tests/test_app.py | 39 +++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/idf_build_apps/__init__.py b/idf_build_apps/__init__.py index 2a31ffe..cc5ae7c 100644 --- a/idf_build_apps/__init__.py +++ b/idf_build_apps/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 """ @@ -28,6 +28,7 @@ from .main import ( build_apps, find_apps, + json_to_app, ) __all__ = [ @@ -37,6 +38,6 @@ 'MakeApp', 'find_apps', 'build_apps', + 'json_to_app', 'setup_logging', - 'SESSION_ARGS', ] diff --git a/idf_build_apps/app.py b/idf_build_apps/app.py index 68a6d87..8866ae5 100644 --- a/idf_build_apps/app.py +++ b/idf_build_apps/app.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import functools @@ -25,10 +25,9 @@ computed_field, ) -from idf_build_apps import ( +from . import ( SESSION_ARGS, ) - from .build_apps_args import ( BuildAppsArgs, ) diff --git a/idf_build_apps/main.py b/idf_build_apps/main.py index b9544b4..c5c738b 100644 --- a/idf_build_apps/main.py +++ b/idf_build_apps/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import argparse @@ -11,11 +11,17 @@ import textwrap import typing as t +from pydantic import ( + Field, + create_model, +) + from . import ( SESSION_ARGS, ) from .app import ( App, + AppDeserializer, CMakeApp, MakeApp, ) @@ -791,3 +797,37 @@ def main(): print(f' {app}') sys.exit(res) + + +def json_to_app(json_str: str, extra_classes: t.Optional[t.List[t.Type[App]]] = None) -> App: + """ + Deserialize json string to App object + + .. note:: + + You can pass extra_cls to support custom App class. A custom App class must be a subclass of App, and have a + different value of `build_system`. For example, a custom CMake app + + >>> class CustomApp(CMakeApp): + >>> build_system: Literal['custom_cmake'] = 'custom_cmake' + + Then you can pass the CustomApp class to the `extra_cls` argument + + >>> json_str = CustomApp('.', 'esp32').to_json() + >>> json_to_app(json_str, extra_classes=[CustomApp]) + + :param json_str: json string + :param extra_classes: extra App class + :return: App object + """ + types = [App, CMakeApp, MakeApp] + if extra_classes: + types.extend(extra_classes) + + custom_deserializer = create_model( + '_CustomDeserializer', + app=(t.Union[tuple(types)], Field(discriminator='build_system')), + __base__=AppDeserializer, + ) + + return custom_deserializer.from_json(json_str) diff --git a/tests/test_app.py b/tests/test_app.py index 77e81d3..22f2c7d 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,13 +1,30 @@ # SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 +import sys + import pytest +from pydantic import ( + ValidationError, +) from idf_build_apps import ( AppDeserializer, CMakeApp, MakeApp, ) +from idf_build_apps.main import ( + json_to_app, +) + +if sys.version_info < (3, 8): + from typing_extensions import ( + Literal, + ) +else: + from typing import ( + Literal, + ) def test_serialization(): @@ -54,3 +71,25 @@ def test_app_sorting(): assert d == e assert not d < e assert not d > e + + +def test_app_deserializer(): + a = CMakeApp('foo', 'esp32') + b = MakeApp('foo', 'esp32') + + class CustomApp(CMakeApp): + build_system: Literal['custom'] = 'custom' # type: ignore + + c = CustomApp('foo', 'esp32') + + assert json_to_app(a.to_json()) == a + assert json_to_app(b.to_json()) == b + + with pytest.raises( + ValidationError, + match="Input tag 'custom' found using 'build_system' does not match " + "any of the expected tags: 'unknown', 'cmake', 'make'", + ): + assert json_to_app(c.to_json()) == c + + assert json_to_app(c.to_json(), extra_classes=[CustomApp]) == c