@autorest/python 6.15.0 → 6.17.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.
@@ -54,7 +54,7 @@ class ReaderAndWriter:
54
54
  fd.write(file_content)
55
55
 
56
56
  def list_file(self) -> List[str]:
57
- return [str(f) for f in self.output_folder.glob("**/*") if f.is_file()]
57
+ return [str(f.relative_to(self.output_folder)) for f in self.output_folder.glob("**/*") if f.is_file()]
58
58
 
59
59
 
60
60
  class Plugin(ReaderAndWriter, ABC):
@@ -37,15 +37,14 @@ class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
37
37
  [
38
38
  Path(f)
39
39
  for f in self.list_file()
40
- if all(
41
- item not in f
42
- for item in (
43
- "__pycache__",
44
- "node_modules",
45
- ".tox",
46
- ".mypy_cache",
47
- )
40
+ if Path(f).parts[0]
41
+ not in (
42
+ "__pycache__",
43
+ "node_modules",
44
+ "venv",
45
+ "env",
48
46
  )
47
+ and not Path(f).parts[0].startswith(".")
49
48
  and Path(f).suffix == ".py"
50
49
  ],
51
50
  )
@@ -164,6 +164,10 @@ class OptionsRetriever:
164
164
  except AttributeError:
165
165
  return packaging_files_config
166
166
 
167
+ @property
168
+ def package_version(self) -> Optional[str]:
169
+ return str(self.options.get("package-version", ""))
170
+
167
171
 
168
172
  class CodeGenerator(Plugin):
169
173
  def __init__(self, *args, **kwargs: Any) -> None:
