@autorest/python 6.5.1 → 6.7.0

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.
@@ -236,11 +236,6 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
236
236
  """Do we want a mixin ABC class for typing purposes?"""
237
237
  return any(o for o in self.operation_groups if o.is_mixin)
238
238
 
239
- @property
240
- def need_format_url(self) -> bool:
241
- """Whether we need to format urls. If so, we need to vendor core."""
242
- return any(rq for rq in self.request_builders if rq.parameters.path)
243
-
244
239
  @property
245
240
  def has_lro_operations(self) -> bool:
246
241
  """Are there any LRO operations in this SDK?"""
@@ -269,8 +264,8 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
269
264
  @property
270
265
  def need_request_converter(self) -> bool:
271
266
  """
272
- Whether we need to convert our created azure.core.rest.HttpRequests to
273
- azure.core.pipeline.transport.HttpRequests
267
+ Whether we need to convert our created azure.core.rest.HttpRequest to
268
+ azure.core.pipeline.transport.HttpRequest
274
269
  """
275
270
  return (
276
271
  self.code_model.options["show_operations"]
@@ -129,18 +129,12 @@ class CodeModel: # pylint: disable=too-many-public-methods
129
129
  return True
130
130
  if async_mode:
131
131
  return self.need_mixin_abc
132
- return (
133
- self.need_request_converter or self.need_format_url or self.need_mixin_abc
134
- )
132
+ return self.need_request_converter or self.need_mixin_abc
135
133
 
136
134
  @property
137
135
  def need_request_converter(self) -> bool:
138
136
  return any(c for c in self.clients if c.need_request_converter)
139
137
 
140
- @property
141
- def need_format_url(self) -> bool:
142
- return any(c for c in self.clients if c.need_format_url)
143
-
144
138
  @property
145
139
  def need_mixin_abc(self) -> bool:
146
140
  return any(c for c in self.clients if c.has_mixin)
@@ -128,6 +128,18 @@ class LROOperationBase(OperationBase[LROResponseType]):
128
128
  "distributed_trace_async",
129
129
  ImportType.AZURECORE,
130
130
  )
131
+ if (
132
+ self.code_model.options["models_mode"] == "dpg"
133
+ and self.lro_response
134
+ and self.lro_response.type
135
+ and self.lro_response.type.type == "model"
136
+ ):
137
+ # used in the case if initial operation returns none
138
+ # but final call returns a model
139
+ relative_path = "..." if async_mode else ".."
140
+ file_import.add_submodule_import(
141
+ f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL
142
+ )
131
143
  file_import.add_submodule_import(
132
144
  "typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
133
145
  )
