Skip to content

Commit 3c24fd2

Browse files
committed
added alias support
1 parent 3fba795 commit 3c24fd2

2 files changed

Lines changed: 33 additions & 7 deletions

File tree

python/exacting/core.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Callable, List, Type, TypeVar
1+
from typing import Any, Callable, Dict, List, Type, TypeVar
22
from typing_extensions import Self, dataclass_transform
33

44
import dataclasses
@@ -84,6 +84,26 @@ def init(self, **kwargs):
8484
return init
8585

8686

87+
def get_nonalias(cls: Type["Exact"], data: dict) -> dict:
88+
for field in cls.__dataclass_fields__.values():
89+
alias = field.metadata.get("exacting_alias")
90+
if alias is not None:
91+
item = data.pop(alias)
92+
data[field.name] = item
93+
94+
return data
95+
96+
97+
def transform_alias(cls: Type["Exact"], data: dict) -> dict:
98+
for field in cls.__dataclass_fields__.values():
99+
alias = field.metadata.get("exacting_alias")
100+
if alias is not None:
101+
item = data.pop(field.name)
102+
data[alias] = item
103+
104+
return data
105+
106+
87107
@dataclass_transform(kw_only_default=True)
88108
class _ModelKwOnly: ...
89109

@@ -97,14 +117,18 @@ def __init_subclass__(cls) -> None:
97117
init = get_exact_init(dataclass(cls))
98118
setattr(cls, "__init__", init)
99119

100-
def exact_as_dict(self):
120+
def exact_as_dict(self) -> Dict[str, Any]:
101121
"""(exacting) Creates a dictionary representation of this dataclass instance.
102122
123+
Returns:
124+
dict[str, Any]
125+
103126
Raises:
104127
AssertionError: Expected a dataclass
105128
"""
106129
assert is_dataclass(self)
107-
return asdict(self)
130+
data = asdict(self)
131+
return transform_alias(self.__class__, data)
108132

109133
def exact_as_bytes(self) -> bytes:
110134
"""(exacting) Convert this instance of dataclass model into bytes."""
@@ -153,9 +177,9 @@ class Person(Exact):
153177
else:
154178
data = jsonc_to_py(raw)
155179

156-
return cls(**data)
180+
return cls(**get_nonalias(cls, data))
157181

158182
@classmethod
159183
def exact_from_bytes(cls, raw: bytes) -> Self:
160184
data = bytes_to_py(raw)
161-
return cls(**data)
185+
return cls(**get_nonalias(cls, data))

python/exacting/fields.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from dataclasses import field as dataclass_field
33

44
from .exacting import Regex
5-
from .etypes import BaseType, TypeResult, expect
5+
from .etypes import BaseType, TypeResult
66

77

88
class RegexType(BaseType[str]):
@@ -32,6 +32,7 @@ def field(
3232
default_factory: Optional[Callable[[], T]] = None,
3333
hash: Optional[bool] = None,
3434
regex: Optional[str] = None,
35+
alias: Optional[str] = None,
3536
) -> Any:
3637
"""Creates a field.
3738
@@ -41,12 +42,13 @@ def field(
4142
Cannot be set at the same time with `default`.
4243
hash (bool, optional): Hashable?
4344
regex (str, optional): Check regex for `str`, if the current field is typed to as a `str`.
45+
alias (str, optional): Alias for serializing/deserializing, but not used in Python.
4446
"""
4547
validators = []
4648
if regex is not None:
4749
validators.append(RegexType(regex))
4850

49-
metadata = {"exacting_validators": validators}
51+
metadata = {"exacting_validators": validators, "exacting_alias": alias}
5052

5153
if default is not None and default_factory is None:
5254
return dataclass_field(default=default, hash=hash, metadata=metadata)

0 commit comments

Comments
 (0)