Skip to content

Commit 847696e

Browse files
committed
Based off of #267 with suggestions applied.
1 parent 2d2dbdc commit 847696e

File tree

11 files changed

+145
-23
lines changed

11 files changed

+145
-23
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Fixes
1010
- Spacing and extra returns for Union types of `additionalProperties` (#266 & #268). Thanks @joshzana & @packyg!
1111
- Title of inline schemas will no longer be missing characters (#271 & #274). Thanks @kalzoo!
12+
- Handling of nulls (Nones) when parsing or constructing dates (#267). Thanks @fyhertz!
1213

1314
## 0.7.2 - 2020-12-08
1415
### Fixes

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

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ def httpx_request(
5555

5656
if isinstance(some_date, datetime.date):
5757
json_some_date = some_date.isoformat()
58-
5958
else:
6059
json_some_date = some_date.isoformat()
6160

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import datetime
2-
from typing import Any, Dict, List, Optional, Union
2+
from typing import Any, Dict, List, Optional, Union, cast
33

44
import attr
55
from dateutil.parser import isoparse
@@ -34,7 +34,6 @@ def to_dict(self) -> Dict[str, Any]:
3434
a_camel_date_time = self.a_camel_date_time.isoformat()
3535

3636
a_date = self.a_date.isoformat()
37-
3837
required_not_nullable = self.required_not_nullable
3938
nested_list_of_enums: Union[Unset, List[Any]] = UNSET
4039
if not isinstance(self.nested_list_of_enums, Unset):
@@ -49,7 +48,6 @@ def to_dict(self) -> Dict[str, Any]:
4948
nested_list_of_enums.append(nested_list_of_enums_item)
5049

5150
a_nullable_date = self.a_nullable_date.isoformat() if self.a_nullable_date else None
52-
5351
attr_1_leading_digit = self.attr_1_leading_digit
5452
required_nullable = self.required_nullable
5553
not_required_nullable = self.not_required_nullable
@@ -113,8 +111,10 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
113111

114112
nested_list_of_enums.append(nested_list_of_enums_item)
115113

116-
a_nullable_date = d.pop("a_nullable_date")
117-
a_nullable_date = isoparse(a_nullable_date).date() if a_nullable_date else None
114+
a_nullable_date = None
115+
_a_nullable_date = d.pop("a_nullable_date")
116+
if _a_nullable_date is not None:
117+
a_nullable_date = isoparse(cast(str, _a_nullable_date)).date()
118118

119119
attr_1_leading_digit = d.pop("1_leading_digit", UNSET)
120120

end_to_end_tests/golden-record/my_test_api_client/api/tests/get_user_list.py

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ def _get_kwargs(
2828

2929
if isinstance(some_date, datetime.date):
3030
json_some_date = some_date.isoformat()
31-
3231
else:
3332
json_some_date = some_date.isoformat()
3433

end_to_end_tests/golden-record/my_test_api_client/models/a_model.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import datetime
2-
from typing import Any, Dict, List, Optional, Union
2+
from typing import Any, Dict, List, Optional, Union, cast
33

44
import attr
55
from dateutil.parser import isoparse
@@ -34,7 +34,6 @@ def to_dict(self) -> Dict[str, Any]:
3434
a_camel_date_time = self.a_camel_date_time.isoformat()
3535

3636
a_date = self.a_date.isoformat()
37-
3837
required_not_nullable = self.required_not_nullable
3938
nested_list_of_enums: Union[Unset, List[Any]] = UNSET
4039
if not isinstance(self.nested_list_of_enums, Unset):
@@ -49,7 +48,6 @@ def to_dict(self) -> Dict[str, Any]:
4948
nested_list_of_enums.append(nested_list_of_enums_item)
5049

5150
a_nullable_date = self.a_nullable_date.isoformat() if self.a_nullable_date else None
52-
5351
attr_1_leading_digit = self.attr_1_leading_digit
5452
required_nullable = self.required_nullable
5553
not_required_nullable = self.not_required_nullable
@@ -113,8 +111,10 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
113111

114112
nested_list_of_enums.append(nested_list_of_enums_item)
115113

116-
a_nullable_date = d.pop("a_nullable_date")
117-
a_nullable_date = isoparse(a_nullable_date).date() if a_nullable_date else None
114+
a_nullable_date = None
115+
_a_nullable_date = d.pop("a_nullable_date")
116+
if _a_nullable_date is not None:
117+
a_nullable_date = isoparse(cast(str, _a_nullable_date)).date()
118118

119119
attr_1_leading_digit = d.pop("1_leading_digit", UNSET)
120120

openapi_python_client/templates/property_templates/date_property.pyi

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
{% macro construct(property, source, initial_value="None") %}
2-
{% if property.required %}
3-
{% if property.nullable %}
4-
{{ property.python_name }} = {{ source }}
5-
{{ property.python_name }} = isoparse({{ property.python_name }}).date() if {{ property.python_name }} else None
6-
{% else %}
2+
{% if property.required and not property.nullable %}
73
{{ property.python_name }} = isoparse({{ source }}).date()
8-
{% endif %}
94
{% else %}
105
{{ property.python_name }} = {{ initial_value }}
116
_{{ property.python_name }} = {{ source }}
@@ -16,11 +11,7 @@ if _{{ property.python_name }} is not None:
1611

1712
{% macro transform(property, source, destination, declare_type=True) %}
1813
{% if property.required %}
19-
{% if property.nullable %}
20-
{{ destination }} = {{ source }}.isoformat() if {{ source }} else None
21-
{% else %}
22-
{{ destination }} = {{ source }}.isoformat()
23-
{% endif %}
14+
{{ destination }} = {{ source }}.isoformat() {% if property.nullable %}if {{ source }} else None {%endif%}
2415
{% else %}
2516
{{ destination }}{% if declare_type %}: Union[Unset, str]{% endif %} = UNSET
2617
if not isinstance({{ source }}, Unset):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from datetime import date
2+
from typing import cast, Union
3+
4+
from dateutil.parser import isoparse
5+
{% from "property_templates/date_property.pyi" import transform, construct %}
6+
some_source = date(2020, 10, 12)
7+
{{ transform(property, "some_source", "some_destination") }}
8+
{{ construct(property, "some_destination") }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from datetime import date
2+
from typing import cast, Union
3+
4+
from dateutil.parser import isoparse
5+
6+
some_source = date(2020, 10, 12)
7+
8+
9+
some_destination: Union[Unset, str] = UNSET
10+
if not isinstance(some_source, Unset):
11+
12+
some_destination = some_source.isoformat() if some_source else None
13+
14+
15+
16+
17+
18+
a_prop = None
19+
_a_prop = some_destination
20+
if _a_prop is not None:
21+
a_prop = isoparse(cast(str, _a_prop)).date()
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from datetime import date
2+
from typing import cast, Union
3+
4+
from dateutil.parser import isoparse
5+
6+
some_source = date(2020, 10, 12)
7+
8+
9+
some_destination = some_source.isoformat()
10+
11+
12+
13+
14+
a_prop = isoparse(some_destination).date()
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from datetime import date
2+
from typing import cast, Union
3+
4+
from dateutil.parser import isoparse
5+
6+
some_source = date(2020, 10, 12)
7+
8+
9+
some_destination = some_source.isoformat() if some_source else None
10+
11+
12+
13+
14+
a_prop = None
15+
_a_prop = some_destination
16+
if _a_prop is not None:
17+
a_prop = isoparse(cast(str, _a_prop)).date()
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from pathlib import Path
2+
3+
import jinja2
4+
5+
6+
def test_required_not_nullable():
7+
from openapi_python_client.parser.properties import DateProperty
8+
9+
prop = DateProperty(
10+
name="a_prop",
11+
required=True,
12+
nullable=False,
13+
default=None,
14+
)
15+
here = Path(__file__).parent
16+
templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates"
17+
18+
env = jinja2.Environment(
19+
loader=jinja2.ChoiceLoader([jinja2.FileSystemLoader(here), jinja2.FileSystemLoader(templates_dir)])
20+
)
21+
22+
template = env.get_template("date_property_template.py")
23+
content = template.render(property=prop)
24+
expected = here / "required_not_null.py"
25+
assert content == expected.read_text()
26+
27+
28+
def test_required_nullable():
29+
from openapi_python_client.parser.properties import DateProperty
30+
31+
prop = DateProperty(
32+
name="a_prop",
33+
required=True,
34+
nullable=True,
35+
default=None,
36+
)
37+
here = Path(__file__).parent
38+
templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates"
39+
40+
env = jinja2.Environment(
41+
loader=jinja2.ChoiceLoader([jinja2.FileSystemLoader(here), jinja2.FileSystemLoader(templates_dir)])
42+
)
43+
44+
template = env.get_template("date_property_template.py")
45+
content = template.render(property=prop)
46+
expected = here / "required_nullable.py"
47+
assert content == expected.read_text()
48+
49+
50+
def test_optional_nullable():
51+
from openapi_python_client.parser.properties import DateProperty
52+
53+
prop = DateProperty(
54+
name="a_prop",
55+
required=False,
56+
nullable=True,
57+
default=None,
58+
)
59+
here = Path(__file__).parent
60+
templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates"
61+
62+
env = jinja2.Environment(
63+
loader=jinja2.ChoiceLoader([jinja2.FileSystemLoader(here), jinja2.FileSystemLoader(templates_dir)])
64+
)
65+
66+
template = env.get_template("date_property_template.py")
67+
content = template.render(property=prop)
68+
expected = here / "optional_nullable.py"
69+
assert content == expected.read_text()

0 commit comments

Comments
 (0)