Merge pull request 'Serializer: use alias, make it configurable for different use cases' (#1811) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -49,7 +49,8 @@ def sanitize_string(s: str) -> str:
|
||||
return json.dumps(s)[1:-1]
|
||||
|
||||
|
||||
def dataclass_to_dict(obj: Any) -> Any:
|
||||
def dataclass_to_dict(obj: Any, *, use_alias: bool = True) -> Any:
|
||||
def _to_dict(obj: Any) -> Any:
|
||||
"""
|
||||
Utility function to convert dataclasses to dictionaries
|
||||
It converts all nested dataclasses, lists, tuples, and dictionaries to dictionaries
|
||||
@@ -60,15 +61,15 @@ def dataclass_to_dict(obj: Any) -> Any:
|
||||
return {
|
||||
# Use either the original name or name
|
||||
sanitize_string(
|
||||
field.metadata.get("original_name", field.name)
|
||||
): dataclass_to_dict(getattr(obj, field.name))
|
||||
field.metadata.get("alias", field.name) if use_alias else field.name
|
||||
): _to_dict(getattr(obj, field.name))
|
||||
for field in fields(obj)
|
||||
if not field.name.startswith("_") # type: ignore
|
||||
}
|
||||
elif isinstance(obj, list | tuple):
|
||||
return [dataclass_to_dict(item) for item in obj]
|
||||
return [_to_dict(item) for item in obj]
|
||||
elif isinstance(obj, dict):
|
||||
return {sanitize_string(k): dataclass_to_dict(v) for k, v in obj.items()}
|
||||
return {sanitize_string(k): _to_dict(v) for k, v in obj.items()}
|
||||
elif isinstance(obj, Path):
|
||||
return sanitize_string(str(obj))
|
||||
elif isinstance(obj, str):
|
||||
@@ -76,6 +77,8 @@ def dataclass_to_dict(obj: Any) -> Any:
|
||||
else:
|
||||
return obj
|
||||
|
||||
return _to_dict(obj)
|
||||
|
||||
|
||||
T = TypeVar("T", bound=dataclass) # type: ignore
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
# Functions to test
|
||||
from clan_cli.api import (
|
||||
dataclass_to_dict,
|
||||
from_dict,
|
||||
)
|
||||
from clan_cli.api import dataclass_to_dict, from_dict
|
||||
from clan_cli.errors import ClanError
|
||||
from clan_cli.inventory import (
|
||||
Inventory,
|
||||
@@ -87,6 +84,7 @@ def test_simple_field_missing() -> None:
|
||||
|
||||
|
||||
def test_deserialize_extensive_inventory() -> None:
|
||||
# TODO: Make this an abstract test, so it doesn't break the test if the inventory changes
|
||||
data = {
|
||||
"meta": {"name": "superclan", "description": "nice clan"},
|
||||
"services": {
|
||||
@@ -130,7 +128,16 @@ def test_alias_field() -> None:
|
||||
data = {"--user-name--": "John"}
|
||||
expected = Person(name="John")
|
||||
|
||||
assert from_dict(Person, data) == expected
|
||||
person = from_dict(Person, data)
|
||||
|
||||
# Deserialize
|
||||
assert person == expected
|
||||
|
||||
# Serialize with alias
|
||||
assert dataclass_to_dict(person) == data
|
||||
|
||||
# Serialize without alias
|
||||
assert dataclass_to_dict(person, use_alias=False) == {"name": "John"}
|
||||
|
||||
|
||||
def test_path_field() -> None:
|
||||
|
||||
Reference in New Issue
Block a user