@autorest/python 6.13.0 → 6.13.2

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.
@@ -132,7 +132,8 @@ TYPE_TO_OBJECT = {
132
132
  "enumvalue": EnumValue,
133
133
  "binary": BinaryType,
134
134
  "any": AnyType,
135
- "datetime": DatetimeType,
135
+ "utcDateTime": DatetimeType,
136
+ "offsetDateTime": DatetimeType,
136
137
  "plainTime": TimeType,
137
138
  "duration": DurationType,
138
139
  "plainDate": DateType,
@@ -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
- inside_types = [type.type_annotation(**kwargs) for type in self.types]
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
- file_import.add_submodule_import("typing", "Union", ImportType.STDLIB)
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:
@@ -1112,16 +1112,18 @@ class _OperationSerializer(
1112
1112
  if response.headers:
1113
1113
  retval.append("")
1114
1114
  deserialize_code: List[str] = []
1115
+ no_stream_logic = False
1115
1116
  if builder.has_stream_response:
1116
1117
  if isinstance(response.type, ByteArraySchema):
1117
- retval.append(f"{'await ' if self.async_mode else ''}response.read()")
1118
- deserialized = "response.content"
1118
+ deserialized = f"{'await ' if self.async_mode else ''}response.read()"
1119
1119
  else:
1120
- deserialized = (
1121
- "response.iter_bytes()"
1122
- if self.code_model.options["version_tolerant"]
1123
- else f"response.stream_download(self._client.{self.pipeline_name})"
1124
- )
1120
+ no_stream_logic = True
1121
+ if self.code_model.options["version_tolerant"]:
1122
+ deserialized = "response.iter_bytes()"
1123
+ else:
1124
+ deserialized = (
1125
+ f"response.stream_download(self._client.{self.pipeline_name})"
1126
+ )
1125
1127
  deserialize_code.append(f"deserialized = {deserialized}")
1126
1128
  elif response.type:
1127
1129
  pylint_disable = ""
@@ -1169,7 +1171,7 @@ class _OperationSerializer(
1169
1171
  deserialize_code.append("else:")
1170
1172
  deserialize_code.append(" deserialized = None")
1171
1173
  if len(deserialize_code) > 0:
1172
- if builder.expose_stream_keyword and not builder.has_stream_response:
1174
+ if builder.expose_stream_keyword and not no_stream_logic:
1173
1175
  retval.append("if _stream:")
1174
1176
  retval.append(" deserialized = response.iter_bytes()")
1175
1177
  retval.append("else:")
@@ -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
- return f"super().__init__({self.properties_to_pass_to_super(model)})"
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) for arg in annotation.__args__ # pyright: ignore
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
- {{ serializer.super_call(model) }}
64
- {% for initialize_property in initialize_properties %}
67
+ {% for line in serializer.super_call(model) %}
68
+ {{ line }}
69
+ {% endfor %}
70
+ {% for initialize_property in initialize_properties %}
65
71
  {{ initialize_property }}
66
- {% endfor %}
67
- {% endif %}
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
- {{ serializer.super_call(model) }}
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
@@ -233,7 +233,7 @@ def update_primitive( # pylint: disable=too-many-return-statements
233
233
  if type_group == "binary":
234
234
  return KNOWN_TYPES["binary"]
235
235
  if type_group == "date-time":
236
- base = _update_type_base("datetime", yaml_data)
236
+ base = _update_type_base("utcDateTime", yaml_data)
237
237
  base["encode"] = yaml_data["format"]
238
238
  return base
239
239
  if type_group == "byte-array":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.13.0",
3
+ "version": "6.13.2",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {