Skip to content

Commit 89c10b3

Browse files
committed
Allow Unset values to avoid being send during serialization
1 parent cbff87f commit 89c10b3

13 files changed

+90
-21
lines changed

openapi_python_client/parser/properties.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,38 @@ def get_imports(self, *, prefix: str) -> Set[str]:
6464
back to the root of the generated client.
6565
"""
6666
if self.nullable or not self.required:
67-
return {"from typing import Optional"}
67+
return {"from typing import Optional",
68+
"from typing import cast",
69+
f"from {prefix}types import UNSET"}
6870
return set()
6971

7072
def to_string(self) -> str:
7173
""" How this should be declared in a dataclass """
7274
if self.default:
7375
default = self.default
7476
elif not self.required:
75-
default = "None"
77+
default = "cast(None, UNSET)"
7678
else:
7779
default = None
7880

7981
if default is not None:
80-
return f"{self.python_name}: {self.get_type_string()} = {self.default}"
82+
return f"{self.python_name}: {self.get_type_string()} = {default}"
8183
else:
8284
return f"{self.python_name}: {self.get_type_string()}"
8385

86+
def to_query_method_arg(self) -> str:
87+
""" How this should be declared in a query string """
88+
if self.default:
89+
default = self.default
90+
elif not self.required:
91+
default = "None"
92+
else:
93+
default = None
94+
95+
if default is not None:
96+
return f"{self.python_name}: {self.get_type_string()} = {default}"
97+
else:
98+
return f"{self.python_name}: {self.get_type_string()}"
8499

85100
@dataclass
86101
class StringProperty(Property):

openapi_python_client/templates/endpoint_macros.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ client: Client,
8080
{% endif %}
8181
{# path parameters #}
8282
{% for parameter in endpoint.path_parameters %}
83-
{{ parameter.to_string() }},
83+
{{parameter.to_query_method_arg()}},
8484
{% endfor %}
8585
{# Form data if any #}
8686
{% if endpoint.form_body_reference %}
@@ -96,10 +96,10 @@ json_body: {{ endpoint.json_body.get_type_string() }},
9696
{% endif %}
9797
{# query parameters #}
9898
{% for parameter in endpoint.query_parameters %}
99-
{{ parameter.to_string() }},
99+
{{parameter.to_query_method_arg()}},
100100
{% endfor %}
101101
{% for parameter in endpoint.header_parameters %}
102-
{{ parameter.to_string() }},
102+
{{ parameter.to_query_method_arg() }},
103103
{% endfor %}
104104
{% endmacro %}
105105

openapi_python_client/templates/model.pyi

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ class {{ model.reference.class_name }}:
2424
{% endif %}
2525
{% endfor %}
2626

27-
return {
28-
{% for property in model.required_properties + model.optional_properties %}
29-
"{{ property.name }}": {{ property.python_name }},
30-
{% endfor %}
31-
}
27+
properties: Dict[str, Any] = dict()
28+
29+
{% for property in model.required_properties + model.optional_properties %}
30+
{% if not property.required %}
31+
if {{property.python_name}} is not UNSET:
32+
properties["{{ property.name }}"] = {{ property.python_name }}
33+
{% else %}
34+
properties["{{ property.name }}"] = {{ property.python_name }}
35+
{% endif %}
36+
{% endfor %}
37+
return properties
3238

3339
@staticmethod
3440
def from_dict(d: Dict[str, Any]) -> "{{ model.reference.class_name }}":

openapi_python_client/templates/property_templates/date_property.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ if {{ source }} is not None:
1212
{% if property.required %}
1313
{{ destination }} = {{ source }}.isoformat()
1414
{% else %}
15-
{{ destination }} = {{ source }}.isoformat() if {{ source }} else None
15+
if {{ source }} is UNSET:
16+
{{ destination }} = UNSET
17+
else:
18+
{{ destination }} = {{ source }}.isoformat() if {{ source }} else None
1619
{% endif %}
1720
{% endmacro %}

openapi_python_client/templates/property_templates/datetime_property.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ if {{ source }} is not None:
1212
{% if property.required %}
1313
{{ destination }} = {{ source }}.isoformat()
1414
{% else %}
15-
{{ destination }} = {{ source }}.isoformat() if {{ source }} else None
15+
if {{ source }} is UNSET:
16+
{{ destination }} = UNSET
17+
else:
18+
{{ destination }} = {{ source }}.isoformat() if {{ source }} else None
1619
{% endif %}
1720
{% endmacro %}

openapi_python_client/templates/property_templates/dict_property.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ if {{ source }} is not None:
1212
{% if property.required %}
1313
{{ destination }} = {{ source }}
1414
{% else %}
15-
{{ destination }} = {{ source }} if {{ source }} else None
15+
if {{ source }} is UNSET:
16+
{{ destination }} = UNSET
17+
else:
18+
{{ destination }} = {{ source }} if {{ source }} else None
1619
{% endif %}
1720
{% endmacro %}

openapi_python_client/templates/property_templates/enum_property.pyi

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from openapi_python_client.templates.types import UNSET
2+
13
{% macro construct(property, source) %}
24
{% if property.required %}
35
{{ property.python_name }} = {{ property.reference.class_name }}({{ source }})
@@ -12,6 +14,9 @@ if {{ source }} is not None:
1214
{% if property.required %}
1315
{{ destination }} = {{ source }}.value
1416
{% else %}
15-
{{ destination }} = {{ source }}.value if {{ source }} else None
17+
if {{ source }} is UNSET:
18+
{{ destination }} = UNSET
19+
else:
20+
{{ destination }} = {{ source }}.value if {{ source }} else None
1621
{% endif %}
1722
{% endmacro %}

openapi_python_client/templates/property_templates/file_property.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
{% if property.required %}
88
{{ destination }} = {{ source }}.to_tuple()
99
{% else %}
10-
{{ destination }} = {{ source }}.to_tuple() if {{ source }} else None
10+
if {{ source }} is UNSET:
11+
{{ destination }} = UNSET
12+
else:
13+
{{ destination }} = {{ source }}.to_tuple() if {{ source }} else None
1114
{% endif %}
1215
{% endmacro %}

openapi_python_client/templates/property_templates/list_property.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from openapi_python_client.templates.types import UNSET
2+
13
{% macro construct(property, source) %}
24
{% set inner_property = property.inner_property %}
35
{% if inner_property.template %}
@@ -36,6 +38,8 @@ for {{ inner_source }} in {{ source }}:
3638
{% if not property.required %}
3739
if {{ source }} is None:
3840
{{ destination }} = None
41+
elif {{ source }} is UNSET:
42+
{{ destination }} = UNSET
3943
else:
4044
{{ _transform(property, source, destination) | indent(4) }}
4145
{% else %}

openapi_python_client/templates/property_templates/ref_property.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ if {{ source }} is not None:
1212
{% if property.required %}
1313
{{ destination }} = {{ source }}.to_dict()
1414
{% else %}
15-
{{ destination }} = {{ source }}.to_dict() if {{ source }} else None
15+
if {{ source }} is UNSET:
16+
{{ destination }} = UNSET
17+
else:
18+
{{ destination }} = {{ source }}.to_dict() if {{ source }} else None
1619
{% endif %}
1720
{% endmacro %}

openapi_python_client/templates/property_templates/union_property.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def _parse_{{ property.python_name }}(data: Dict[str, Any]) -> {{ property.get_t
2525
{% if not property.required %}
2626
if {{ source }} is None:
2727
{{ destination }}: {{ property.get_type_string() }} = None
28+
elif {{ source }} is UNSET:
29+
{{ destination }} = UNSET
2830
{% endif %}
2931
{% for inner_property in property.inner_properties %}
3032
{% if loop.first and property.required %}{# No if None statement before this #}
@@ -36,7 +38,7 @@ else:
3638
{% endif %}
3739
{% if inner_property.template %}
3840
{% from "property_templates/" + inner_property.template import transform %}
39-
{{ transform(inner_property, source, destination) | indent(8) }}
41+
{{ transform(inner_property, source, destination) | indent(4) }}
4042
{% else %}
4143
{{ destination }} = {{ source }}
4244
{% endif %}

openapi_python_client/templates/types.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
""" Contains some shared types for properties """
2-
from typing import BinaryIO, Generic, MutableMapping, Optional, TextIO, Tuple, TypeVar, Union
2+
from typing import BinaryIO, Generic, MutableMapping, NewType, Optional, TextIO, Tuple, TypeVar, Union
33

44
import attr
55

6+
Unset = NewType("Unset", object)
7+
UNSET: Unset = Unset(object())
8+
69

710
@attr.s(auto_attribs=True)
811
class File:

tests/test_openapi_parser/test_properties.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_to_string(self, mocker):
4343

4444
assert p.to_string() == f"{snake_case(name)}: {get_type_string()}"
4545
p.required = False
46-
assert p.to_string() == f"{snake_case(name)}: {get_type_string()} = None"
46+
assert p.to_string() == f"{snake_case(name)}: {get_type_string()} = cast(None, UNSET)"
4747

4848
p.default = "TEST"
4949
assert p.to_string() == f"{snake_case(name)}: {get_type_string()} = TEST"
@@ -57,7 +57,26 @@ def test_get_imports(self, mocker):
5757
assert p.get_imports(prefix="") == set()
5858

5959
p.required = False
60-
assert p.get_imports(prefix="") == {"from typing import Optional"}
60+
assert p.get_imports(prefix="") == {
61+
"from types import UNSET",
62+
"from typing import Optional",
63+
"from typing import cast",
64+
}
65+
66+
def test_to_query_method_arg(self, mocker):
67+
from openapi_python_client.parser.properties import Property
68+
69+
name = mocker.MagicMock()
70+
snake_case = mocker.patch("openapi_python_client.utils.snake_case")
71+
p = Property(name=name, required=True, default=None, nullable=False)
72+
get_type_string = mocker.patch.object(p, "get_type_string")
73+
74+
assert p.to_query_method_arg() == f"{snake_case(name)}: {get_type_string()}"
75+
p.required = False
76+
assert p.to_query_method_arg() == f"{snake_case(name)}: {get_type_string()} = None"
77+
78+
p.default = "TEST"
79+
assert p.to_query_method_arg() == f"{snake_case(name)}: {get_type_string()} = TEST"
6180

6281
def test__validate_default(self):
6382
from openapi_python_client.parser.properties import Property

0 commit comments

Comments
 (0)