@autorest/python 5.11.2 → 5.12.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.
Files changed (45) hide show
  1. package/ChangeLog.md +26 -0
  2. package/autorest/codegen/__init__.py +5 -5
  3. package/autorest/codegen/models/client.py +2 -2
  4. package/autorest/codegen/models/code_model.py +5 -1
  5. package/autorest/codegen/models/operation_group.py +5 -3
  6. package/autorest/codegen/models/parameter.py +4 -3
  7. package/autorest/codegen/models/parameter_list.py +30 -26
  8. package/autorest/codegen/models/request_builder.py +5 -1
  9. package/autorest/codegen/models/request_builder_parameter.py +4 -3
  10. package/autorest/codegen/models/request_builder_parameter_list.py +15 -6
  11. package/autorest/codegen/models/rest.py +3 -2
  12. package/autorest/codegen/serializers/__init__.py +48 -48
  13. package/autorest/codegen/serializers/builder_serializer.py +32 -27
  14. package/autorest/codegen/serializers/client_serializer.py +37 -9
  15. package/autorest/codegen/serializers/general_serializer.py +7 -5
  16. package/autorest/codegen/serializers/import_serializer.py +6 -6
  17. package/autorest/codegen/serializers/metadata_serializer.py +2 -2
  18. package/autorest/codegen/serializers/model_base_serializer.py +3 -3
  19. package/autorest/codegen/serializers/model_generic_serializer.py +1 -1
  20. package/autorest/codegen/serializers/model_python3_serializer.py +1 -1
  21. package/autorest/codegen/serializers/{operation_group_serializer.py → operation_groups_serializer.py} +27 -29
  22. package/autorest/codegen/serializers/operations_init_serializer.py +34 -2
  23. package/autorest/codegen/serializers/patch_serializer.py +15 -0
  24. package/autorest/codegen/serializers/rest_serializer.py +9 -4
  25. package/autorest/codegen/serializers/utils.py +2 -2
  26. package/autorest/codegen/templates/config.py.jinja2 +1 -11
  27. package/autorest/codegen/templates/init.py.jinja2 +4 -5
  28. package/autorest/codegen/templates/metadata.json.jinja2 +2 -2
  29. package/autorest/codegen/templates/model_init.py.jinja2 +1 -1
  30. package/autorest/codegen/templates/{operations_class.py.jinja2 → operation_group.py.jinja2} +2 -0
  31. package/autorest/codegen/templates/{operations_container.py.jinja2 → operation_groups_container.py.jinja2} +7 -21
  32. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +13 -0
  33. package/autorest/codegen/templates/patch.py.jinja2 +31 -0
  34. package/autorest/codegen/templates/request_builder.py.jinja2 +7 -2
  35. package/autorest/codegen/templates/request_builders.py.jinja2 +1 -1
  36. package/autorest/codegen/templates/setup.py.jinja2 +1 -1
  37. package/autorest/multiapi/serializers/__init__.py +3 -3
  38. package/autorest/multiapi/serializers/import_serializer.py +5 -5
  39. package/autorest/namer/name_converter.py +1 -0
  40. package/package.json +2 -2
  41. package/setup.py +1 -0
  42. package/autorest/codegen/templates/operations_class_mixin.py.jinja2 +0 -16
  43. package/autorest/codegen/templates/operations_container_init.py.jinja2 +0 -24
  44. package/autorest/codegen/templates/operations_container_mixin.py.jinja2 +0 -21
  45. package/autorest/codegen/templates/operations_init.py.jinja2 +0 -26
package/ChangeLog.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Change Log
2
2
 
