Skip to content

Commit

Permalink
feat: add json_to_app method with support custom classes
Browse files Browse the repository at this point in the history
  • Loading branch information
hfudev committed Jan 5, 2024
1 parent c7b982b commit 1b384be
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 1 deletion.
51 changes: 50 additions & 1 deletion idf_build_apps/main.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -11,6 +11,11 @@
import textwrap
import typing as t

from pydantic import (
Field,
create_model,
)

from . import (
SESSION_ARGS,
)
Expand Down Expand Up @@ -45,6 +50,7 @@
Manifest,
)
from .utils import (
BaseModel,
InvalidCommand,
files_matches_patterns,
get_parallel_start_stop,
Expand Down Expand Up @@ -791,3 +797,46 @@ def main():
print(f' {app}')

sys.exit(res)


class AppDeserializer(BaseModel):
app: t.Union[App, CMakeApp, MakeApp] = Field(discriminator='build_system')

@classmethod
def from_json(cls, json_data: t.Union[str, bytes, bytearray]) -> App:
json_dict = json.loads(json_data.strip())
return cls.model_validate({'app': json_dict}).app


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)
39 changes: 39 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
@@ -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():
Expand Down Expand Up @@ -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

0 comments on commit 1b384be

Please sign in to comment.