@@ -18,6 +18,7 @@ from .request_builder import (
18
18
  from .parameter import Parameter, ParameterMethodLocation
19
19
  from .lro_operation import LROOperation
20
20
  from .lro_paging_operation import LROPagingOperation
21
+ from ...utils import extract_original_name
21
22
 
22
23
  ParameterListType = TypeVar(
23
24
  "ParameterListType",
@@ -92,7 +93,7 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
92
93
  if operation_yaml.get("isLroInitialOperation"):
93
94
  # we want to change the name
94
95
  request_builder.name = request_builder.get_name(
95
- request_builder.yaml_data["name"][1 : -len("_initial")],
96
+ extract_original_name(request_builder.yaml_data["name"]),
96
97
  request_builder.yaml_data,
97
98
  request_builder.code_model,
98
99
  request_builder.client,
@@ -32,6 +32,10 @@ class UsageFlags(Enum):
32
32
  ApiVersionEnum = 8
33
33
  JsonMergePatch = 16
34
34
  MultipartFormData = 32
35
+ Spread = 64
36
+ Error = 128
37
+ Json = 256
38
+ Xml = 512
35
39
 
36
40
 
37
41
  def _get_properties(type: "ModelType", properties: List[Property]) -> List[Property]:
@@ -86,7 +90,7 @@ class ModelType( # pylint: disable=abstract-method
86
90
 
87
91
  @property
88
92
  def is_usage_output(self) -> bool:
89
- return self.usage == UsageFlags.Output.value
93
+ return self.usage & UsageFlags.Output.value
90
94
 
91
95
  @property
92
96
  def flattened_property(self) -> Optional[Property]:
@@ -343,6 +343,8 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
343
343
  "ResourceExistsError",
344
344
  "ResourceNotModifiedError",
345
345
  ]
346
+ if self.stream_value:
347
+ errors.extend(["StreamConsumedError", "StreamClosedError"])
346
348
  for error in errors:
347
349
  file_import.add_submodule_import("exceptions", error, ImportType.SDKCORE)
348
350
  if self.code_model.options["azure_arm"]:
@@ -555,6 +555,10 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
555
555
 
556
556
  for client in self.code_model.clients:
557
557
  for og in client.operation_groups:
558
+ if self.code_model.options["multiapi"] and any(
559
+ o.api_versions[0] != self.code_model.options["default_api_version"] for o in og.operations
560
+ ):
561
+ continue
558
562
  test_serializer = TestSerializer(self.code_model, env, client=client, operation_group=og)
559
563
  for is_async in (True, False):
560
564
  try:
@@ -58,6 +58,10 @@ OperationType = TypeVar(
58
58
  )
59
59
 
60
60
 
61
+ def _all_same(data: List[List[str]]) -> bool:
62
+ return len(data) > 1 and all(sorted(data[0]) == sorted(data[i]) for i in range(1, len(data)))
63
+
64
+
61
65
  def _json_serializable(content_type: str) -> bool:
62
66
  return bool(JSON_REGEXP.match(content_type.split(";")[0].strip().lower()))
63
67
 
@@ -410,14 +414,19 @@ class RequestBuilderSerializer(_BuilderBaseSerializer[RequestBuilderType]): # p
410
414
  return "response.json()"
411
415
 
412
416
  @staticmethod
413
- def declare_non_inputtable_constants(builder: RequestBuilderType) -> List[str]:
417
+ def declare_non_inputtable_headers_queries(builder: RequestBuilderType) -> List[str]:
414
418
  def _get_value(param):
419
+ declaration = param.get_declaration() if param.constant else None
415
420
  if param.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]:
416
421
  kwarg_dict = "headers" if param.location == ParameterLocation.HEADER else "params"
417
- return f"_{kwarg_dict}.pop('{param.wire_name}', {param.get_declaration()})"
418
- return f"{param.get_declaration()}"
422
+ return f"_{kwarg_dict}.pop('{param.wire_name}', {declaration})"
423
+ return declaration
419
424
 
420
- return [f"{p.client_name} = {_get_value(p)}" for p in builder.parameters.constant if not p.in_method_signature]
425
+ return [
426
+ f"{p.client_name} = {_get_value(p)}"
427
+ for p in (builder.parameters.headers + builder.parameters.query)
428
+ if not p.in_method_signature
429
+ ]
421
430
 
422
431
  @property
423
432
  def _function_def(self) -> str:
@@ -561,16 +570,22 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
561
570
  return retval
562
571
 
563
572
  def make_pipeline_call(self, builder: OperationType) -> List[str]:
573
+ retval = []
564
574
  type_ignore = self.async_mode and builder.group_name == "" # is in a mixin
565
- return [
566
- f"_stream = {builder.stream_value}",
567
- f"pipeline_response: PipelineResponse = {self._call_method}self._client.{self.pipeline_name}.run( "
568
- + f"{'# type: ignore' if type_ignore else ''} # pylint: disable=protected-access",
569
- " _request,",
570
- " stream=_stream,",
571
- " **kwargs",
572
- ")",
573
- ]
575
+ if builder.stream_value is True and not self.code_model.options["version_tolerant"]:
576
+ retval.append("_decompress = kwargs.pop('decompress', True)")
577
+ retval.extend(
578
+ [
579
+ f"_stream = {builder.stream_value}",
580
+ f"pipeline_response: PipelineResponse = {self._call_method}self._client.{self.pipeline_name}.run( "
581
+ + f"{'# type: ignore' if type_ignore else ''} # pylint: disable=protected-access",
582
+ " _request,",
583
+ " stream=_stream,",
584
+ " **kwargs",
585
+ ")",
586
+ ]
587
+ )
588
+ return retval
574
589
 
575
590
  @property
576
591
  def _function_def(self) -> str:
@@ -894,6 +909,9 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
894
909
  builder: OperationType,
895
910
  response: Response,
896
911
  ) -> List[str]:
912
+ return self.response_headers(response) + self.response_deserialization(builder, response)
913
+
914
+ def response_headers(self, response: Response) -> List[str]:
897
915
  retval: List[str] = [
898
916
  (
899
917
  f"response_headers['{response_header.wire_name}']=self._deserialize("
@@ -903,6 +921,14 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
903
921
  ]
904
922
  if response.headers:
905
923
  retval.append("")
924
+ return retval
925
+
926
+ def response_deserialization(
927
+ self,
928
+ builder: OperationType,
929
+ response: Response,
930
+ ) -> List[str]:
931
+ retval: List[str] = []
906
932
  deserialize_code: List[str] = []
907
933
  stream_logic = True
908
934
  if builder.has_stream_response:
@@ -913,7 +939,9 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
913
939
  if self.code_model.options["version_tolerant"]:
914
940
  deserialized = "response.iter_bytes()"
915
941
  else:
916
- deserialized = f"response.stream_download(self._client.{self.pipeline_name})"
942
+ deserialized = (
943
+ f"response.stream_download(self._client.{self.pipeline_name}, decompress=_decompress)"
944
+ )
917
945
  deserialize_code.append(f"deserialized = {deserialized}")
918
946
  elif response.type:
919
947
  pylint_disable = ""
@@ -961,11 +989,17 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
961
989
  def handle_error_response(self, builder: OperationType) -> List[str]:
962
990
  async_await = "await " if self.async_mode else ""
963
991
  retval = [f"if response.status_code not in {str(builder.success_status_codes)}:"]
964
- response_read = f" {async_await}response.read() # Load the body in memory and close the socket"
992
+ response_read = [
993
+ " try:",
994
+ f" {async_await}response.read() # Load the body in memory and close the socket",
995
+ " except (StreamConsumedError, StreamClosedError):",
996
+ " pass",
997
+ ]
965
998
  if builder.stream_value is True: # _stream is True so no need to judge it
966
- retval.append(response_read)
999
+ retval.extend(response_read)
967
1000
  elif isinstance(builder.stream_value, str): # _stream is not sure, so we need to judge it
968
- retval.extend([" if _stream:", f" {response_read}"])
1001
+ retval.append(" if _stream:")
1002
+ retval.extend([f" {l}" for l in response_read])
969
1003
  type_ignore = " # type: ignore" if _need_type_ignore(builder) else ""
970
1004
  retval.append(
971
1005
  f" map_error(status_code=response.status_code, response=response, error_map=error_map){type_ignore}"
@@ -999,13 +1033,34 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]): # pylint: di
999
1033
  retval.append("response_headers = {}")
1000
1034
  if builder.has_response_body or builder.any_response_has_headers:
1001
1035
  if len(builder.responses) > 1:
1036
+ status_codes, res_headers, res_deserialization = [], [], []
1002
1037
  for status_code in builder.success_status_codes:
1003
1038
  response = builder.get_response_from_status(status_code)
1004
1039
  if response.headers or response.type:
1040
+ status_codes.append(status_code)
1041
+ res_headers.append(self.response_headers(response))
1042
+ res_deserialization.append(self.response_deserialization(builder, response))
1043
+
1044
+ is_headers_same = _all_same(res_headers)
1045
+ is_deserialization_same = _all_same(res_deserialization)
1046
+ if is_deserialization_same:
1047
+ if is_headers_same:
1048
+ retval.extend(res_headers[0])
1049
+ retval.extend(res_deserialization[0])
1050
+ retval.append("")
1051
+ else:
1052
+ for status_code, headers in zip(status_codes, res_headers):
1053
+ if headers:
1054
+ retval.append(f"if response.status_code == {status_code}:")
1055
+ retval.extend([f" {line}" for line in headers])
1056
+ retval.append("")
1057
+ retval.extend(res_deserialization[0])
1058
+ retval.append("")
1059
+ else:
1060
+ for status_code, headers, deserialization in zip(status_codes, res_headers, res_deserialization):
1005
1061
  retval.append(f"if response.status_code == {status_code}:")
1006
- retval.extend(
1007
- [f" {line}" for line in self.response_headers_and_deserialization(builder, response)]
1008
- )
1062
+ retval.extend([f" {line}" for line in headers])
1063
+ retval.extend([f" {line}" for line in deserialization])
1009
1064
  retval.append("")
1010
1065
  else:
1011
1066
  retval.extend(self.response_headers_and_deserialization(builder, builder.responses[0]))
@@ -148,7 +148,7 @@ class ParameterSerializer:
148
148
  param.wire_name,
149
149
  ParameterSerializer.serialize_parameter(param, serializer_name),
150
150
  )