3
+ ### 2021-12-06 - 5.12.0
4
+
5
+ | Library | Min Version
6
+ | --------------- | -------
7
+ |`@autorest/core` | `3.6.2`
8
+ |`@autorest/modelerfour` | `4.19.1`
9
+ |`azure-core` dep of generated code | `1.20.1`
10
+ |`msrest` dep of generated code | `0.6.21`
11
+ |`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.0`
12
+
13
+ **Breaking Changes in Version Tolerant Generation**
14
+
15
+ - Remove metadata property for version tolerant and low level client generations #1090
16
+ - Generate SDKs with `--python3-only` defaulting to `True` for version tolerant and low level client #1087
17
+
18
+ **New Features**
19
+
20
+ - Generate a `_patch.py` file if one does not exist. These files are used to customize the generated code #1092
21
+
22
+ **Bug Fixes**
23
+
24
+ - Can now handle body params with names `json`, `content`, `data`, and `files` #1081
25
+ - Improve generated templates for `data` and `files` input body params by adding quotes around the keys #1082
26
+ - Using flag `--python3-only` will get you typed sync client and config files #1085
27
+ - Pin `mistune` dependency to less than `2.x.x` so autorest can be successfully installed #1106
28
+
3
29
  ### 2021-11-05 - 5.11.2
4
30
 
5
31
  | Library | Min Version
@@ -60,7 +60,7 @@ def _validate_code_model_options(options: Dict[str, Any]) -> None:
60
60
  "to 'public' or 'hidden'."
61
61
  )
62
62
 
