Skip to content

Commit a365384

Browse files
committed
Perf improvements to #268 (additionalProperties Union Fix):
- Added CHANGELOG entry - Change inner_properties_without_template to bool generated once - Change inner_properties_with_template to generator
1 parent bfab9d4 commit a365384

File tree

6 files changed

+42
-30
lines changed

6 files changed

+42
-30
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.7.3 - Unreleased
9+
### Fixes
10+
- Spacing and extra returns for Union types of `additionalProperties` (#266 & #268). Thanks @joshzana & @packyg!
11+
812
## 0.7.2 - 2020-12-08
913
### Fixes
1014
- A bug in handling optional properties that are themselves models (introduced in 0.7.1) (#262). Thanks @packyg!

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _parse_additional_property(
5858
return additional_property
5959
except: # noqa: E722
6060
pass
61-
return cast(str, data)
61+
return cast(Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool], data)
6262

6363
additional_property = _parse_additional_property(prop_dict)
6464

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _parse_additional_property(
5858
return additional_property
5959
except: # noqa: E722
6060
pass
61-
return cast(str, data)
61+
return cast(Union[ModelWithAnyJsonPropertiesAdditionalProperty, List[str], str, float, int, bool], data)
6262

6363
additional_property = _parse_additional_property(prop_dict)
6464

openapi_python_client/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,15 @@
2525
__version__ = version(__package__)
2626

2727

28+
TEMPLATE_FILTERS = {
29+
"snakecase": utils.snake_case,
30+
"kebabcase": utils.kebab_case,
31+
"pascalcase": utils.pascal_case,
32+
"any": any,
33+
}
34+
35+
2836
class Project:
29-
TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case, "pascalcase": utils.pascal_case}
3037
project_name_override: Optional[str] = None
3138
package_name_override: Optional[str] = None
3239
package_version_override: Optional[str] = None
@@ -57,7 +64,7 @@ def __init__(self, *, openapi: GeneratorData, custom_template_path: Optional[Pat
5764
)
5865
self.version: str = self.package_version_override or openapi.version
5966

60-
self.env.filters.update(self.TEMPLATE_FILTERS)
67+
self.env.filters.update(TEMPLATE_FILTERS)
6168

6269
def build(self) -> Sequence[GeneratorError]:
6370
""" Create the project from templates """

openapi_python_client/parser/properties/__init__.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
import builtins
21
from itertools import chain
3-
from typing import Any, ClassVar, Dict, Generic, Iterable, List, Optional, Set, Tuple, TypeVar, Union
2+
from typing import Any, ClassVar, Dict, Generic, Iterable, List, Optional, Set, Tuple, TypeVar, Union, Iterator
43

54
import attr
65

7-
from ... import schema as oai
8-
from ... import utils
9-
from ..errors import PropertyError, ValidationError
10-
from ..reference import Reference
116
from .converter import convert, convert_chain
127
from .enum_property import EnumProperty
138
from .model_property import ModelProperty
149
from .property import Property
1510
from .schemas import Schemas
11+
from ..errors import PropertyError, ValidationError
12+
from ..reference import Reference
13+
from ... import schema as oai
14+
from ... import utils
1615

1716

1817
@attr.s(auto_attribs=True, frozen=True)
@@ -160,6 +159,13 @@ class UnionProperty(Property):
160159

161160
inner_properties: List[Property]
162161
template: ClassVar[str] = "union_property.pyi"
162+
has_properties_without_templates: bool = attr.ib(init=False)
163+
164+
def __attrs_post_init__(self) -> None:
165+
super().__attrs_post_init__()
166+
object.__setattr__(
167+
self, "has_properties_without_templates", any(prop.template is None for prop in self.inner_properties)
168+
)
163169

164170
def get_type_string(self, no_optional: bool = False) -> str:
165171
""" Get a string representation of type that should be used when declaring this property """
@@ -185,16 +191,11 @@ def get_imports(self, *, prefix: str) -> Set[str]:
185191
imports = super().get_imports(prefix=prefix)
186192
for inner_prop in self.inner_properties:
187193
imports.update(inner_prop.get_imports(prefix=prefix))
188-
imports.add("from typing import Union")
194+
imports.add("from typing import cast, Union")
189195
return imports
190196

191-
@builtins.property
192-
def inner_properties_with_template(self) -> List[Property]:
193-
return [prop for prop in self.inner_properties if prop.template]
194-
195-
@builtins.property
196-
def inner_properties_without_template(self) -> List[Property]:
197-
return [prop for prop in self.inner_properties if not prop.template]
197+
def inner_properties_with_template(self) -> Iterator[Property]:
198+
return (prop for prop in self.inner_properties if prop.template)
198199

199200

200201
def _string_based_property(

openapi_python_client/templates/property_templates/union_property.pyi

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
def _parse_{{ property.python_name }}(data: Any) -> {{ property.get_type_string() }}:
33
data = None if isinstance(data, Unset) else data
44
{{ property.python_name }}: {{ property.get_type_string() }}
5-
{% for inner_property in property.inner_properties_with_template %}
6-
{% if not loop.last or property.inner_properties_without_template %}
5+
{% for inner_property in property.inner_properties_with_template() %}
6+
{% if not loop.last or property.has_properties_without_templates %}
77
try:
88
{% from "property_templates/" + inner_property.template import construct %}
99
{{ construct(inner_property, "data", initial_value="UNSET") | indent(8) }}
@@ -16,9 +16,9 @@ def _parse_{{ property.python_name }}(data: Any) -> {{ property.get_type_string(
1616
return {{ property.python_name }}
1717
{% endif %}
1818
{% endfor %}
19-
{% if property.inner_properties_without_template %}
19+
{% if property.has_properties_without_templates %}
2020
{# Doesn't really matter what we cast it to as this type will be erased, so cast to one of the options #}
21-
return cast({{ property.inner_properties_without_template[0].get_type_string() }}, data)
21+
return cast({{ property.get_type_string() }}, data)
2222
{% endif %}
2323

2424
{{ property.python_name }} = _parse_{{ property.python_name }}({{ source }})
@@ -32,28 +32,28 @@ if isinstance({{ source }}, Unset):
3232
{{ destination }} = UNSET
3333
{% endif %}
3434
{% if property.nullable %}
35-
{% if property.required %}
35+
{% if property.required %}
3636
if {{ source }} is None:
37-
{% else %}{# There's an if UNSET statement before this #}
37+
{% else %}{# There's an if UNSET statement before this #}
3838
elif {{ source }} is None:
39-
{% endif %}
39+
{% endif %}
4040
{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = None
4141
{% endif %}
42-
{% for inner_property in property.inner_properties_with_template %}
42+
{% for inner_property in property.inner_properties_with_template() %}
4343
{% if loop.first and property.required and not property.nullable %}{# No if UNSET or if None statement before this #}
4444
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
45-
{% elif not loop.last or property.inner_properties_without_template %}
45+
{% elif not loop.last or property.has_properties_without_templates %}
4646
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
4747
{% else %}
4848
else:
4949
{% endif %}
50-
{% from "property_templates/" + inner_property.template import transform %}
50+
{% from "property_templates/" + inner_property.template import transform %}
5151
{{ transform(inner_property, source, destination, declare_type=False) | indent(4) }}
5252
{% endfor %}
53-
{% if property.inner_properties_without_template and (property.inner_properties_with_template or not property.required)%}
53+
{% if property.has_properties_without_templates and (property.inner_properties_with_template() | any or not property.required)%}
5454
else:
5555
{{ destination }} = {{ source }}
56-
{% elif property.inner_properties_without_template %}
56+
{% elif property.has_properties_without_templates %}
5757
{{ destination }} = {{ source }}
5858
{% endif %}
5959

0 commit comments

Comments
 (0)