Skip to content

Commit b405fce

Browse files
Merge main and reimplement
2 parents f5846ce + b2adc2c commit b405fce

File tree

60 files changed

+714
-155
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+714
-155
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## 0.8.0 - Unreleased
99

10+
### Breaking Changes
11+
12+
- Generated clients will no longer pass through `None` to query parameters. Previously, any query params set to `None` would surface as empty strings (per the default behavior of `httpx`). This is contrary to the defaults indicated by the OpenAPI 3.0.3 spec. Ommitting these parameters makes us more compliant. If you require a style of `null` to be passed to your query parameters, please request support for the OpenAPI "style" attribute. Thank you to @forest-benchling and @bowenwr for a ton of input on this.
13+
1014
### Additions
1115

1216
- New `--meta` command line option for specifying what type of metadata should be generated:
@@ -22,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2226

2327
- Lowered the minimum version of `python-dateutil` to 2.8.0 for improved compatibility (#298 & #299). Thanks @bowenwr!
2428
- The `from_dict` method on generated models is now a `@classmethod` instead of `@staticmethod` (#215 & #292). Thanks @forest-benchling!
29+
- Renamed all templates to end in `.jinja`, and all python-templates to end in `.py.jinja` to fix confusion with the latest version of mypy. Note **this will break existing custom templates until you update your template file names**.
30+
31+
### Fixes
32+
33+
- Parser will softly ignore value error during schema responses' status code convertion from string to integer (not a number). Errors will be reported to the end user and parsing will continue to proceed (#327).
34+
- The generated `from_dict` and `to_dict` methods of models will now properly handle `nullable` and `not required` properties that are themselves generated models (#315). Thanks @forest-benchling!
2535

2636
## 0.7.3 - 2020-12-21
2737

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,29 +95,21 @@ def httpx_request(
9595
if not isinstance(model_prop, Unset):
9696
json_model_prop = model_prop.to_dict()
9797

98-
params: Dict[str, Any] = {}
99-
if not isinstance(string_prop, Unset) and string_prop is not None:
100-
params["string_prop"] = string_prop
101-
if not isinstance(json_datetime_prop, Unset) and json_datetime_prop is not None:
102-
params["datetime_prop"] = json_datetime_prop
103-
if not isinstance(json_date_prop, Unset) and json_date_prop is not None:
104-
params["date_prop"] = json_date_prop
105-
if not isinstance(float_prop, Unset) and float_prop is not None:
106-
params["float_prop"] = float_prop
107-
if not isinstance(int_prop, Unset) and int_prop is not None:
108-
params["int_prop"] = int_prop
109-
if not isinstance(boolean_prop, Unset) and boolean_prop is not None:
110-
params["boolean_prop"] = boolean_prop
111-
if not isinstance(json_list_prop, Unset) and json_list_prop is not None:
112-
params["list_prop"] = json_list_prop
113-
if not isinstance(json_union_prop, Unset) and json_union_prop is not None:
114-
params["union_prop"] = json_union_prop
115-
if not isinstance(json_union_prop_with_ref, Unset) and json_union_prop_with_ref is not None:
116-
params["union_prop_with_ref"] = json_union_prop_with_ref
117-
if not isinstance(json_enum_prop, Unset) and json_enum_prop is not None:
118-
params["enum_prop"] = json_enum_prop
119-
if not isinstance(json_model_prop, Unset) and json_model_prop is not None:
98+
params: Dict[str, Any] = {
99+
"string_prop": string_prop,
100+
"datetime_prop": json_datetime_prop,
101+
"date_prop": json_date_prop,
102+
"float_prop": float_prop,
103+
"int_prop": int_prop,
104+
"boolean_prop": boolean_prop,
105+
"list_prop": json_list_prop,
106+
"union_prop": json_union_prop,
107+
"union_prop_with_ref": json_union_prop_with_ref,
108+
"enum_prop": json_enum_prop,
109+
}
110+
if not isinstance(json_model_prop, Unset):
120111
params.update(json_model_prop)
112+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
121113

122114
response = client.request(
123115
"post",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def httpx_request(
6262
"an_enum_value": json_an_enum_value,
6363
"some_date": json_some_date,
6464
}
65+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
6566

6667
response = client.request(
6768
"get",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/int_enum_tests_int_enum_post.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def httpx_request(
4444
params: Dict[str, Any] = {
4545
"int_enum": json_int_enum,
4646
}
47+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
4748

4849
response = client.request(
4950
"post",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ def httpx_request(
4343
if not isinstance(query_param, Unset):
4444
json_query_param = query_param
4545

46-
params: Dict[str, Any] = {}
47-
if not isinstance(json_query_param, Unset) and json_query_param is not None:
48-
params["query_param"] = json_query_param
46+
params: Dict[str, Any] = {
47+
"query_param": json_query_param,
48+
}
49+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
4950

5051
response = client.request(
5152
"get",

end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
""" Contains all the data models used in inputs/outputs """
22

33
from .a_model import AModel
4+
from .a_model_model import AModelModel
5+
from .a_model_not_required_model import AModelNotRequiredModel
6+
from .a_model_not_required_nullable_model import AModelNotRequiredNullableModel
7+
from .a_model_nullable_model import AModelNullableModel
48
from .an_enum import AnEnum
59
from .an_int_enum import AnIntEnum
610
from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost

end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import attr
55
from dateutil.parser import isoparse
66

7+
from ..models.a_model_model import AModelModel
8+
from ..models.a_model_not_required_model import AModelNotRequiredModel
9+
from ..models.a_model_not_required_nullable_model import AModelNotRequiredNullableModel
10+
from ..models.a_model_nullable_model import AModelNullableModel
711
from ..models.an_enum import AnEnum
812
from ..models.different_enum import DifferentEnum
913
from ..types import UNSET, Unset
@@ -19,12 +23,16 @@ class AModel:
1923
a_camel_date_time: Union[datetime.datetime, datetime.date]
2024
a_date: datetime.date
2125
required_not_nullable: str
26+
model: AModelModel
2227
a_nullable_date: Optional[datetime.date]
2328
required_nullable: Optional[str]
29+
nullable_model: Optional[AModelNullableModel]
2430
nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET
2531
attr_1_leading_digit: Union[Unset, str] = UNSET
2632
not_required_nullable: Union[Unset, Optional[str]] = UNSET
2733
not_required_not_nullable: Union[Unset, str] = UNSET
34+
not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET
35+
not_required_nullable_model: Union[Optional[AModelNotRequiredNullableModel], Unset] = UNSET
2836

2937
def to_dict(self) -> Dict[str, Any]:
3038
an_enum_value = self.an_enum_value.value
@@ -37,6 +45,8 @@ def to_dict(self) -> Dict[str, Any]:
3745

3846
a_date = self.a_date.isoformat()
3947
required_not_nullable = self.required_not_nullable
48+
model = self.model.to_dict()
49+
4050
nested_list_of_enums: Union[Unset, List[Any]] = UNSET
4151
if not isinstance(self.nested_list_of_enums, Unset):
4252
nested_list_of_enums = []
@@ -54,6 +64,17 @@ def to_dict(self) -> Dict[str, Any]:
5464
required_nullable = self.required_nullable
5565
not_required_nullable = self.not_required_nullable
5666
not_required_not_nullable = self.not_required_not_nullable
67+
nullable_model = self.nullable_model.to_dict() if self.nullable_model else None
68+
69+
not_required_model: Union[Unset, Dict[str, Any]] = UNSET
70+
if not isinstance(self.not_required_model, Unset):
71+
not_required_model = self.not_required_model.to_dict()
72+
73+
not_required_nullable_model: Union[None, Unset, Dict[str, Any]] = UNSET
74+
if not isinstance(self.not_required_nullable_model, Unset):
75+
not_required_nullable_model = (
76+
self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None
77+
)
5778

5879
field_dict: Dict[str, Any] = {}
5980
field_dict.update(
@@ -62,8 +83,10 @@ def to_dict(self) -> Dict[str, Any]:
6283
"aCamelDateTime": a_camel_date_time,
6384
"a_date": a_date,
6485
"required_not_nullable": required_not_nullable,
86+
"model": model,
6587
"a_nullable_date": a_nullable_date,
6688
"required_nullable": required_nullable,
89+
"nullable_model": nullable_model,
6790
}
6891
)
6992
if nested_list_of_enums is not UNSET:
@@ -74,6 +97,10 @@ def to_dict(self) -> Dict[str, Any]:
7497
field_dict["not_required_nullable"] = not_required_nullable
7598
if not_required_not_nullable is not UNSET:
7699
field_dict["not_required_not_nullable"] = not_required_not_nullable
100+
if not_required_model is not UNSET:
101+
field_dict["not_required_model"] = not_required_model
102+
if not_required_nullable_model is not UNSET:
103+
field_dict["not_required_nullable_model"] = not_required_nullable_model
77104

78105
return field_dict
79106

@@ -101,6 +128,8 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
101128

102129
required_not_nullable = d.pop("required_not_nullable")
103130

131+
model = AModelModel.from_dict(d.pop("model"))
132+
104133
nested_list_of_enums = []
105134
_nested_list_of_enums = d.pop("nested_list_of_enums", UNSET)
106135
for nested_list_of_enums_item_data in _nested_list_of_enums or []:
@@ -126,17 +155,38 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
126155

127156
not_required_not_nullable = d.pop("not_required_not_nullable", UNSET)
128157

158+
nullable_model = None
159+
_nullable_model = d.pop("nullable_model")
160+
if _nullable_model is not None:
161+
nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model))
162+
163+
not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET
164+
_not_required_model = d.pop("not_required_model", UNSET)
165+
if not isinstance(_not_required_model, Unset):
166+
not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model))
167+
168+
not_required_nullable_model = None
169+
_not_required_nullable_model = d.pop("not_required_nullable_model", UNSET)
170+
if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset):
171+
not_required_nullable_model = AModelNotRequiredNullableModel.from_dict(
172+
cast(Dict[str, Any], _not_required_nullable_model)
173+
)
174+
129175
a_model = cls(
130176
an_enum_value=an_enum_value,
131177
a_camel_date_time=a_camel_date_time,
132178
a_date=a_date,
133179
required_not_nullable=required_not_nullable,
180+
model=model,
134181
nested_list_of_enums=nested_list_of_enums,
135182
a_nullable_date=a_nullable_date,
136183
attr_1_leading_digit=attr_1_leading_digit,
137184
required_nullable=required_nullable,
138185
not_required_nullable=not_required_nullable,
139186
not_required_not_nullable=not_required_not_nullable,
187+
nullable_model=nullable_model,
188+
not_required_model=not_required_model,
189+
not_required_nullable_model=not_required_nullable_model,
140190
)
141191

142192
return a_model
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
T = TypeVar("T", bound="AModelModel")
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class AModelModel:
10+
""" """
11+
12+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
13+
14+
def to_dict(self) -> Dict[str, Any]:
15+
16+
field_dict: Dict[str, Any] = {}
17+
field_dict.update(self.additional_properties)
18+
field_dict.update({})
19+
20+
return field_dict
21+
22+
@classmethod
23+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
24+
d = src_dict.copy()
25+
a_model_model = cls()
26+
27+
a_model_model.additional_properties = d
28+
return a_model_model
29+
30+
@property
31+
def additional_keys(self) -> List[str]:
32+
return list(self.additional_properties.keys())
33+
34+
def __getitem__(self, key: str) -> Any:
35+
return self.additional_properties[key]
36+
37+
def __setitem__(self, key: str, value: Any) -> None:
38+
self.additional_properties[key] = value
39+
40+
def __delitem__(self, key: str) -> None:
41+
del self.additional_properties[key]
42+
43+
def __contains__(self, key: str) -> bool:
44+
return key in self.additional_properties
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
T = TypeVar("T", bound="AModelNotRequiredModel")
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class AModelNotRequiredModel:
10+
""" """
11+
12+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
13+
14+
def to_dict(self) -> Dict[str, Any]:
15+
16+
field_dict: Dict[str, Any] = {}
17+
field_dict.update(self.additional_properties)
18+
field_dict.update({})
19+
20+
return field_dict
21+
22+
@classmethod
23+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
24+
d = src_dict.copy()
25+
a_model_not_required_model = cls()
26+
27+
a_model_not_required_model.additional_properties = d
28+
return a_model_not_required_model
29+
30+
@property
31+
def additional_keys(self) -> List[str]:
32+
return list(self.additional_properties.keys())
33+
34+
def __getitem__(self, key: str) -> Any:
35+
return self.additional_properties[key]
36+
37+
def __setitem__(self, key: str, value: Any) -> None:
38+
self.additional_properties[key] = value
39+
40+
def __delitem__(self, key: str) -> None:
41+
del self.additional_properties[key]
42+
43+
def __contains__(self, key: str) -> bool:
44+
return key in self.additional_properties
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
T = TypeVar("T", bound="AModelNotRequiredNullableModel")
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class AModelNotRequiredNullableModel:
10+
""" """
11+
12+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
13+
14+
def to_dict(self) -> Dict[str, Any]:
15+
16+
field_dict: Dict[str, Any] = {}
17+
field_dict.update(self.additional_properties)
18+
field_dict.update({})
19+
20+
return field_dict
21+
22+
@classmethod
23+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
24+
d = src_dict.copy()
25+
a_model_not_required_nullable_model = cls()
26+
27+
a_model_not_required_nullable_model.additional_properties = d
28+
return a_model_not_required_nullable_model
29+
30+
@property
31+
def additional_keys(self) -> List[str]:
32+
return list(self.additional_properties.keys())
33+
34+
def __getitem__(self, key: str) -> Any:
35+
return self.additional_properties[key]
36+
37+
def __setitem__(self, key: str, value: Any) -> None:
38+
self.additional_properties[key] = value
39+
40+
def __delitem__(self, key: str) -> None:
41+
del self.additional_properties[key]
42+
43+
def __contains__(self, key: str) -> bool:
44+
return key in self.additional_properties

0 commit comments

Comments
 (0)