63
- if not options["show_operations"] and options["add_python_3_operation_files"]:
63
+ if not options["show_operations"] and options["add_python3_operation_files"]:
64
64
  raise ValueError(
65
65
  "Can not add typed sync operation files if you are not showing operations. "
66
66
  "If you want typed synced operation files, you have to add flag "
@@ -256,7 +256,7 @@ class CodeGenerator(Plugin):
256
256
  version_tolerant = self._autorestapi.get_boolean_value("version-tolerant", False)
257
257
  show_operations = self._autorestapi.get_boolean_value("show-operations", not low_level_client)
258
258
  models_mode_default = "none" if low_level_client or version_tolerant else "msrest"
259
- python_3_only = self._autorestapi.get_boolean_value("python3-only", False)
259
+ python3_only = self._autorestapi.get_boolean_value("python3-only", low_level_client or version_tolerant)
260
260
 
261
261
  options: Dict[str, Any] = {
262
262
  "azure_arm": azure_arm,
@@ -282,13 +282,13 @@ class CodeGenerator(Plugin):
282
282
  "only_path_and_body_params_positional": self._autorestapi.get_boolean_value(
283
283
  "only-path-and-body-params-positional", low_level_client or version_tolerant
284
284
  ),
285
- "add_python_3_operation_files": self._autorestapi.get_boolean_value(
286
- "add-python3-operation-files", python_3_only
285
+ "add_python3_operation_files": self._autorestapi.get_boolean_value(
286
+ "add-python3-operation-files", python3_only and not low_level_client
287
287
  ),
288
288
  "version_tolerant": version_tolerant,
289
289
  "low_level_client": low_level_client,
290
290
  "combine_operation_files": self._autorestapi.get_boolean_value("combine-operation-files", version_tolerant),
291
- "python_3_only": python_3_only,
291
+ "python3_only": python3_only,
292
292
  }
293
293
 
294
294
  if options["builders_visibility"] is None:
@@ -96,6 +96,6 @@ class Client:
96
96
  pass
97
97
  return file_import
98
98
 
99
- def send_request_signature(self, async_mode) -> List[str]:
99
+ def send_request_signature(self, async_mode: bool, is_python3_file: bool) -> List[str]:
100
100
  request_signature = ["request: HttpRequest," if async_mode else "request, # type: HttpRequest"]
101
- return request_signature + self.parameters.method_signature_kwargs(async_mode)
101
+ return request_signature + self.parameters.method_signature_kwargs(is_python3_file)
@@ -29,7 +29,7 @@ from .rest import Rest
29
29
 
30
30
  _LOGGER = logging.getLogger(__name__)
31
31
 
32
- class CodeModel: # pylint: disable=too-many-instance-attributes
32
+ class CodeModel: # pylint: disable=too-many-instance-attributes, too-many-public-methods
33
33
  """Holds all of the information we have parsed out of the yaml file. The CodeModel is what gets
34
34
  serialized by the serializers.
35
35
 
@@ -203,6 +203,10 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
203
203
  operation for operation in operation_group.operations if operation not in next_operations
204
204
  ]
205
205
 
206
+ @property
207
+ def has_schemas(self):
208
+ return self.schemas or self.enums
209
+
206
210
  @property
207
211
  def default_authentication_policy(self) -> Type[CredentialSchemaPolicy]:
208
212
  return ARMChallengeAuthenticationPolicy if self.options['azure_arm'] else BearerTokenCredentialPolicy
@@ -56,7 +56,7 @@ class OperationGroup(BaseModel):
56
56
  file_import.merge(operation.imports_for_multiapi(async_mode))
57
57
  return file_import
58
58
 
59
- def imports(self, async_mode: bool, has_schemas: bool) -> FileImport:
59
+ def imports(self, async_mode: bool) -> FileImport:
60
60
  file_import = FileImport()
61
61
  file_import.add_from_import("azure.core.exceptions", "ClientAuthenticationError", ImportType.AZURECORE)
62
62
  file_import.add_from_import("azure.core.exceptions", "ResourceNotFoundError", ImportType.AZURECORE)
@@ -73,7 +73,7 @@ class OperationGroup(BaseModel):
73
73
  "azure.core.tracing.decorator", "distributed_trace", ImportType.AZURECORE,
74
74
  )
75
75
  local_path = "..." if async_mode else ".."
76
- if has_schemas and self.code_model.options["models_mode"]:
76
+ if self.code_model.has_schemas and self.code_model.options["models_mode"]:
77
77
  file_import.add_from_import(local_path, "models", ImportType.LOCAL, alias="_models")
78
78
  if self.code_model.options["builders_visibility"] == "embedded" and async_mode:
79
79
  if not self.code_model.options["combine_operation_files"]:
@@ -85,7 +85,9 @@ class OperationGroup(BaseModel):
85
85
  else:
86
86
  operation_group_builders = self.code_model.rest.request_builders
87
87
  for request_builder in operation_group_builders:
88
- suffix = "_py3" if self.code_model.options["python_3_only"] else ""
88
+ python3_only = self.code_model.options["python3_only"]
89
+ typed_sync_operation_file = self.code_model.options["add_python3_operation_files"]
90
+ suffix = "_py3" if typed_sync_operation_file and not python3_only else ""
89
91
  file_import.add_from_import(
90
92
  f"...{self.code_model.operations_folder_name}.{self.filename}{suffix}",
91
93
  request_builder.name,
@@ -97,6 +97,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
97
97
  self.is_data_input = yaml_data.get("isPartialBody", False) and not self.is_multipart
98
98
  self.content_types = content_types or []
99
99
  self.body_kwargs: List[Parameter] = []
100
+ self.is_body_kwarg = False
100
101
 
101
102
  def __hash__(self) -> int:
102
103
  return hash(self.serialized_name)
@@ -167,7 +168,7 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
167
168
  @property
168
169
  def pre_semicolon_content_types(self) -> List[str]:
169
170
  """Splits on semicolon of media types and returns the first half.
170
- I.e. ["text/plain; encoding=UTF-8"] -> ["text/plain"]
171
+ I.e. ["text/plain; charset=UTF-8"] -> ["text/plain"]
171
172
  """
172
173
  return [content_type.split(";")[0] for content_type in self.content_types]
173
174
 
@@ -282,8 +283,8 @@ class Parameter(BaseModel): # pylint: disable=too-many-instance-attributes, too
282
283
  def has_default_value(self):
283
284
  return self.default_value is not None or not self.required
284
285
 
285
- def method_signature(self, is_python_3_file: bool) -> str:
286
- if is_python_3_file:
286
+ def method_signature(self, is_python3_file: bool) -> str:
287
+ if is_python3_file:
287
288
  if self.has_default_value:
288
289
  return f"{self.serialized_name}: {self.type_annotation} = {self.default_value_declaration},"
289
290
  return f"{self.serialized_name}: {self.type_annotation},"
@@ -172,11 +172,15 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
172
172
 
173
173
  @property
174
174
  def multipart(self) -> List[Parameter]:
175
- return self.get_from_predicate(lambda parameter: parameter.is_multipart)
175
+ return self.get_from_predicate(
176
+ lambda parameter: parameter.is_multipart and not parameter.is_body_kwarg
177
+ )
176
178
 
177
179
  @property
178
180
  def data_inputs(self) -> List[Parameter]:
179
- return self.get_from_predicate(lambda parameter: parameter.is_data_input)
181
+ return self.get_from_predicate(
182
+ lambda parameter: parameter.is_data_input and not parameter.is_body_kwarg
183
+ )
180
184
 
181
185
  def _filter_out_multiple_content_type(self, kwarg_params):
182
186
  """We don't want multiple content type kwargs in the method signature"""
@@ -214,24 +218,24 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
214
218
  return signature_parameters
215
219
 
216
220
 
217
- def method_signature(self, is_python_3_file: bool) -> List[str]:
221
+ def method_signature(self, is_python3_file: bool) -> List[str]:
218
222
  return _method_signature_helper(
219
- positional=self.method_signature_positional(is_python_3_file),
220
- keyword_only=self.method_signature_keyword_only(is_python_3_file),
221
- kwarg_params=self.method_signature_kwargs(is_python_3_file)
223
+ positional=self.method_signature_positional(is_python3_file),
224
+ keyword_only=self.method_signature_keyword_only(is_python3_file),
225
+ kwarg_params=self.method_signature_kwargs(is_python3_file)
222
226
  )
223
227
 
224
- def method_signature_positional(self, is_python_3_file: bool) -> List[str]:
225
- return [parameter.method_signature(is_python_3_file) for parameter in self.positional]
228
+ def method_signature_positional(self, is_python3_file: bool) -> List[str]:
229
+ return [parameter.method_signature(is_python3_file) for parameter in self.positional]
226
230
 
227
- def method_signature_keyword_only(self, is_python_3_file: bool) -> List[str]:
228
- if not (self.keyword_only and is_python_3_file):
231
+ def method_signature_keyword_only(self, is_python3_file: bool) -> List[str]:
232
+ if not (self.keyword_only and is_python3_file):
229
233
  return []
230
- return ["*,"] + [parameter.method_signature(is_python_3_file) for parameter in self.keyword_only]
234
+ return ["*,"] + [parameter.method_signature(is_python3_file) for parameter in self.keyword_only]
231
235
 
232
236
  @staticmethod
233
- def method_signature_kwargs(is_python_3_file: bool) -> List[str]:
234
- return ["**kwargs: Any"] if is_python_3_file else ["**kwargs # type: Any"]
237
+ def method_signature_kwargs(is_python3_file: bool) -> List[str]:
238
+ return ["**kwargs: Any"] if is_python3_file else ["**kwargs # type: Any"]
235
239
 
236
240
  @property
237
241
  def positional(self) -> List[Parameter]:
@@ -245,9 +249,9 @@ class ParameterList(MutableSequence): # pylint: disable=too-many-public-methods
245
249
  def kwargs(self) -> List[Parameter]:
246
250
  return [p for p in self.method if p.is_kwarg]
247
251
 
248
- def kwargs_to_pop(self, is_python_3_file: bool) -> List[Parameter]:
252
+ def kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
249
253
  kwargs_to_pop = self.kwargs
250
- if not is_python_3_file:
254
+ if not is_python3_file:
251
255
  kwargs_to_pop += self.keyword_only
252
256
  return kwargs_to_pop
253
257
 
@@ -433,41 +437,41 @@ class GlobalParameterList(ParameterList):
433
437
  return True
434
438
  return serialized_name != self.host_variable_name
435
439
 
436
- def kwargs_to_pop(self, is_python_3_file: bool) -> List[Parameter]:
440
+ def kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
437
441
  return [
438
- k for k in super().kwargs_to_pop(is_python_3_file)
442
+ k for k in super().kwargs_to_pop(is_python3_file)
439
443
  if not self._param_is_in_config_method(k.serialized_name)
440
444
  ]
441
445
 
442
- def config_kwargs_to_pop(self, is_python_3_file: bool) -> List[Parameter]:
443
- current_kwargs_to_pop = super().kwargs_to_pop(is_python_3_file)
446
+ def config_kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
447
+ current_kwargs_to_pop = super().kwargs_to_pop(is_python3_file)
444
448
  return [k for k in current_kwargs_to_pop if self._param_is_in_config_method(k.serialized_name)]
445
449
 
446
450
  @property
447
451
  def config_method(self) -> List[Parameter]:
448
452
  return [p for p in self.method if self._param_is_in_config_method(p.serialized_name)]
449
453
 
450
- def client_method_signature(self, is_python_3_file: bool) -> List[str]:
451
- return self.method_signature(is_python_3_file)
454
+ def client_method_signature(self, is_python3_file: bool) -> List[str]:
455
+ return self.method_signature(is_python3_file)
452
456
 
453
- def config_method_signature(self, is_python_3_file: bool) -> List[str]:
457
+ def config_method_signature(self, is_python3_file: bool) -> List[str]:
454
458
 
455
459
  positional = [
456
- p.method_signature(is_python_3_file)
460
+ p.method_signature(is_python3_file)
457
461
  for p in self.positional
458
462
  if self._param_is_in_config_method(p.serialized_name)
459
463
  ]
460
464
  keyword_only_params = [p for p in self.keyword_only if self._param_is_in_config_method(p.serialized_name)]
461
465
  keyword_only_method_signature = []
462
- if is_python_3_file:
466
+ if is_python3_file:
463
467
  keyword_only_method_signature = (
464
468
  ["*,"] +
465
469
  [
466
- p.method_signature(is_python_3_file) for p in keyword_only_params
470
+ p.method_signature(is_python3_file) for p in keyword_only_params
467
471
  ]
468
472
  ) if keyword_only_params else []
469
473
  return _method_signature_helper(
470
474
  positional=positional,
471
475
  keyword_only=keyword_only_method_signature,
472
- kwarg_params=self.method_signature_kwargs(is_python_3_file)
476
+ kwarg_params=self.method_signature_kwargs(is_python3_file)
473
477
  )
@@ -80,13 +80,17 @@ class RequestBuilder(BaseBuilder):
80
80
  file_import.add_from_import(
81
81
  f"{relative_path}_vendor", "_format_url_section", ImportType.LOCAL
82
82
  )
83
+ if self.parameters.headers or self.parameters.query:
84
+ file_import.add_from_import(
85
+ "typing", "Dict", ImportType.STDLIB, typing_section=TypingSection.CONDITIONAL
86
+ )
83
87
  file_import.add_from_import(
84
88
  "typing", "Any", ImportType.STDLIB, typing_section=TypingSection.CONDITIONAL
85
89
  )
86
90
  file_import.add_from_import("msrest", "Serializer", ImportType.AZURECORE)
87
91
  if self.parameters.has_body and (
88
92
  self.code_model.options["builders_visibility"] != "embedded" or
89
- self.code_model.options["add_python_3_operation_files"]
93
+ self.code_model.options["add_python3_operation_files"]
90
94
  ):
91
95
  file_import.define_mypy_type("JSONType", "Any")
92
96
  return file_import
@@ -29,12 +29,13 @@ class RequestBuilderParameter(ParameterOnlyPathAndBodyPositional):
29
29
 
30
30
  @property
31
31
  def name_in_high_level_operation(self) -> str:
32
+ template = "{}" if self.code_model.options["version_tolerant"] else "_{}"
32
33
  if self.is_multipart:
33
- return "files"
34
+ return template.format("files")
34
35
  if self.is_data_input:
35
- return "data"
36
+ return template.format("data")
36
37
  if self.is_body and not self.constant:
37
- return self.serialized_name
38
+ return f"_{self.serialized_name}"
38
39
  name = self.yaml_data["language"]["python"]["name"]
39
40
  if self.implementation == "Client" and self.in_method_code:
40
41
  # for these, we're passing the client params to the request builder.
@@ -44,6 +44,7 @@ class RequestBuilderParameterList(ParameterList):
44
44
  def _change_body_param_name(self, parameter: Parameter, name: str) -> None:
45
45
  self.body_kwarg_names[name] = None
46
46
  parameter.serialized_name = name
47
+ parameter.is_body_kwarg = True
47
48
 
48
49
  def _is_json(self, body_method_param: Parameter) -> bool:
49
50
  if 'json' in body_method_param.serialization_formats:
@@ -97,7 +98,6 @@ class RequestBuilderParameterList(ParameterList):
97
98
  "Multipart input for files. See the template in our example to find the input shape. " +
98
99
  file_kwarg.description
99
100
  )
100
- file_kwarg.is_multipart = False
101
101
  file_kwarg.content_types = [
102
102
  c for c in content_types_to_assign
103
103
  if c == "multipart/form-data"
@@ -119,7 +119,6 @@ class RequestBuilderParameterList(ParameterList):
119
119
  "Pass in dictionary that contains form data to include in the body of the request. " +
120
120
  data_kwarg.description
121
121
  )
122
- data_kwarg.is_data_input = False
123
122
  data_kwarg.content_types = [
124
123
  c for c in content_types_to_assign
125
124
  if c == "application/x-www-form-urlencoded"
@@ -208,7 +207,7 @@ class RequestBuilderParameterList(ParameterList):
208
207
  if not self._json_body:
209
208
  try:
210
209
  json_param = next(
211
- b for b in self.body if b.serialized_name not in _REQUEST_BUILDER_BODY_NAMES and
210
+ b for b in self.body if not b.is_body_kwarg and
212
211
  b.is_json_parameter
213
212
  )
214
213
  self._json_body = json_param.schema
@@ -217,10 +216,10 @@ class RequestBuilderParameterList(ParameterList):
217
216
  raise ValueError("There is no JSON body in these parameters")
218
217
  return self._json_body
219
218
 
220
- def kwargs_to_pop(self, is_python_3_file: bool) -> List[Parameter]:
219
+ def kwargs_to_pop(self, is_python3_file: bool) -> List[Parameter]:
221
220
  # we don't want to pop the body kwargs in py2.7. We send them straight to HttpRequest
222
221
  kwargs_to_pop = self.kwargs
223
- if not is_python_3_file:
222
+ if not is_python3_file:
224
223
  kwargs_to_pop += [k for k in self.keyword_only if not (k.is_body and not k.constant)]
225
224
  return kwargs_to_pop
226
225
 
@@ -240,9 +239,19 @@ class RequestBuilderParameterList(ParameterList):
240
239
  seen_content_type = False
241
240
 
242
241
  for parameter in parameters:
242
+
243
+ if (
244
+ parameter.location == ParameterLocation.Body and
245
+ (parameter.is_data_input or parameter.is_multipart) and
246
+ not parameter.is_body_kwarg
247
+ ):
248
+ # if i am a part of files or data, and i'm not the files or
249
+ # data kwarg, ignore me
250
+ continue
243
251
  if (
244
252
  parameter.location == ParameterLocation.Body and
245
- parameter.serialized_name not in _REQUEST_BUILDER_BODY_NAMES
253
+ not parameter.is_body_kwarg and
254
+ not parameter.constant
246
255
  ):
247
256
  # we keep the original body param from the swagger for documentation purposes
248
257
  # we don't want it in the method signature
@@ -19,10 +19,11 @@ class Rest(BaseModel):
19
19
  super(Rest, self). __init__(yaml_data=yaml_data)
20
20
  self.request_builders = request_builders
21
21
 
22
- def imports(self) -> FileImport:
22
+ def imports(self, builder_group_name: str) -> FileImport:
23
23
  file_import = FileImport()
24
24
  for request_builder in self.request_builders:
25
- file_import.merge(request_builder.imports())
25
+ if request_builder.builder_group_name == builder_group_name:
26
+ file_import.merge(request_builder.imports())
26
27
  return file_import
27
28
 
28
29
  @classmethod
@@ -3,29 +3,32 @@
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
6
+ from typing import List, Optional
7
7
  from pathlib import Path
8
8
  from jinja2 import PackageLoader, Environment
9
9
  from autorest.codegen.models.operation_group import OperationGroup
10
10
 
11
11
  from ...jsonrpc import AutorestAPI
12
- from ..models import CodeModel
13
- from ..models.request_builder import RequestBuilder
12
+ from ..models import (
13
+ CodeModel,
14
+ OperationGroup,
15
+ RequestBuilder,
16
+ )
14
17
  from .enum_serializer import EnumSerializer
15
18
  from .general_serializer import GeneralSerializer
16
19
  from .model_generic_serializer import ModelGenericSerializer
17
20
  from .model_init_serializer import ModelInitSerializer
18
21
  from .model_python3_serializer import ModelPython3Serializer
19
22
  from .operations_init_serializer import OperationsInitSerializer
20
- from .operation_group_serializer import OperationGroupSerializer
23
+ from .operation_groups_serializer import OperationGroupsSerializer
21
24
  from .metadata_serializer import MetadataSerializer
22
25
  from .rest_serializer import RestPython3Serializer, RestGenericSerializer, RestSerializer
26
+ from .patch_serializer import PatchSerializer
23
27
 
24
28
  __all__ = [
25
29
  "JinjaSerializer",
26
30
  ]
27
31
 
28
-
29
32
  class JinjaSerializer:
30
33
  def __init__(self, autorestapi: AutorestAPI) -> None:
31
34
  self._autorestapi = autorestapi
@@ -45,8 +48,8 @@ class JinjaSerializer:
45
48
  )
46
49
 
47
50
  # if there was a patch file before, we keep it
48
- self._keep_patch_file(namespace_path / Path("_patch.py"))
49
- self._keep_patch_file(namespace_path / Path("aio") / Path("_patch.py"))
51
+ self._keep_patch_file(namespace_path / Path("_patch.py"), env)
52
+ self._keep_patch_file(namespace_path / Path("aio") / Path("_patch.py"), env)
50
53
 
51
54
  self._serialize_and_write_top_level_folder(code_model=code_model, env=env, namespace_path=namespace_path)
52
55
 
@@ -70,16 +73,18 @@ class JinjaSerializer:
70
73
 
71
74
 
72
75
 
73
- def _keep_patch_file(self, path_file: Path):
76
+ def _keep_patch_file(self, path_file: Path, env: Environment):
74
77
  if self._autorestapi.read_file(path_file):
75
78
  self._autorestapi.write_file(path_file, self._autorestapi.read_file(path_file))
79
+ else:
80
+ self._autorestapi.write_file(path_file, PatchSerializer(env=env).serialize())
76
81
 
77
82
 
78
83
  def _serialize_and_write_models_folder(self, code_model: CodeModel, env: Environment, namespace_path: Path) -> None:
79
84
  # Write the models folder
80
85
  models_path = namespace_path / Path("models")
81
86
  if code_model.schemas:
82
- if not code_model.options['python_3_only']:
87
+ if not code_model.options['python3_only']:
83
88
  self._autorestapi.write_file(
84
89
  models_path / Path("_models.py"), ModelGenericSerializer(code_model=code_model, env=env).serialize()
85
90
  )
@@ -144,36 +149,49 @@ class JinjaSerializer:
144
149
  ).serialize_init()
