@autorest/python 6.2.11 → 6.2.15

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 (56) hide show
  1. package/autorest/__init__.py +7 -5
  2. package/autorest/_utils.py +7 -1
  3. package/autorest/black/__init__.py +6 -1
  4. package/autorest/codegen/__init__.py +1 -1
  5. package/autorest/codegen/models/__init__.py +8 -2
  6. package/autorest/codegen/models/base.py +4 -13
  7. package/autorest/codegen/models/client.py +9 -11
  8. package/autorest/codegen/models/code_model.py +3 -3
  9. package/autorest/codegen/models/combined_type.py +4 -3
  10. package/autorest/codegen/models/credential_types.py +7 -14
  11. package/autorest/codegen/models/dictionary_type.py +1 -1
  12. package/autorest/codegen/models/imports.py +3 -3
  13. package/autorest/codegen/models/lro_operation.py +5 -5
  14. package/autorest/codegen/models/model_type.py +89 -47
  15. package/autorest/codegen/models/operation.py +8 -8
  16. package/autorest/codegen/models/operation_group.py +3 -1
  17. package/autorest/codegen/models/paging_operation.py +2 -2
  18. package/autorest/codegen/models/parameter.py +27 -6
  19. package/autorest/codegen/models/parameter_list.py +1 -9
  20. package/autorest/codegen/models/primitive_types.py +1 -1
  21. package/autorest/codegen/models/property.py +15 -3
  22. package/autorest/codegen/models/response.py +2 -2
  23. package/autorest/codegen/serializers/__init__.py +2 -2
  24. package/autorest/codegen/serializers/builder_serializer.py +64 -28
  25. package/autorest/codegen/serializers/client_serializer.py +6 -4
  26. package/autorest/codegen/serializers/general_serializer.py +7 -2
  27. package/autorest/codegen/serializers/model_serializer.py +14 -4
  28. package/autorest/codegen/serializers/sample_serializer.py +2 -6
  29. package/autorest/codegen/templates/config.py.jinja2 +25 -6
  30. package/autorest/codegen/templates/enum.py.jinja2 +2 -2
  31. package/autorest/codegen/templates/metadata.json.jinja2 +18 -9
  32. package/autorest/codegen/templates/model_base.py.jinja2 +74 -63
  33. package/autorest/codegen/templates/model_container.py.jinja2 +2 -2
  34. package/autorest/codegen/templates/model_dpg.py.jinja2 +6 -4
  35. package/autorest/codegen/templates/model_msrest.py.jinja2 +2 -2
  36. package/autorest/codegen/templates/serialization.py.jinja2 +57 -29
  37. package/autorest/codegen/templates/vendor.py.jinja2 +3 -2
  38. package/autorest/jsonrpc/localapi.py +3 -3
  39. package/autorest/jsonrpc/server.py +3 -3
  40. package/autorest/m2r/__init__.py +1 -1
  41. package/autorest/m4reformatter/__init__.py +13 -2
  42. package/autorest/multiapi/models/__init__.py +2 -0
  43. package/autorest/multiapi/models/code_model.py +13 -0
  44. package/autorest/multiapi/models/global_parameter.py +1 -0
  45. package/autorest/multiapi/models/imports.py +3 -3
  46. package/autorest/multiapi/serializers/__init__.py +30 -3
  47. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +8 -12
  48. package/autorest/postprocess/get_all.py +3 -1
  49. package/autorest/postprocess/venvtools.py +5 -4
  50. package/autorest/preprocess/__init__.py +19 -7
  51. package/autorest/preprocess/python_mappings.py +1 -0
  52. package/index.js +0 -0
  53. package/package.json +2 -1
  54. package/requirements.txt +6 -6
  55. package/setup.py +0 -1
  56. package/venvtools.py +2 -3
@@ -38,7 +38,22 @@ import logging
38
38
  import re
39
39
  import sys
40
40
  import codecs
41
- from typing import Optional, Union, AnyStr, IO, Mapping
41
+ from typing import (
42
+ Dict,
43
+ Any,
44
+ cast,
45
+ Optional,
46
+ Union,
47
+ AnyStr,
48
+ IO,
49
+ Mapping,
50
+ Callable,
51
+ TypeVar,
52
+ MutableMapping,
53
+ Type,
54
+ List,
55
+ Mapping,
56
+ )
42
57
 