151
- if not param.optional:
151
+ if not param.optional and (param.in_method_signature or param.constant):
152
152
  retval = [set_parameter]
153
153
  else:
154
154
  retval = [
@@ -576,6 +576,7 @@ class Model(_MyMutableMapping):
576
576
  """
577
577
 
578
578
  result = {}
579
+ readonly_props = []
579
580
  if exclude_readonly:
580
581
  readonly_props = [p._rest_name for p in self._attr_to_rest_field.values() if _is_readonly(p)]
581
582
  for k, v in self.items():
@@ -8,8 +8,8 @@
8
8
  {% if request_builder_serializer.pop_kwargs_from_signature(request_builder) %}
9
9
  {{ op_tools.serialize(request_builder_serializer.pop_kwargs_from_signature(request_builder)) | indent }}
10
10
  {%- endif -%}
11
- {% if request_builder_serializer.declare_non_inputtable_constants(request_builder) %}
12
- {{ op_tools.serialize(request_builder_serializer.declare_non_inputtable_constants(request_builder)) | indent }}
11
+ {% if request_builder_serializer.declare_non_inputtable_headers_queries(request_builder) %}
12
+ {{ op_tools.serialize(request_builder_serializer.declare_non_inputtable_headers_queries(request_builder)) | indent }}
13
13
  {% endif %}
14
14
  # Construct URL
15
15
  {{ request_builder_serializer.construct_url(request_builder) }}
@@ -8,7 +8,7 @@
8
8
  import copy
9
9
  from typing import Callable, Dict, Any, List, Optional
10
10
 
11
- from ..utils import to_snake_case
11
+ from ..utils import to_snake_case, extract_original_name
12
12
  from .helpers import (
13
13
  add_redefined_builtin_info,
14
14
  pad_builtin_namespaces,
@@ -235,7 +235,8 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
235
235
  property["clientName"] = self.pad_reserved_words(property["clientName"].lower(), PadType.PROPERTY)
236
236
  add_redefined_builtin_info(property["clientName"], property)
237
237
  if type.get("name"):
238
- name = self.pad_reserved_words(type["name"], PadType.MODEL)
238
+ pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
239
+ name = self.pad_reserved_words(type["name"], pad_type)
239
240
  type["name"] = name[0].upper() + name[1:]
240
241
  type["description"] = update_description(type.get("description", ""), type["name"])
241
242
  type["snakeCaseName"] = to_snake_case(type["name"])
@@ -243,7 +244,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
243
244
  # we're enums
244
245
  values_to_add = []
245
246
  for value in type["values"]:
246
- padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM).upper()
247
+ padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE).upper()
247
248
  if self.version_tolerant:
248
249
  if padded_name[0] in "0123456789":
249
250
  padded_name = "ENUM_" + padded_name
@@ -362,7 +363,12 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
362
363
  yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP)
363
364
  yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
364
365
  yaml_data["name"] = yaml_data["name"].lower()
365
- yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
366
+ if yaml_data.get("isLroInitialOperation") is True:
367
+ yaml_data["name"] = (
368
+ "_" + self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD) + "_initial"
369
+ )
370
+ else:
371
+ yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
366
372
  yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
367
373
  yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
368
374
  body_parameter = yaml_data.get("bodyParameter")
@@ -53,9 +53,10 @@ basic_latin_chars = {
53
53
 
54
54
  class PadType(str, Enum):
55
55
  MODEL = "Model"
56
+ ENUM_CLASS = "Enum"
56
57
  METHOD = "_method"
57
58
  PARAMETER = "_parameter"
58
- ENUM = "_enum"
59
+ ENUM_VALUE = "_enum"
59
60
  PROPERTY = "_property"
60
61
  OPERATION_GROUP = "Operations"
61
62
 
@@ -175,9 +176,10 @@ RESERVED_WORDS = {
175
176
  "retry_on_status_codes",
176
177
  *_always_reserved,
177
178
  ],
178
- PadType.MODEL: [*_always_reserved],
179
+ PadType.MODEL: ["enum", *_always_reserved],
179
180
  PadType.PROPERTY: ["self", *_always_reserved],
180
- PadType.ENUM: ["mro", *_always_reserved],
181
+ PadType.ENUM_CLASS: ["enum", *_always_reserved],
182
+ PadType.ENUM_VALUE: ["mro", *_always_reserved],
181
183
  PadType.OPERATION_GROUP: [*_always_reserved],
182
184
  }
183
185
 
@@ -147,3 +147,7 @@ def build_policies(
147
147
  "self._config.logging_policy",
148
148
  ]
149
149
  return [p for p in policies if p]
150
+
151
+
152
+ def extract_original_name(name: str) -> str:
153
+ return name[1 : -len("_initial")]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.15.0",
3
+ "version": "6.17.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "devDependencies": {
26
26
  "@microsoft.azure/autorest.testserver": "^3.3.46",
27
27
  "typescript": "~5.1.3",
28
- "@azure-tools/typespec-python": "^0.25.0"
28
+ "@azure-tools/typespec-python": "^0.27.0"
29
29
  },
30
30
  "files": [
31
31
  "autorest/**/*.py",