145
150
  )
146
151
 
147
- def _serialize_and_write_operations_folder_process(
152
+ def _serialize_and_write_operations_file(
148
153
  self,
149
154
  code_model: CodeModel,
150
155
  env: Environment,
151
156
  namespace_path: Path,
152
- operation_groups: List[OperationGroup],
153
- filename: str
157
+ operation_group: Optional[OperationGroup] = None
154
158
  ) -> None:
155
- # write sync operation group and operation files
156
- if not code_model.options['python_3_only'] and not code_model.options["add_python_3_operation_files"]:
157
- operation_group_serializer = OperationGroupSerializer(
159
+ filename = operation_group.filename if operation_group else "_operations"
160
+ # write first sync file
161
+ operation_group_serializer = OperationGroupsSerializer(
162
+ code_model=code_model,
163
+ env=env,
164
+ async_mode=False,
165
+ is_python3_file=code_model.options['python3_only'],
166
+ operation_group=operation_group
167
+ )
168
+ self._autorestapi.write_file(
169
+ namespace_path / Path(code_model.operations_folder_name) / Path(f"{filename}.py"),
170
+ operation_group_serializer.serialize(),
171
+ )
172
+
173
+ if not code_model.options['python3_only'] and code_model.options["add_python3_operation_files"]:
174
+ # write typed second file if not python 3 only
175
+ operation_group_serializer = OperationGroupsSerializer(
158
176
  code_model=code_model,
159
177
  env=env,
160
- operation_groups=operation_groups,
161
178
  async_mode=False,
162
- is_python_3_file=False,
179
+ is_python3_file=True,
180
+
163
181
  )
164
182
  self._autorestapi.write_file(
165
- namespace_path / Path(code_model.operations_folder_name) / Path(f"{filename}.py"),
183
+ namespace_path / Path(code_model.operations_folder_name) / Path(f"{filename}_py3.py"),
166
184
  operation_group_serializer.serialize(),
167
185
  )
168
186
 
169
187
  if not code_model.options["no_async"]:
170
188
  # write async operation group and operation files
171
- operation_group_async_serializer = OperationGroupSerializer(
189
+ operation_group_async_serializer = OperationGroupsSerializer(
172
190
  code_model=code_model,
173
191
  env=env,
174
- operation_groups=operation_groups,
175
192
  async_mode=True,
176
- is_python_3_file=True,
193
+ is_python3_file=True,
194
+ operation_group=operation_group
177
195
  )
178
196
  self._autorestapi.write_file(
179
197
  (
@@ -185,20 +203,6 @@ class JinjaSerializer:
185
203
  operation_group_async_serializer.serialize(),
186
204
  )
187
205
 
188
- if code_model.options["add_python_3_operation_files"]:
189
- # write typed sync operation files
190
- operation_group_serializer = OperationGroupSerializer(
191
- code_model=code_model,
192
- env=env,
193
- operation_groups=operation_groups,
194
- async_mode=False,
195
- is_python_3_file=True,
196
- )
197
- self._autorestapi.write_file(
198
- namespace_path / Path(code_model.operations_folder_name) / Path(f"{filename}_py3.py"),
199
- operation_group_serializer.serialize(),
200
- )
201
-
202
206
  def _serialize_and_write_operations_folder(
203
207
  self, code_model: CodeModel, env: Environment, namespace_path: Path
204
208
  ) -> None:
@@ -217,24 +221,20 @@ class JinjaSerializer:
217
221
  operations_async_init_serializer.serialize(),
218
222
  )
219
223
 
220
- if not code_model.options["combine_operation_files"]:
224
+ if code_model.options["combine_operation_files"]:
225
+ self._serialize_and_write_operations_file(
226
+ code_model=code_model,
227
+ env=env,
228
+ namespace_path=namespace_path,
229
+ )
230
+ else:
221
231
  for operation_group in code_model.operation_groups:
222
- self._serialize_and_write_operations_folder_process(
232
+ self._serialize_and_write_operations_file(
223
233
  code_model=code_model,
224
234
  env=env,
225
235
  namespace_path=namespace_path,
226
- operation_groups=[operation_group],
227
- filename=operation_group.filename
236
+ operation_group=operation_group,
228
237
  )
229
- else:
230
- self._serialize_and_write_operations_folder_process(
231
- code_model=code_model,
232
- env=env,
233
- namespace_path=namespace_path,
234
- operation_groups=code_model.operation_groups,
235
- filename="_operations"
236
- )
237
-
238
238
 
239
239
  def _serialize_and_write_version_file(
240
240
  self, code_model: CodeModel, namespace_path: Path, general_serializer: GeneralSerializer