43
58
  try:
44
59
  from urllib import quote # type: ignore
@@ -48,12 +63,13 @@ import xml.etree.ElementTree as ET
48
63
 
49
64
  import isodate # type: ignore
50
65
 
51
- from typing import Dict, Any, cast
52
-
53
66
  from azure.core.exceptions import DeserializationError, SerializationError, raise_with_traceback
54
67
 
55
68
  _BOM = codecs.BOM_UTF8.decode(encoding="utf-8")
56
69
 
70
+ ModelType = TypeVar("ModelType", bound="Model")
71
+ JSON = MutableMapping[str, Any]
72
+
57
73
 
58
74
  class RawDeserializer:
59
75
 
@@ -277,8 +293,8 @@ class Model(object):
277
293
  _attribute_map: Dict[str, Dict[str, Any]] = {}
278
294
  _validation: Dict[str, Dict[str, Any]] = {}
279
295
 
280
- def __init__(self, **kwargs):
281
- self.additional_properties = {}
296
+ def __init__(self, **kwargs: Any) -> None:
297
+ self.additional_properties: Dict[str, Any] = {}
282
298
  for k in kwargs:
283
299
  if k not in self._attribute_map:
284
300
  _LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__)
@@ -287,25 +303,25 @@ class Model(object):
287
303
  else:
288
304
  setattr(self, k, kwargs[k])
289
305
 
290
- def __eq__(self, other):
306
+ def __eq__(self, other: Any) -> bool:
291
307
  """Compare objects by comparing all attributes."""
292
308
  if isinstance(other, self.__class__):
293
309
  return self.__dict__ == other.__dict__
294
310
  return False
295
311
 
296
- def __ne__(self, other):
312
+ def __ne__(self, other: Any) -> bool:
297
313
  """Compare objects by comparing all attributes."""
298
314
  return not self.__eq__(other)
299
315
 
300
- def __str__(self):
316
+ def __str__(self) -> str:
301
317
  return str(self.__dict__)
302
318
 
303
319
  @classmethod
304
- def enable_additional_properties_sending(cls):
320
+ def enable_additional_properties_sending(cls) -> None:
305
321
  cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"}
306
322
 
307
323
  @classmethod
308
- def is_xml_model(cls):
324
+ def is_xml_model(cls) -> bool:
309
325
  try:
310
326
  cls._xml_map # type: ignore
311
327
  except AttributeError:
@@ -322,7 +338,7 @@ class Model(object):
322
338
 
323
339
  return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None))
324
340
 
325
- def serialize(self, keep_readonly=False, **kwargs):
341
+ def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON:
326
342
  """Return the JSON that would be sent to azure from this model.
327
343
 
328
344
  This is an alias to `as_dict(full_restapi_key_transformer, keep_readonly=False)`.
@@ -336,8 +352,15 @@ class Model(object):
336
352
  serializer = Serializer(self._infer_class_models())
337
353
  return serializer._serialize(self, keep_readonly=keep_readonly, **kwargs)
338
354
 
339
- def as_dict(self, keep_readonly=True, key_transformer=attribute_transformer, **kwargs):
340
- """Return a dict that can be JSONify using json.dump.
355
+ def as_dict(
356
+ self,
357
+ keep_readonly: bool = True,
358
+ key_transformer: Callable[
359
+ [str, Dict[str, Any], Any], Any
360
+ ] = attribute_transformer,
361
+ **kwargs: Any
362
+ ) -> JSON:
363
+ """Return a dict that can be serialized using json.dump.
341
364
 
342
365
  Advanced usage might optionally use a callback as parameter:
343
366
 
@@ -384,7 +407,7 @@ class Model(object):
384
407
  return client_models
385
408
 
386
409
  @classmethod
387
- def deserialize(cls, data, content_type=None):
410
+ def deserialize(cls: Type[ModelType], data: Any, content_type: Optional[str] = None) -> ModelType:
388
411
  """Parse a str using the RestAPI syntax and return a model.
389
412
 
390
413
  :param str data: A str using RestAPI structure. JSON by default.
