Skip to content

Commit ab67e6c

Browse files
committed
Optional converter
1 parent a844585 commit ab67e6c

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

rest_client_gen/models/attr.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import operator
21
from inspect import isclass
3-
from typing import List, Tuple
2+
from typing import Iterable, List, Tuple
43

54
from .base import GenericModelCodeGenerator, template
65
from ..dynamic_typing import DList, DOptional, ImportPathList, MetaData, ModelMeta, StringSerializable
@@ -11,13 +10,28 @@
1110
"{% if not loop.last %}, {% endif %}" \
1211
"{% endfor %}"
1312

13+
DEFAULT_ORDER = (
14+
("default", "converter", "factory"),
15+
"*",
16+
("metadata",)
17+
)
1418

15-
def sort_kwargs(kwargs: dict) -> dict:
16-
# TODO: Unify this function
17-
meta = kwargs.pop("metadata", {})
18-
sorted_dict = dict(sorted(kwargs.items(), key=operator.itemgetter(0)))
19-
if meta:
20-
sorted_dict["metadata"] = meta
19+
20+
def sort_kwargs(kwargs: dict, ordering: Iterable[Iterable[str]] = DEFAULT_ORDER) -> dict:
21+
sorted_dict_1 = {}
22+
sorted_dict_2 = {}
23+
current = sorted_dict_1
24+
for group in ordering:
25+
if isinstance(group, str):
26+
if group != "*":
27+
raise ValueError(f"Unknown kwarg group: {group}")
28+
current = sorted_dict_2
29+
else:
30+
for item in group:
31+
if item in kwargs:
32+
value = kwargs.pop(item)
33+
current[item] = value
34+
sorted_dict = {**sorted_dict_1, **kwargs, **sorted_dict_2}
2135
return sorted_dict
2236

2337

@@ -72,8 +86,12 @@ def field_data(self, name: str, meta: MetaData, optional: bool) -> Tuple[ImportP
7286
body_kwargs["factory"] = "list"
7387
else:
7488
body_kwargs["default"] = "None"
75-
if isclass(meta) and issubclass(meta, StringSerializable):
89+
if isclass(meta.type) and issubclass(meta.type, StringSerializable):
90+
body_kwargs["converter"] = f"optional({meta.type.__name__})"
91+
imports.append(("attr.converter", "optional"))
92+
elif isclass(meta) and issubclass(meta, StringSerializable):
7693
body_kwargs["converter"] = meta.__name__
94+
7795
if not self.no_meta:
7896
body_kwargs["metadata"] = {METADATA_FIELD_NAME: name}
7997
data["body"] = self.ATTRIB.render(kwargs=sort_kwargs(body_kwargs))

test/test_code_generation/test_attrs_generation.py

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

33
import pytest
44

5-
from rest_client_gen.dynamic_typing import (DList, DOptional, FloatString, ModelMeta, compile_imports)
5+
from rest_client_gen.dynamic_typing import (DList, DOptional, FloatString, IntString, ModelMeta, compile_imports)
66
from rest_client_gen.models import sort_fields
77
from rest_client_gen.models.attr import AttrsModelCodeGenerator, METADATA_FIELD_NAME
88
from rest_client_gen.models.base import generate_code
@@ -65,7 +65,7 @@ class Test:
6565
"model": ("Test", {
6666
"foo": int,
6767
"baz": DOptional(DList(DList(str))),
68-
# "bar": DOptional(IntString),
68+
"bar": DOptional(IntString),
6969
"qwerty": FloatString,
7070
"asdfg": DOptional(int)
7171
}),
@@ -80,11 +80,11 @@ class Test:
8080
"type": "Optional[List[List[str]]]",
8181
"body": f"attr.ib(factory=list, {field_meta('baz')})"
8282
},
83-
# "bar": {
84-
# "name": "bar",
85-
# "type": "Optional[IntString]",
86-
# "body": f"attr.ib(converter=IntString, default=None, {field_meta('bar')})"
87-
# },
83+
"bar": {
84+
"name": "bar",
85+
"type": "Optional[IntString]",
86+
"body": f"attr.ib(default=None, converter=optional(IntString), {field_meta('bar')})"
87+
},
8888
"qwerty": {
8989
"name": "qwerty",
9090
"type": "FloatString",
@@ -98,7 +98,8 @@ class Test:
9898
},
9999
"generated": trim(f"""
100100
import attr
101-
from rest_client_gen.dynamic_typing.string_serializable import FloatString
101+
from attr.converter import optional
102+
from rest_client_gen.dynamic_typing.string_serializable import FloatString, IntString
102103
from typing import List, Optional
103104
104105
@@ -107,6 +108,7 @@ class Test:
107108
foo: int = attr.ib({field_meta('foo')})
108109
qwerty: FloatString = attr.ib(converter=FloatString, {field_meta('qwerty')})
109110
baz: Optional[List[List[str]]] = attr.ib(factory=list, {field_meta('baz')})
111+
bar: Optional[IntString] = attr.ib(default=None, converter=optional(IntString), {field_meta('bar')})
110112
asdfg: Optional[int] = attr.ib(default=None, {field_meta('asdfg')})
111113
""")
112114
}

0 commit comments

Comments
 (0)