@@ -207,7 +207,10 @@ class ModelType( # pylint: disable=abstract-method
207
207
 
208
208
  @property
209
209
  def has_readonly_or_constant_property(self) -> bool:
210
- return any(x.readonly or x.constant for x in self.properties)
210
+ return any(
211
+ x.readonly or x.constant or x.visibility == ["read"]
212
+ for x in self.properties
213
+ )
211
214
 
212
215
  @property
213
216
  def discriminator(self) -> Optional[Property]:
@@ -331,7 +331,9 @@ class OperationBase( # pylint: disable=too-many-public-methods
331
331
  )
332
332
  return file_import
333
333
 
334
- def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
334
+ def imports( # pylint: disable=too-many-branches
335
+ self, async_mode: bool, **kwargs: Any
336
+ ) -> FileImport:
335
337
  if self.abstract:
336
338
  return FileImport()
337
339
  file_import = self._imports_shared(async_mode, **kwargs)
@@ -356,6 +358,12 @@ class OperationBase( # pylint: disable=too-many-public-methods
356
358
  file_import.merge(
357
359
  self.parameters.body_parameter.type.imports(operation=self, **kwargs)
358
360
  )
361
+ if not async_mode:
362
+ for param in self.parameters.headers:
363
+ if param.wire_name.lower() == "repeatability-request-id":
364
+ file_import.add_import("uuid", ImportType.STDLIB)
365
+ elif param.wire_name.lower() == "repeatability-first-sent":
366
+ file_import.add_import("datetime", ImportType.STDLIB)
359
367
 
360
368
  # Exceptions
361
369
  errors = [
@@ -391,16 +399,30 @@ class OperationBase( # pylint: disable=too-many-public-methods
391
399
  file_import.add_submodule_import(
392
400
  f"{relative_path}_vendor", "_convert_request", ImportType.LOCAL
393
401
  )
394
- if async_mode:
395
- file_import.add_submodule_import(
396
- "azure.core.pipeline.transport",
397
- "AsyncHttpResponse",
398
- ImportType.AZURECORE,
399
- )
402
+ if self.code_model.need_request_converter:
403
+ if async_mode:
404
+ file_import.add_submodule_import(
405
+ "azure.core.pipeline.transport",
406
+ "AsyncHttpResponse",
407
+ ImportType.AZURECORE,
408
+ )
409
+ else:
410
+ file_import.add_submodule_import(
411
+ "azure.core.pipeline.transport",
412
+ "HttpResponse",
413
+ ImportType.AZURECORE,
414
+ )
400
415
  else:
401
- file_import.add_submodule_import(
402
- "azure.core.pipeline.transport", "HttpResponse", ImportType.AZURECORE
403
- )
416
+ if async_mode:
417
+ file_import.add_submodule_import(
418
+ "azure.core.rest",
419
+ "AsyncHttpResponse",
420
+ ImportType.AZURECORE,
421
+ )
422
+ else:
423
+ file_import.add_submodule_import(
424
+ "azure.core.rest", "HttpResponse", ImportType.AZURECORE
425
+ )
404
426
  if (
405
427
  self.code_model.options["builders_visibility"] == "embedded"
406
428
  and not async_mode
@@ -52,6 +52,9 @@ class ParameterDelimeter(str, Enum):
52
52
  COMMA = "comma"
53
53
 
54
54
 
55
+ SPECIAL_HANDLE_HEADERS = ["repeatability-request-id", "repeatability-first-sent"]
56
+
57
+
55
58
  class _ParameterBase(
56
59
  BaseModel, abc.ABC
57
60
  ): # pylint: disable=too-many-instance-attributes
@@ -90,6 +93,10 @@ class _ParameterBase(
90
93
  self.default_to_unset_sentinel: bool = self.yaml_data.get(
91
94
  "defaultToUnsetSentinel", False
92
95
  )
96
+ self.is_special_handle_header: bool = (
97
+ self.location == ParameterLocation.HEADER
98
+ and self.wire_name.lower() in SPECIAL_HANDLE_HEADERS
99
+ )
93
100
 
94
101
  @property
95
102
  def constant(self) -> bool:
@@ -213,7 +213,9 @@ class _ParameterListBase(
213
213
  method_params: List[Union[ParameterType, BodyParameterType]] = [
214
214
  p
215
215
  for p in self.parameters
216
- if p.in_method_signature and p.implementation == self.implementation
216
+ if p.in_method_signature
217
+ and p.implementation == self.implementation
218
+ and not p.is_special_handle_header
217
219
  ]
218
220
  if self._body_parameter:
219
221
  if self._body_parameter.in_method_signature:
@@ -29,6 +29,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
29
29
  self.type = type
30
30
  self.optional: bool = self.yaml_data["optional"]
31
31
  self.readonly: bool = self.yaml_data.get("readonly", False)
32
+ self.visibility: List[str] = self.yaml_data.get("visibility", [])
32
33
  self.is_polymorphic: bool = self.yaml_data.get("isPolymorphic", False)
33
34
  self.is_discriminator: bool = yaml_data.get("isDiscriminator", False)
34
35
  self.client_default_value = yaml_data.get("clientDefaultValue", None)
@@ -92,10 +92,6 @@ class RequestBuilderBase(BaseBuilder[ParameterListType]):
92
92
  ImportType.AZURECORE,
93
93
  )
94
94
 
95
- if self.parameters.path:
96
- file_import.add_submodule_import(
97
- f"{relative_path}_vendor", "_format_url_section", ImportType.LOCAL
98
- )
99
95
  if self.parameters.headers or self.parameters.query:
100
96
  file_import.add_submodule_import(
101
97
  "azure.core.utils", "case_insensitive_dict", ImportType.AZURECORE
@@ -437,6 +437,21 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
437
437
  ]
438
438
  return retval
439
439
 
440
+ @staticmethod
441
+ def _serialize_special_handle_header(param: Parameter) -> List[str]:
442
+ if param.wire_name.lower() == "repeatability-request-id":
443
+ return [
444
+ """if "Repeatability-Request-ID" not in _headers:""",
445
+ """ _headers["Repeatability-Request-ID"] = str(uuid.uuid4())""",
446
+ ]
447
+ if param.wire_name.lower() == "repeatability-first-sent":
448
+ return [
449
+ """if "Repeatability-First-Sent" not in _headers:""",
450
+ """ _headers["Repeatability-First-Sent"] = _SERIALIZER.serialize_data(datetime.datetime.now(),
451
+ "rfc-1123")""",
452
+ ]
453
+ raise ValueError(f"Unsupported special header: {param}")
454
+
440
455
  def serialize_path(self, builder: BuilderType) -> List[str]:
441
456
  return self.parameter_serializer.serialize_path(
442
457
  builder.parameters.path, self.serializer_name
@@ -548,12 +563,15 @@ class RequestBuilderSerializer(
548
563
  def serialize_headers(self, builder: RequestBuilderType) -> List[str]:
549
564
  retval = ["# Construct headers"]
550
565
  for parameter in builder.parameters.headers:
551
- retval.extend(
552
- self._serialize_parameter(
553
- parameter,
554
- kwarg_name="headers",
566
+ if parameter.is_special_handle_header:
567
+ retval.extend(self._serialize_special_handle_header(parameter))
568
+ else:
569
+ retval.extend(
570
+ self._serialize_parameter(
571
+ parameter,
572
+ kwarg_name="headers",
573
+ )
555
574
  )
556
- )
557
575
  return retval
558
576
 
559
577
  def serialize_query(self, builder: RequestBuilderType) -> List[str]:
@@ -710,6 +728,7 @@ class _OperationSerializer(
710
728
  )
711
729
  else PopKwargType.SIMPLE,
712
730
  check_client_input=not self.code_model.options["multiapi"],
731
+ operation_name=f"('{builder.name}')" if builder.group_name == "" else "",
713
732
  )
714
733
  cls_annotation = builder.cls_type_annotation(async_mode=self.async_mode)
715
734
  pylint_disable = ""
@@ -1524,7 +1543,7 @@ class _LROOperationSerializer(_OperationSerializer[LROOperationType]):
1524
1543
  retval.append("if cont_token is None:")
1525
1544
  retval.append(
1526
1545
  f" raw_result = {self._call_method}self.{builder.initial_operation.name}("
1527
- f"{'' if builder.lro_response and builder.lro_response.type else ' # type: ignore'}"
1546
+ f"{'' if any(rsp.type for rsp in builder.initial_operation.responses) else ' # type: ignore'}"
1528
1547
  )
1529
1548
  retval.extend(
1530
1549
  [
@@ -150,10 +150,16 @@ class ClientSerializer:
150
150
  og for og in self.client.operation_groups if not og.is_mixin
151
151
  ]
152
152
  for og in operation_groups:
153
+ if og.code_model.options["multiapi"]:
154
+ api_version = (
155
+ f", '{og.api_versions[0]}'" if og.api_versions else ", None"
156
+ )
157
+ else:
158
+ api_version = ""
153
159
  retval.extend(
154
160
  [
155
161
  f"self.{og.property_name} = {og.class_name}({og.pylint_disable}",
156
- " self._client, self._config, self._serialize, self._deserialize",
162
+ f" self._client, self._config, self._serialize, self._deserialize{api_version}",
157
163
  ")",
158
164
  ]
159
165
  )
@@ -109,10 +109,6 @@ class GeneralSerializer:
109
109
  ImportType.AZURECORE,
110
110
  )
111
111
 
112
- if self.code_model.need_format_url and not self.async_mode:
113
- file_import.add_submodule_import("typing", "List", ImportType.STDLIB)
114
- file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
115
-
116
112
  if self.code_model.need_mixin_abc:
117
113
  file_import.add_submodule_import(
118
114
  "abc",
@@ -257,6 +257,9 @@ class DpgModelSerializer(_ModelSerializer):
257
257
  args.append(f'name="{prop.wire_name}"')
258
258
  if prop.readonly:
259
259
  args.append("readonly=True")
260
+ if prop.visibility:
261
+ v_list = ", ".join(f'"{x}"' for x in prop.visibility)
262
+ args.append(f"visibility=[{v_list}]")
260
263
  if prop.client_default_value is not None:
261
264
  args.append(f"default={prop.client_default_value_declaration}")
262
265
 
@@ -280,3 +283,11 @@ class DpgModelSerializer(_ModelSerializer):
280
283
  f"{cast(ConstantType, prop.type).get_declaration()}"
281
284
  )
282
285
  return init_args
286
+
287
+ @staticmethod
288
+ def _init_line_parameters(model: ModelType):
289
+ return [
290
+ p
291
+ for p in model.properties
292
+ if not p.is_discriminator and not p.constant and p.visibility != ["read"]
293
+ ]
@@ -3,7 +3,7 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from typing import List, Sequence, Union
6
+ from typing import List, Sequence, Union, Optional
7
7
  from enum import Enum, auto
8
8
 
9
9
 
@@ -111,6 +111,7 @@ class ParameterSerializer:
111
111
  pop_headers_kwarg: PopKwargType,
112
112
  pop_params_kwarg: PopKwargType,
113
113
  check_client_input: bool = False,
114
+ operation_name: Optional[str] = None,
114
115
  ) -> List[str]:
115
116
  retval = []
116
117
 
@@ -142,9 +143,18 @@ class ParameterSerializer:
142
143
  if kwarg.location == ParameterLocation.HEADER
143
144
  else "params"
144
145
  )
146
+ if (
147
+ kwarg.client_name == "api_version"
148
+ and kwarg.code_model.options["multiapi"]
149
+ and operation_name is not None
150
+ ):
151
+ default_value = (
152
+ f"self._api_version{operation_name} or {default_value}"
153
+ )
145
154
  default_value = (
146
155
  f"_{kwarg_dict}.pop('{kwarg.wire_name}', {default_value})"
147
156
  )
157
+
148
158
  retval.append(
149
159
  f"{kwarg.client_name}: {type_annot} = kwargs.pop('{kwarg.client_name}', "
150
160
  + f"{default_value})"
@@ -136,7 +136,7 @@ def _serialize_datetime(o):
136
136
 
137
137
  def _is_readonly(p):
138
138
  try:
139
- return p._readonly # pylint: disable=protected-access
139
+ return p._visibility == ["read"] # pylint: disable=protected-access
140
140
  except AttributeError:
141
141
  return False
142
142
 
@@ -634,14 +634,14 @@ class _RestField:
634
634
  name: typing.Optional[str] = None,
635
635
  type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
636
636
  is_discriminator: bool = False,
637
- readonly: bool = False,
637
+ visibility: typing.Optional[typing.List[str]] = None,
638
638
  default: typing.Any = _UNSET,
639
639
  ):
640
640
  self._type = type
641
641
  self._rest_name_input = name
642
642
  self._module: typing.Optional[str] = None
643
643
  self._is_discriminator = is_discriminator
644
- self._readonly = readonly
644
+ self._visibility = visibility
645
645
  self._is_model = False
646
646
  self._default = default
647
647
 
@@ -681,10 +681,10 @@ def rest_field(
681
681
  *,
682
682
  name: typing.Optional[str] = None,
683
683
  type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
684
- readonly: bool = False,
684
+ visibility: typing.Optional[typing.List[str]] = None,
685
685
  default: typing.Any = _UNSET,
686
686
  ) -> typing.Any:
687
- return _RestField(name=name, type=type, readonly=readonly, default=default)
687
+ return _RestField(name=name, type=type, visibility=visibility, default=default)
688
688
 
689
689
 
690
690
  def rest_discriminator(
@@ -30,12 +30,22 @@ class {{ operation_group.class_name }}{{ base_class }}:{{ disable }}
30
30
  self._config = input_args.pop(0) if input_args else kwargs.pop("config")
31
31
  self._serialize = input_args.pop(0) if input_args else kwargs.pop("serializer")
32
32
  self._deserialize = input_args.pop(0) if input_args else kwargs.pop("deserializer")
33
+ {% if code_model.options["multiapi"] %}
34
+ self._api_version = input_args.pop(0) if input_args else kwargs.pop("api_version")
35
+ {% endif %}
33
36
  {{ check_abstract_methods() }}
34
37
  {% elif operation_group.has_abstract_operations %}
35
38
 
36
39
  def __init__(self){{ return_none_type_annotation }}:
37
40
  {{ check_abstract_methods() }}
38
41
  {% endif %}
42
+ {% if operation_group.is_mixin and code_model.options["multiapi"] %}
43
+ def _api_version(self, op_name: str) -> str: # pylint: disable=unused-argument
44
+ try:
45
+ return self._config.api_version
46
+ except: # pylint: disable=bare-except
47
+ return ""
48
+ {% endif %}
39
49
  {% for operation in operation_group.operations if not operation.abstract %}
40
50
 
41
51
  {% set request_builder = operation.request_builder %}
@@ -15,7 +15,7 @@
15
15
  {{ request_builder_serializer.construct_url(request_builder) }}
16
16
  {% if request_builder.parameters.path %}
17
17
  {{ op_tools.serialize(request_builder_serializer.serialize_path(request_builder)) | indent }}
18
- _url: str = _format_url_section(_url, **path_format_arguments) # type: ignore
18
+ _url: str = _url.format(**path_format_arguments) # type: ignore
19
19
  {% endif %}
20
20
 
21
21
  {% if request_builder.parameters.query %}
@@ -664,8 +664,9 @@ class Serializer(object):
664
664
  _serialized.update(_new_attr) # type: ignore
665
665
  _new_attr = _new_attr[k] # type: ignore
666
666
  _serialized = _serialized[k]
667
- except ValueError:
668
- continue
667
+ except ValueError as err:
668
+ if isinstance(err, SerializationError):
669
+ raise
669
670
 
670
671
  except (AttributeError, KeyError, TypeError) as err:
671
672
  msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj))
@@ -905,7 +906,9 @@ class Serializer(object):
905
906
  for d in data:
906
907
  try:
907
908
  serialized.append(self.serialize_data(d, iter_type, **kwargs))
908
- except ValueError:
909
+ except ValueError as err:
910
+ if isinstance(err, SerializationError):
911
+ raise
909
912
  serialized.append(None)
910
913
 
911
914
  if div:
@@ -952,7 +955,9 @@ class Serializer(object):
952
955
  for key, value in attr.items():
953
956
  try:
954
957
  serialized[self.serialize_unicode(key)] = self.serialize_data(value, dict_type, **kwargs)
955
- except ValueError:
958
+ except ValueError as err:
959
+ if isinstance(err, SerializationError):
960
+ raise
956
961
  serialized[self.serialize_unicode(key)] = None
957
962
 
958
963
  if "xml" in serialization_ctxt:
@@ -11,21 +11,6 @@ def _convert_request(request, files=None):
11
11
  request.set_formdata_body(files)
12
12
  return request
13
13
  {% endif %}
14
- {% if code_model.need_format_url and not async_mode %}
15
-
16
- def _format_url_section(template, **kwargs):
17
- components = template.split("/")
18
- while components:
19
- try:
20
- return template.format(**kwargs)
21
- except KeyError as key:
22
- # Need the cast, as for some reasons "split" is typed as list[str | Any]
23
- formatted_components = cast(List[str], template.split("/"))
24
- components = [
25
- c for c in formatted_components if "{{{}}}".format(key.args[0]) not in c
26
- ]
27
- template = "/".join(components)
28
- {% endif %}
29
14
  {% if code_model.need_mixin_abc %}
30
15
  {% for client in clients | selectattr("has_mixin") %}
31
16
 
@@ -87,6 +87,8 @@ class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_
87
87
  else:
88
88
  raise ValueError("API version {} is not available".format(api_version))
89
89
  {% endif %}
90
+ if api_version:
91
+ kwargs.setdefault('api_version', api_version)
90
92
  self._config = {{ code_model.client.name }}Configuration({{ code_model.global_parameters.call }}{{ ", " if code_model.global_parameters.call }}**kwargs)
91
93
  self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url={{ code_model.host_variable_name }}, config=self._config, **kwargs)
92
94
  super({{ code_model.client.name }}, self).__init__(
@@ -132,7 +134,7 @@ class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_
132
134
  else:
133
135
  raise ValueError("API version {} does not have operation group '{{ operation_group.name }}'".format(api_version))
134
136
  self._config.api_version = api_version
135
- return OperationClass(self._client, self._config, Serializer(self._models_dict(api_version)), Deserializer(self._models_dict(api_version)))
137
+ return OperationClass(self._client, self._config, Serializer(self._models_dict(api_version)), Deserializer(self._models_dict(api_version)), api_version)
136
138
  {% endfor %}
137
139
 
138
140
  {{ def }} close(self):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.5.1",
3
+ "version": "6.7.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "@microsoft.azure/autorest.testserver": "^3.3.46",
26
- "typescript": "^5.0.4"
26
+ "typescript": "~5.1.3"
27
27
  },
28
28
  "files": [
29
29
  "autorest/**/*.py",