Skip to content

Commit a46865d

Browse files
committed
Attrs converters
1 parent b02197c commit a46865d

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

json_to_models/models/attr.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ def __init__(self, model: ModelMeta, meta=False, attrs_kwargs: dict = None, **kw
2828

2929
@property
3030
def decorators(self) -> Tuple[ImportPathList, List[str]]:
31-
return [('attr', None)], [self.ATTRS.render(kwargs=self.attrs_kwargs)]
31+
imports, decorators = super().decorators
32+
imports.append(('attr', None))
33+
decorators.insert(0, self.ATTRS.render(kwargs=self.attrs_kwargs))
34+
return imports, decorators
3235

3336
def field_data(self, name: str, meta: MetaData, optional: bool) -> Tuple[ImportPathList, dict]:
3437
"""
@@ -49,13 +52,23 @@ def field_data(self, name: str, meta: MetaData, optional: bool) -> Tuple[ImportP
4952
body_kwargs["factory"] = "dict"
5053
else:
5154
body_kwargs["default"] = "None"
52-
if isclass(meta.type) and issubclass(meta.type, StringSerializable):
55+
if isclass(meta.type) and issubclass(meta.type, StringSerializable) and not self.post_init_converters:
5356
body_kwargs["converter"] = f"optional({meta.type.__name__})"
5457
imports.append(("attr.converter", "optional"))
55-
elif isclass(meta) and issubclass(meta, StringSerializable):
58+
elif isclass(meta) and issubclass(meta, StringSerializable) and not self.post_init_converters:
5659
body_kwargs["converter"] = meta.__name__
5760

5861
if not self.no_meta and name != data["name"]:
5962
body_kwargs["metadata"] = {METADATA_FIELD_NAME: name}
6063
data["body"] = self.ATTRIB.render(kwargs=sort_kwargs(body_kwargs, DEFAULT_ORDER))
6164
return imports, data
65+
66+
@property
67+
def convert_strings_kwargs(self) -> Tuple[ImportPathList, dict]:
68+
"""
69+
:return: Imports and Dict with kw-arguments for `json_to_models.models.string_converters.convert_strings` decorator.
70+
"""
71+
imports, kwargs = super().convert_strings_kwargs
72+
imports.append(('json_to_models.models', ['ClassType']))
73+
kwargs["class_type"] = 'ClassType.Attrs'
74+
return imports, kwargs

test/test_code_generation/test_attrs_generation.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import pytest
44

5-
from json_to_models.dynamic_typing import (DDict, DList, DOptional, FloatString, IntString, ModelMeta, compile_imports)
5+
from json_to_models.dynamic_typing import (DDict, DList, DOptional, DUnion, FloatString, IntString, ModelMeta,
6+
compile_imports)
67
from json_to_models.models.attr import AttrsModelCodeGenerator, DEFAULT_ORDER
78
from json_to_models.models.base import METADATA_FIELD_NAME, generate_code, sort_kwargs
89
from json_to_models.models.structure import sort_fields
@@ -142,23 +143,53 @@ class Test:
142143
},
143144
"generated": trim(f"""
144145
import attr
145-
from attr.converter import optional
146146
from json_to_models.dynamic_typing import FloatString, IntString
147+
from json_to_models.models import ClassType
148+
from json_to_models.models.string_converters import convert_strings
147149
from typing import Dict, List, Optional
148150
149151
150152
@attr.s
153+
@convert_strings(['bar#O.S', 'qwerty'], class_type=ClassType.Attrs)
151154
class Test:
152155
foo: int = attr.ib()
153-
qwerty: FloatString = attr.ib(converter=FloatString)
156+
qwerty: FloatString = attr.ib()
154157
dict: Dict[str, int] = attr.ib()
155158
not_: bool = attr.ib({field_meta('not')})
156159
one_day: int = attr.ib({field_meta('1day')})
157160
den_nedeli: str = attr.ib({field_meta('день_недели')})
158161
baz: Optional[List[List[str]]] = attr.ib(factory=list)
159-
bar: Optional[IntString] = attr.ib(default=None, converter=optional(IntString))
162+
bar: Optional[IntString] = attr.ib(default=None)
160163
asdfg: Optional[int] = attr.ib(default=None)
161164
""")
165+
},
166+
"converters": {
167+
"model": ("Test", {
168+
"a": int,
169+
"b": IntString,
170+
"c": DOptional(FloatString),
171+
"d": DList(DList(DList(IntString))),
172+
"e": DDict(IntString),
173+
"u": DUnion(DDict(IntString), DList(DList(IntString))),
174+
}),
175+
"generated": trim("""
176+
import attr
177+
from json_to_models.dynamic_typing import FloatString, IntString
178+
from json_to_models.models import ClassType
179+
from json_to_models.models.string_converters import convert_strings
180+
from typing import Dict, List, Optional, Union
181+
182+
183+
@attr.s
184+
@convert_strings(['b', 'c#O.S', 'd#L.L.L.S', 'e#D.S'], class_type=ClassType.Attrs)
185+
class Test:
186+
a: int = attr.ib()
187+
b: IntString = attr.ib()
188+
d: List[List[List[IntString]]] = attr.ib()
189+
e: Dict[str, IntString] = attr.ib()
190+
u: Union[Dict[str, IntString], List[List[IntString]]] = attr.ib()
191+
c: Optional[FloatString] = attr.ib(default=None)
192+
""")
162193
}
163194
}
164195

@@ -200,5 +231,5 @@ def test_fields_attr(value: ModelMeta, expected: dict):
200231
@pytest.mark.parametrize("value,expected", test_data_unzip["generated"])
201232
def test_generated_attr(value: ModelMeta, expected: str):
202233
generated = generate_code(([{"model": value, "nested": []}], {}), AttrsModelCodeGenerator,
203-
class_generator_kwargs={'meta': True})
234+
class_generator_kwargs={'meta': True, 'post_init_converters': True})
204235
assert generated.rstrip() == expected, generated

test/test_code_generation/test_string_converters.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,18 @@ class A:
7575

7676
assert a == A(1, [1, 2, 3, 4], {'s': 2, 'w': 3}, None,
7777
[{'a': [1, 2]}, {'b': [3, 2]}])
78+
79+
@attr.s
80+
@convert_strings(['x', 'y#L.S', 'z#D.S', 'a#O.S', 'b#O.L.D.L.S'], class_type=ClassType.Attrs)
81+
class A:
82+
x: IntString = attr.ib()
83+
y: List[IntString] = attr.ib()
84+
z: Dict[str, IntString] = attr.ib()
85+
a: Optional[IntString] = attr.ib(default=None)
86+
b: Optional[List[Dict[str, List[IntString]]]] = attr.ib(default=None)
87+
88+
a = A('1', '1234', {'s': '2', 'w': '3'}, None,
89+
[{'a': ['1', '2']}, {'b': ['3', '2']}])
90+
91+
assert a == A(1, [1, 2, 3, 4], {'s': 2, 'w': 3}, None,
92+
[{'a': [1, 2]}, {'b': [3, 2]}])

0 commit comments

Comments
 (0)