@autorest/python 6.13.0 → 6.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/autorest/codegen/models/combined_type.py +17 -2
- package/autorest/codegen/models/model_type.py +22 -0
- package/autorest/codegen/models/property.py +3 -0
- package/autorest/codegen/serializers/model_serializer.py +12 -4
- package/autorest/codegen/templates/model_base.py.jinja2 +12 -1
- package/autorest/codegen/templates/model_dpg.py.jinja2 +27 -4
- package/autorest/codegen/templates/model_msrest.py.jinja2 +3 -1
- package/autorest/codegen/templates/serialization.py.jinja2 +1 -1
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@ class CombinedType(BaseType):
|
|
|
29
29
|
super().__init__(yaml_data, code_model)
|
|
30
30
|
self.types = types # the types that this type is combining
|
|
31
31
|
self.name = yaml_data.get("name")
|
|
32
|
+
self._is_union_of_literals = all(i.type == "constant" for i in self.types)
|
|
32
33
|
|
|
33
34
|
@property
|
|
34
35
|
def serialization_type(self) -> str:
|
|
@@ -73,7 +74,20 @@ class CombinedType(BaseType):
|
|
|
73
74
|
|
|
74
75
|
Special case for enum, for instance: Union[str, "EnumName"]
|
|
75
76
|
"""
|
|
76
|
-
|
|
77
|
+
# remove duplicates
|
|
78
|
+
inside_types = list(
|
|
79
|
+
dict.fromkeys([type.type_annotation(**kwargs) for type in self.types])
|
|
80
|
+
)
|
|
81
|
+
if len(inside_types) == 1:
|
|
82
|
+
return inside_types[0]
|
|
83
|
+
if self._is_union_of_literals:
|
|
84
|
+
parsed_values = []
|
|
85
|
+
for entry in inside_types:
|
|
86
|
+
match = re.search(r"Literal\[(.*)\]", entry)
|
|
87
|
+
if match is not None:
|
|
88
|
+
parsed_values.append(match.group(1))
|
|
89
|
+
join_string = ", ".join(parsed_values)
|
|
90
|
+
return f"Literal[{join_string}]"
|
|
77
91
|
|
|
78
92
|
# If the inside types has been a Union, peel first and then re-union
|
|
79
93
|
pattern = re.compile(r"Union\[.*\]")
|
|
@@ -116,7 +130,8 @@ class CombinedType(BaseType):
|
|
|
116
130
|
return file_import
|
|
117
131
|
for type in self.types:
|
|
118
132
|
file_import.merge(type.imports(**kwargs))
|
|
119
|
-
|
|
133
|
+
if not self._is_union_of_literals:
|
|
134
|
+
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB)
|
|
120
135
|
return file_import
|
|
121
136
|
|
|
122
137
|
@classmethod
|
|
@@ -76,6 +76,22 @@ class ModelType( # pylint: disable=abstract-method
|
|
|
76
76
|
self.snake_case_name: str = self.yaml_data["snakeCaseName"]
|
|
77
77
|
self.page_result_model: bool = self.yaml_data.get("pageResultModel", False)
|
|
78
78
|
|
|
79
|
+
@property
|
|
80
|
+
def flattened_property(self) -> Optional[Property]:
|
|
81
|
+
try:
|
|
82
|
+
return next(p for p in self.properties if p.flatten)
|
|
83
|
+
except StopIteration:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def flattened_items(self) -> List[str]:
|
|
88
|
+
return [
|
|
89
|
+
item.client_name
|
|
90
|
+
for prop in self.properties
|
|
91
|
+
if isinstance(prop.type, ModelType) and prop.flatten
|
|
92
|
+
for item in prop.type.properties
|
|
93
|
+
]
|
|
94
|
+
|
|
79
95
|
@property
|
|
80
96
|
def is_form_data(self) -> bool:
|
|
81
97
|
return any(p.is_multipart_file_input for p in self.properties)
|
|
@@ -360,3 +376,9 @@ class DPGModelType(GeneratedModelType):
|
|
|
360
376
|
@property
|
|
361
377
|
def instance_check_template(self) -> str:
|
|
362
378
|
return "isinstance({}, _model_base.Model)"
|
|
379
|
+
|
|
380
|
+
def imports(self, **kwargs: Any) -> FileImport:
|
|
381
|
+
file_import = super().imports(**kwargs)
|
|
382
|
+
if self.flattened_property:
|
|
383
|
+
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
384
|
+
return file_import
|
|
@@ -40,6 +40,9 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
40
40
|
self.is_multipart_file_input: bool = yaml_data.get(
|
|
41
41
|
"isMultipartFileInput", False
|
|
42
42
|
)
|
|
43
|
+
self.flatten = self.yaml_data.get("flatten", False) and not getattr(
|
|
44
|
+
self.type, "flattened_property", False
|
|
45
|
+
)
|
|
43
46
|
|
|
44
47
|
@property
|
|
45
48
|
def pylint_disable(self) -> str:
|
|
@@ -60,8 +60,8 @@ class _ModelSerializer(BaseSerializer, ABC):
|
|
|
60
60
|
def variable_documentation_string(prop: Property) -> List[str]:
|
|
61
61
|
return _documentation_string(prop, "ivar", "vartype")
|
|
62
62
|
|
|
63
|
-
def super_call(self, model: ModelType):
|
|
64
|
-
return f"super().__init__({self.properties_to_pass_to_super(model)})"
|
|
63
|
+
def super_call(self, model: ModelType) -> List[str]:
|
|
64
|
+
return [f"super().__init__({self.properties_to_pass_to_super(model)})"]
|
|
65
65
|
|
|
66
66
|
@staticmethod
|
|
67
67
|
def initialize_discriminator_property(model: ModelType, prop: Property) -> str:
|
|
@@ -197,8 +197,16 @@ class MsrestModelSerializer(_ModelSerializer):
|
|
|
197
197
|
|
|
198
198
|
|
|
199
199
|
class DpgModelSerializer(_ModelSerializer):
|
|
200
|
-
def super_call(self, model: ModelType):
|
|
201
|
-
|
|
200
|
+
def super_call(self, model: ModelType) -> List[str]:
|
|
201
|
+
super_call = f"super().__init__({self.properties_to_pass_to_super(model)})"
|
|
202
|
+
if model.flattened_property:
|
|
203
|
+
return [
|
|
204
|
+
"_flattened_input = {k: kwargs.pop(k) for k in kwargs.keys() & self.__flattened_items}",
|
|
205
|
+
super_call,
|
|
206
|
+
"for k, v in _flattened_input.items():",
|
|
207
|
+
" setattr(self, k, v)",
|
|
208
|
+
]
|
|
209
|
+
return [super_call]
|
|
202
210
|
|
|
203
211
|
def imports(self) -> FileImport:
|
|
204
212
|
file_import = FileImport(self.code_model)
|
|
@@ -15,6 +15,7 @@ import base64
|
|
|
15
15
|
import re
|
|
16
16
|
import copy
|
|
17
17
|
import typing
|
|
18
|
+
import enum
|
|
18
19
|
import email.utils
|
|
19
20
|
from datetime import datetime, date, time, timedelta, timezone
|
|
20
21
|
from json import JSONEncoder
|
|
@@ -451,6 +452,8 @@ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-m
|
|
|
451
452
|
return _serialize_bytes(o, format)
|
|
452
453
|
if isinstance(o, decimal.Decimal):
|
|
453
454
|
return float(o)
|
|
455
|
+
if isinstance(o, enum.Enum):
|
|
456
|
+
return o.value
|
|
454
457
|
try:
|
|
455
458
|
# First try datetime.datetime
|
|
456
459
|
return _serialize_datetime(o, format)
|
|
@@ -663,8 +666,12 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=R0911, R0915,
|
|
|
663
666
|
pass
|
|
664
667
|
|
|
665
668
|
if getattr(annotation, "__origin__", None) is typing.Union:
|
|
669
|
+
# initial ordering is we make `string` the last deserialization option, because it is often them most generic
|
|
666
670
|
deserializers = [
|
|
667
|
-
_get_deserialize_callable_from_annotation(arg, module, rf)
|
|
671
|
+
_get_deserialize_callable_from_annotation(arg, module, rf)
|
|
672
|
+
for arg in sorted(
|
|
673
|
+
annotation.__args__, key=lambda x: hasattr(x, "__name__") and x.__name__ == "str" # pyright: ignore
|
|
674
|
+
)
|
|
668
675
|
]
|
|
669
676
|
|
|
670
677
|
def _deserialize_with_union(deserializers, obj):
|
|
@@ -813,6 +820,10 @@ class _RestField:
|
|
|
813
820
|
self._format = format
|
|
814
821
|
self._is_multipart_file_input = is_multipart_file_input
|
|
815
822
|
|
|
823
|
+
@property
|
|
824
|
+
def _class_type(self) -> typing.Any:
|
|
825
|
+
return getattr(self._type, "args", [None])[0]
|
|
826
|
+
|
|
816
827
|
@property
|
|
817
828
|
def _rest_name(self) -> str:
|
|
818
829
|
if self._rest_name_input is None:
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
{% endif %}
|
|
40
40
|
{% endfor %}
|
|
41
41
|
|
|
42
|
+
{% if code_model.options["models_mode"] == "dpg" and model.flattened_property %}
|
|
43
|
+
__flattened_items = ["{{ model.flattened_items|join('\", \"') }}"]
|
|
44
|
+
{% endif %}
|
|
45
|
+
|
|
42
46
|
{% if not model.internal and serializer.init_line(model) %}
|
|
43
47
|
@overload
|
|
44
48
|
def __init__(
|
|
@@ -60,8 +64,27 @@
|
|
|
60
64
|
{% set initialize_properties = serializer.initialize_properties(model) %}
|
|
61
65
|
{% if not model.internal and serializer.init_line(model) or initialize_properties %}
|
|
62
66
|
def __init__(self, *args: Any, **kwargs: Any) -> None:{{ '# pylint: disable=useless-super-delegation' if not initialize_properties else '' }}
|
|
63
|
-
{
|
|
64
|
-
|
|
67
|
+
{% for line in serializer.super_call(model) %}
|
|
68
|
+
{{ line }}
|
|
69
|
+
{% endfor %}
|
|
70
|
+
{% for initialize_property in initialize_properties %}
|
|
65
71
|
{{ initialize_property }}
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
{% endfor %}
|
|
73
|
+
{% if code_model.options["models_mode"] == "dpg" and model.flattened_property %}
|
|
74
|
+
{% set flattened_property_attr = model.flattened_property.client_name %}
|
|
75
|
+
|
|
76
|
+
def __getattr__(self, name: str) -> Any:
|
|
77
|
+
if name in self.__flattened_items:
|
|
78
|
+
if self.{{ flattened_property_attr }} is None: return None
|
|
79
|
+
return getattr(self.{{ flattened_property_attr }}, name)
|
|
80
|
+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
|
|
81
|
+
|
|
82
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
83
|
+
if key in self.__flattened_items:
|
|
84
|
+
if self.{{ flattened_property_attr }} is None:
|
|
85
|
+
self.{{ flattened_property_attr }} = self._attr_to_rest_field["{{ flattened_property_attr }}"]._class_type()
|
|
86
|
+
setattr(self.properties, key, value)
|
|
87
|
+
else:
|
|
88
|
+
super().__setattr__(key, value)
|
|
89
|
+
{% endif %}
|
|
90
|
+
{% endif %}
|
|
@@ -83,7 +83,9 @@
|
|
|
83
83
|
{% endfor %}
|
|
84
84
|
{% endif %}
|
|
85
85
|
"""
|
|
86
|
-
|
|
86
|
+
{% for line in serializer.super_call(model) %}
|
|
87
|
+
{{ line }}
|
|
88
|
+
{% endfor %}
|
|
87
89
|
{% for initialize_property in initialize_properties %}
|
|
88
90
|
{{ initialize_property }}
|
|
89
91
|
{% endfor %}
|
|
@@ -1520,7 +1520,7 @@ class Deserializer(object):
|
|
|
1520
1520
|
return target, target
|
|
1521
1521
|
|
|
1522
1522
|
try:
|
|
1523
|
-
target = target._classify(data, self.dependencies)
|
|
1523
|
+
target = target._classify(data, self.dependencies) # type: ignore
|
|
1524
1524
|
except AttributeError:
|
|
1525
1525
|
pass # Target is not a Model, no classify
|
|
1526
1526
|
return target, target.__class__.__name__ # type: ignore
|