@@ -396,7 +419,12 @@ class Model(object):
396
419
  return deserializer(cls.__name__, data, content_type=content_type)
397
420
 
398
421
  @classmethod
399
- def from_dict(cls, data, key_extractors=None, content_type=None):
422
+ def from_dict(
423
+ cls: Type[ModelType],
424
+ data: Any,
425
+ key_extractors: Optional[Callable[[str, Dict[str, Any], Any], Any]] = None,
426
+ content_type: Optional[str] = None,
427
+ ) -> ModelType:
400
428
  """Parse a dict using given key extractor return a model.
401
429
 
402
430
  By default consider key
@@ -409,8 +437,8 @@ class Model(object):
409
437
  :raises: DeserializationError if something went wrong
410
438
  """
411
439
  deserializer = Deserializer(cls._infer_class_models())
412
- deserializer.key_extractors = (
413
- [
440
+ deserializer.key_extractors = ( # type: ignore
441
+ [ # type: ignore
414
442
  attribute_key_case_insensitive_extractor,
415
443
  rest_key_case_insensitive_extractor,
416
444
  last_rest_key_case_insensitive_extractor,
@@ -518,7 +546,7 @@ class Serializer(object):
518
546
  "multiple": lambda x, y: x % y != 0,
519
547
  }
520
548
 
521
- def __init__(self, classes=None):
549
+ def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
522
550
  self.serialize_type = {
523
551
  "iso-8601": Serializer.serialize_iso,
524
552
  "rfc-1123": Serializer.serialize_rfc,
@@ -534,7 +562,7 @@ class Serializer(object):
534
562
  "[]": self.serialize_iter,
535
563
  "{}": self.serialize_dict,
536
564
  }
537
- self.dependencies = dict(classes) if classes else {}
565
+ self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
538
566
  self.key_transformer = full_restapi_key_transformer
539
567
  self.client_side_validation = True
540
568
 
@@ -626,8 +654,7 @@ class Serializer(object):
626
654
  serialized.append(local_node) # type: ignore
627
655
  else: # JSON
628
656
  for k in reversed(keys): # type: ignore
629
- unflattened = {k: new_attr}
630
- new_attr = unflattened
657
+ new_attr = {k: new_attr}
631
658
 
632
659
  _new_attr = new_attr
633
660
  _serialized = serialized
@@ -656,8 +683,8 @@ class Serializer(object):
656
683
  """
657
684
 
658
685
  # Just in case this is a dict
659
- internal_data_type = data_type.strip("[]{}")
660
- internal_data_type = self.dependencies.get(internal_data_type, None)
686
+ internal_data_type_str = data_type.strip("[]{}")
687
+ internal_data_type = self.dependencies.get(internal_data_type_str, None)
661
688
  try:
662
689
  is_xml_model_serialization = kwargs["is_xml"]
663
690
  except KeyError:
@@ -1161,7 +1188,8 @@ def rest_key_extractor(attr, attr_desc, data):
1161
1188
  working_data = data
1162
1189
 
1163
1190
  while "." in key:
1164
- dict_keys = _FLATTEN.split(key)
1191
+ # Need the cast, as for some reasons "split" is typed as list[str | Any]
1192
+ dict_keys = cast(List[str], _FLATTEN.split(key))
1165
1193
  if len(dict_keys) == 1:
1166
1194
  key = _decode_attribute_map_key(dict_keys[0])
1167
1195
  break
@@ -1332,7 +1360,7 @@ class Deserializer(object):
1332
1360
 
1333
1361
  valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
1334
1362
 
1335
- def __init__(self, classes=None):
1363
+ def __init__(self, classes: Optional[Mapping[str, Type[ModelType]]]=None):
1336
1364
  self.deserialize_type = {
1337
1365
  "iso-8601": Deserializer.deserialize_iso,
1338
1366
  "rfc-1123": Deserializer.deserialize_rfc,
@@ -1352,7 +1380,7 @@ class Deserializer(object):
1352
1380
  "duration": (isodate.Duration, datetime.timedelta),
1353
1381
  "iso-8601": (datetime.datetime),
1354
1382
  }
1355
- self.dependencies = dict(classes) if classes else {}
1383
+ self.dependencies: Dict[str, Type[ModelType]] = dict(classes) if classes else {}
1356
1384
  self.key_extractors = [rest_key_extractor, xml_key_extractor]
1357
1385
  # Additional properties only works if the "rest_key_extractor" is used to
1358
1386
  # extract the keys. Making it to work whatever the key extractor is too much
@@ -1471,7 +1499,7 @@ class Deserializer(object):
1471
1499
  Once classification has been determined, initialize object.
1472
1500
 
1473
1501
  :param str target: The target object type to deserialize to.
1474
- :param str/dict data: The response data to deseralize.
1502
+ :param str/dict data: The response data to deserialize.
1475
1503
  """
1476
1504
  if target is None:
1477
1505
  return None, None
@@ -1486,7 +1514,7 @@ class Deserializer(object):
1486
1514
  target = target._classify(data, self.dependencies)
1487
1515
  except AttributeError:
1488
1516
  pass # Target is not a Model, no classify
1489
- return target, target.__class__.__name__
1517
+ return target, target.__class__.__name__ # type: ignore
1490
1518
 
1491
1519
  def failsafe_deserialize(self, target_obj, data, content_type=None):
1492
1520
  """Ignores any errors encountered in deserialization,
@@ -1496,7 +1524,7 @@ class Deserializer(object):
1496
1524
  a deserialization error.
1497
1525
 
1498
1526
  :param str target_obj: The target object type to deserialize to.
1499
- :param str/dict data: The response data to deseralize.
1527
+ :param str/dict data: The response data to deserialize.
1500
1528
  :param str content_type: Swagger "produces" if available.
1501
1529
  """
1502
1530
  try:
@@ -19,14 +19,15 @@ def _format_url_section(template, **kwargs):
19
19
  try:
20
20
  return template.format(**kwargs)
21
21
  except KeyError as key:
22
- formatted_components = template.split("/")
22
+ # Need the cast, as for some reasons "split" is typed as list[str | Any]
23
+ formatted_components = cast(List[str], template.split("/"))
23
24
  components = [
24
25
  c for c in formatted_components if "{{{}}}".format(key.args[0]) not in c
25
26
  ]
26
27
  template = "/".join(components)
27
28
  {% endif %}
28
29
  {% if code_model.need_mixin_abc %}
29
- {% for client in code_model.clients | selectattr("has_mixin") %}
30
+ {% for client in clients | selectattr("has_mixin") %}
30
31
 
31
32
  class {{ client.name }}MixinABC(ABC):
32
33
  """DO NOT use this class. It is for internal typing use only."""
@@ -26,17 +26,17 @@ class LocalAutorestAPI(AutorestAPI):
26
26
  reachable_files = []
27
27
  self._reachable_files = reachable_files
28
28
  self._output_folder = Path(output_folder)
29
- self.values: Dict[str, Optional[str]] = dict()
29
+ self.values: Dict[str, Optional[str]] = {}
30
30
 
31
31
  def write_file(self, filename: Union[str, Path], file_content: str) -> None:
32
32
  _LOGGER.debug("Writing file: %s", filename)
33
- with (self._output_folder / Path(filename)).open("w") as fd:
33
+ with (self._output_folder / Path(filename)).open("w", encoding="utf-8") as fd:
34
34
  fd.write(file_content)
35
35
  _LOGGER.debug("Written file: %s", filename)
36
36
 
37
37
  def read_file(self, filename: Union[str, Path]) -> str:
38
38
  _LOGGER.debug("Reading file: %s", filename)
39
- with (self._output_folder / Path(filename)).open("r") as fd:
39
+ with (self._output_folder / Path(filename)).open("r", encoding="utf-8") as fd:
40
40
  return fd.read()
41
41
 
42
42
  def list_inputs(self) -> List[str]:
@@ -84,15 +84,15 @@ def main() -> None:
84
84
  ):
85
85
  try:
86
86
  import ptvsd # pylint: disable=import-outside-toplevel
87
- except ImportError:
87
+ except ImportError as exc:
88
88
  raise SystemExit(
89
89
  "Please pip install ptvsd in order to use VSCode debugging"
90
- )
90
+ ) from exc
91
91
 
92
92
  # 5678 is the default attach port in the VS Code debug configurations
93
93
  ptvsd.enable_attach(address=("localhost", 5678), redirect_output=True)
94
94
  ptvsd.wait_for_attach()
95
- breakpoint() # pylint: disable=undefined-variable
95
+ breakpoint() # pylint: disable=undefined-variable,forgotten-debug-statement
96
96
 
97
97
  _LOGGER.debug("Starting JSON RPC server")
98
98
 
@@ -22,7 +22,7 @@ class AutorestRender(m2r2.RestRenderer):
22
22
  in the description/summary.
23
23
  """
24
24
 
25
- def inline_html(self, html: str) -> str: # pylint: disable=no-self-use
25
+ def inline_html(self, html: str) -> str:
26
26
  """Do not render inline HTML with a role definition."""
27
27
  return f":code:`{html}`"
28
28
 
@@ -143,6 +143,7 @@ def create_model(yaml_data: Dict[str, Any]) -> Dict[str, Any]:
143
143
  base["name"] = yaml_data["language"]["default"]["name"]
144
144
  base["description"] = yaml_data["language"]["default"]["description"]
145
145
  base["isXml"] = "xml" in yaml_data.get("serializationFormats", [])
146
+ base["base"] = "msrest"
146
147
  return base
147
148
 
148
149
 
@@ -422,6 +423,12 @@ class M4Reformatter(
422
423
  def legacy(self) -> bool:
423
424
  return not (self.version_tolerant or self.low_level_client)
424
425
 
426
+ @property
427
+ def only_path_and_body_parameters_positional(self) -> bool:
428
+ return self.version_tolerant or bool(
429
+ self._autorestapi.get_boolean_value("only-path-and-body-params-positional")
430
+ )
431
+
425
432
  @property
426
433
  def default_optional_constants_to_none(self) -> bool:
427
434
  return bool(
@@ -951,7 +958,11 @@ class M4Reformatter(
951
958
  if name == "$host":
952
959
  # I am the non-parameterized endpoint. Modify name based off of flag
953
960
 
954
- client_name = "base_url" if self.legacy else "endpoint"
961
+ client_name = (
962
+ "endpoint"
963
+ if self.only_path_and_body_parameters_positional
964
+ else "base_url"
965
+ )
955
966
  global_parameter["language"]["default"]["description"] = "Service URL."
956
967
  elif (
957
968
  global_parameter.get("origin") == "modelerfour:synthesized/api-version"
@@ -1082,7 +1093,7 @@ class M4Reformatter(
1082
1093
  "skipUrlEncoding": True,
1083
1094
  "inOverload": False,
1084
1095
  }
1085
- if self.version_tolerant or self.low_level_client:
1096
+ if self.only_path_and_body_parameters_positional:
1086
1097
  parameters.append(credential)
1087
1098
  else:
1088
1099
  parameters.insert(0, credential)
@@ -5,10 +5,12 @@
5
5
  # --------------------------------------------------------------------------
6
6
  from .code_model import CodeModel
7
7
  from .imports import ImportType, FileImport, TypingSection
8
+ from .global_parameter import GlobalParameter
8
9
 
9
10
  __all__ = [
10
11
  "CodeModel",
11
12
  "FileImport",
12
13
  "ImportType",
13
14
  "TypingSection",
15
+ "GlobalParameter",
14
16
  ]
@@ -71,6 +71,19 @@ class CodeModel: # pylint: disable=too-many-instance-attributes
71
71
  operation_groups.sort(key=lambda x: x.name)
72
72
  return operation_groups
73
73
 
74
+ @property
75
+ def host_variable_name(self) -> str:
76
+ if self.client.parameterized_host_template_to_api_version:
77
+ return "base_url"
78
+ params = (
79
+ self.global_parameters.parameters
80
+ + self.global_parameters.service_client_specific_global_parameters
81
+ )
82
+ try:
83
+ return next(p for p in params if p.name in ["endpoint", "base_url"]).name
84
+ except StopIteration:
85
+ return "_endpoint"
86
+
74
87
  @property
75
88
  def last_rt_list(self) -> Dict[str, str]:
76
89
  """Build the a mapping RT => API version if RT doesn't exist in latest detected API version.
@@ -17,6 +17,7 @@ class GlobalParameter:
17
17
  self.global_parameter_metadata_sync = global_parameter_metadata_sync
18
18
  self.global_parameter_metadata_async = global_parameter_metadata_async
19
19
  self.required = global_parameter_metadata_sync["required"]
20
+ self.method_location = global_parameter_metadata_sync["method_location"]
20
21
 
21
22
  def _global_parameter_metadata(self, async_mode: bool) -> Dict[str, Any]:
22
23
  if async_mode:
@@ -85,7 +85,7 @@ class FileImport:
85
85
  ],
86
86
  ],
87
87
  ] = (
88
- imports or dict()
88
+ imports or {}
89
89
  )
90
90
 
91
91
  def _add_import(
@@ -123,8 +123,8 @@ class FileImport:
123
123
  ]
124
124
  ] = None
125
125
  name_input = convert_list_to_tuple(name_import)
126
- self._imports.setdefault(typing_section, dict()).setdefault(
127
- import_type, dict()
126
+ self._imports.setdefault(typing_section, {}).setdefault(
127
+ import_type, {}
128
128
  ).setdefault(from_section, set()).add(name_input)
129
129
 
130
130
  def add_submodule_import(
@@ -4,13 +4,13 @@
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
6
  from pathlib import Path
7
- from typing import Any, Optional, Union
7
+ from typing import Any, Optional, Union, List
8
8
  from jinja2 import PackageLoader, Environment
9
9
 
10
10
  from .import_serializer import FileImportSerializer
11
11
 
12
12
  from ...jsonrpc import AutorestAPI
13
- from ..models import CodeModel
13
+ from ..models import CodeModel, GlobalParameter
14
14
  from ... import ReaderAndWriter, ReaderAndWriterAutorest
15
15
 
16
16
  __all__ = [
@@ -26,6 +26,15 @@ _FILE_TO_TEMPLATE = {
26
26
  }
27
27
 
28
28
 
29
+ def _method_signature_helper(
30
+ parameters: List[GlobalParameter], async_mode: bool
31
+ ) -> List[str]:
32
+ return [
33
+ p.signature(async_mode)
34
+ for p in sorted(parameters, key=lambda p: p.required, reverse=True)
35
+ ]
36
+
37
+
29
38
  def _get_file_path(filename: str, async_mode: bool) -> Path:
30
39
  filename += ".py"
31
40
  if async_mode:
@@ -48,8 +57,26 @@ class MultiAPISerializer(ReaderAndWriter): # pylint: disable=abstract-method
48
57
  def _serialize_helper(self, code_model: CodeModel, async_mode: bool) -> None:
49
58
  def _render_template(file: str, **kwargs: Any) -> str:
50
59
  template = self.env.get_template(_FILE_TO_TEMPLATE[file])
60
+ all_params = (
61
+ code_model.global_parameters.parameters
62
+ + code_model.global_parameters.service_client_specific_global_parameters
63
+ )
64
+ positional_params = [
65
+ p for p in all_params if p.method_location == "positional"
66
+ ]
67
+ keyword_only_params = [
68
+ p for p in all_params if p.method_location == "keywordOnly"
69
+ ]
51
70
  return template.render(
52
- code_model=code_model, async_mode=async_mode, **kwargs
71
+ code_model=code_model,
72
+ async_mode=async_mode,
73
+ positional_params=_method_signature_helper(
74
+ positional_params, async_mode
75
+ ),
76
+ keyword_only_params=_method_signature_helper(
77
+ keyword_only_params, async_mode
78
+ ),
79
+ **kwargs
53
80
  )
54
81
 
55
82
  # serialize init file
@@ -1,18 +1,14 @@
1
1
  {% macro method_signature() %}
2
2
  def __init__(
3
3
  self,
4
- {% for parameter in code_model.global_parameters.parameters %}
5
- {% if parameter.required %}
6
- {{ parameter.signature(async_mode) }}
7
- {% endif %}
8
- {% endfor %}
9
- {% for parameter in code_model.global_parameters.parameters %}
10
- {% if not parameter.required %}
11
- {{ parameter.signature(async_mode) }}
12
- {% endif %}
4
+ {% for pos in positional_params %}
5
+ {{ pos }}
13
6
  {% endfor %}
14
- {% for parameter in code_model.global_parameters.service_client_specific_global_parameters %}
15
- {{ parameter.signature(async_mode) }}
7
+ {% if keyword_only_params %}
8
+ *,
9
+ {% endif %}
10
+ {% for ko in keyword_only_params %}
11
+ {{ ko }}
16
12
  {% endfor %}
17
13
  **kwargs: Any
18
14
  ){{" -> None" if async_mode else "" }}:{% endmacro %}
@@ -92,7 +88,7 @@ class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_
92
88
  raise ValueError("API version {} is not available".format(api_version))
93
89
  {% endif %}
94
90
  self._config = {{ code_model.client.name }}Configuration({{ code_model.global_parameters.call }}{{ ", " if code_model.global_parameters.call }}**kwargs)
95
- self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url=base_url, config=self._config, **kwargs)
91
+ self._client = {{ async_prefix }}{{ code_model.client.pipeline_client }}(base_url={{ code_model.host_variable_name }}, config=self._config, **kwargs)
96
92
  super({{ code_model.client.name }}, self).__init__(
97
93
  api_version=api_version,
98
94
  profile=profile
@@ -15,5 +15,7 @@ def main(namespace):
15
15
  if __name__ == "__main__":
16
16
  patched = ",".join(main(sys.argv[1]))
17
17
  output_folder = sys.argv[2]
18
- with open(f"{output_folder}/.temp_folder/patched.txt", "w") as f:
18
+ with open(
19
+ f"{output_folder}/.temp_folder/patched.txt", "w", encoding="utf-8-sig"
20
+ ) as f:
19
21
  f.write(patched)
@@ -48,7 +48,7 @@ def create(
48
48
 
49
49
 
50
50
  def python_run( # pylint: disable=inconsistent-return-statements
51
- venv_context, module, command, directory=_ROOT_DIR, *, error_ok=False
51
+ venv_context, module, command, directory=_ROOT_DIR
52
52
  ) -> Optional[str]:
53
53
  try:
54
54
  cmd_line = [
@@ -64,10 +64,11 @@ def python_run( # pylint: disable=inconsistent-return-statements
64
64
  stdout=False,
65
65
  )
66
66
  if module == "get_all":
67
- with open(f"{command[1]}/.temp_folder/patched.txt", "r") as f:
67
+ with open(
68
+ f"{command[1]}/.temp_folder/patched.txt", "r", encoding="utf-8-sig"
69
+ ) as f:
68
70
  return f.read()
69
71
  except subprocess.CalledProcessError as err:
70
72
  print(err)
71
- if not error_ok:
72
- sys.exit(1)
73
+ sys.exit(1)
73
74
  return None
@@ -23,7 +23,6 @@ from .._utils import parse_args, get_body_type_for_description, JSON_REGEXP, KNO
23
23
  def add_body_param_type(
24
24
  code_model: Dict[str, Any],
25
25
  body_parameter: Dict[str, Any],
26
- models_mode: Optional[str],
27
26
  ):
28
27
  if (
29
28
  body_parameter
@@ -35,11 +34,12 @@ def add_body_param_type(
35
34
  and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
36
35
  ):
37
36
  origin_type = body_parameter["type"]["type"]
37
+ is_dpg_model = body_parameter["type"].get("base") == "dpg"
38
38
  body_parameter["type"] = {
39
39
  "type": "combined",
40
40
  "types": [body_parameter["type"], KNOWN_TYPES["binary"]],
41
41
  }
42
- if origin_type == "model" and models_mode == "dpg":
42
+ if origin_type == "model" and is_dpg_model:
43
43
  body_parameter["type"]["types"].insert(1, KNOWN_TYPES["any-object"])
44
44
  code_model["types"].append(body_parameter["type"])
45
45
 
@@ -60,13 +60,21 @@ def update_overload_section(
60
60
  overload_h["type"] = original_h["type"]
61
61
 
62
62
 
63
- def add_overload(yaml_data: Dict[str, Any], body_type: Dict[str, Any]):
63
+ def add_overload(
64
+ yaml_data: Dict[str, Any], body_type: Dict[str, Any], for_flatten_params=False
65
+ ):
64
66
  overload = copy.deepcopy(yaml_data)
65
67
  overload["isOverload"] = True
66
68
  overload["bodyParameter"]["type"] = body_type
67
69
 
68
70
  overload["overloads"] = []
69
71
 
72
+ if for_flatten_params:
73
+ overload["bodyParameter"]["flattened"] = True
74
+ else:
75
+ overload["parameters"] = [
76
+ p for p in overload["parameters"] if not p.get("inFlattenedBody")
77
+ ]
70
78
  # for yaml sync, we need to make sure all of the responses, parameters, and exceptions' types have the same yaml id
71
79
  for overload_p, original_p in zip(overload["parameters"], yaml_data["parameters"]):
72
80
  overload_p["type"] = original_p["type"]
@@ -107,6 +115,10 @@ def add_overloads_for_body_param(yaml_data: Dict[str, Any]) -> None:
107
115
  ):
108
116
  continue
109
117
  yaml_data["overloads"].append(add_overload(yaml_data, body_type))
118
+ if body_type.get("type") == "model" and body_type.get("base") == "json":
119
+ yaml_data["overloads"].append(
120
+ add_overload(yaml_data, body_type, for_flatten_params=True)
121
+ )
110
122
  content_type_param = next(
111
123
  p for p in yaml_data["parameters"] if p["restApiName"].lower() == "content-type"
112
124
  )
@@ -162,9 +174,9 @@ def update_parameter(yaml_data: Dict[str, Any]) -> None:
162
174
  if yaml_data.get("propertyToParameterName"):
163
175
  # need to create a new one with padded keys and values
164
176
  yaml_data["propertyToParameterName"] = {
165
- pad_reserved_words(prop, PadType.PROPERTY)
166
- .lower(): pad_reserved_words(param_name, PadType.PARAMETER)
167
- .lower()
177
+ pad_reserved_words(prop, PadType.PROPERTY): pad_reserved_words(
178
+ param_name, PadType.PARAMETER
179
+ )
168
180
  for prop, param_name in yaml_data["propertyToParameterName"].items()
169
181
  }
170
182
 
@@ -257,7 +269,7 @@ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
257
269
  response["discriminator"] = "operation"
258
270
  if body_parameter and not is_overload:
259
271
  # if we have a JSON body, we add a binary overload
260
- add_body_param_type(code_model, body_parameter, self.models_mode)
272
+ add_body_param_type(code_model, body_parameter)
261
273
  add_overloads_for_body_param(yaml_data)
262
274
 
263
275
  def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
@@ -173,6 +173,7 @@ REDEFINED_BUILTINS = [ # we don't pad, but we need to do lint ignores
173
173
  "min",
174
174
  "max",
175
175
  "filter",
176
+ "property",
176
177
  ]
177
178
 
178
179
  BUILTIN_PACKAGES = [
package/index.js ADDED
File without changes
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.2.11",
3
+ "version": "6.2.15",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
+ "main": "index.js",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "https://github.com/Azure/autorest.python/tree/autorestv3"
package/requirements.txt CHANGED
@@ -1,13 +1,13 @@
1
- black==22.6.0
1
+ black==22.12.0
2
2
  click==8.1.3
3
- docutils==0.18
3
+ docutils==0.19
4
4
  Jinja2==3.1.2
5
- json-rpc==1.13.0
6
- m2r2==0.3.2
5
+ json-rpc==1.14.0
6
+ m2r2==0.3.3
7
7
  MarkupSafe==2.1.1
8
8
  mistune==0.8.4
9
9
  mypy-extensions==0.4.3
10
- pathspec==0.9.0
11
- platformdirs==2.5.2
10
+ pathspec==0.10.3
11
+ platformdirs==2.6.2
12
12
  PyYAML==6.0
13
13
  tomli==2.0.1
package/setup.py CHANGED
@@ -52,6 +52,5 @@ setup(
52
52
  "pyyaml",
53
53
  "m2r2",
54
54
  "black",
55
- "docutils<0.19", # m2r2 fails with docutils 0.19
56
55
  ],
57
56
  )