@autorest/python 5.14.0 → 5.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.
Files changed (120) hide show
  1. package/ChangeLog.md +91 -2
  2. package/README.md +30 -4
  3. package/autorest/__init__.py +2 -3
  4. package/autorest/black/__init__.py +12 -5
  5. package/autorest/codegen/__init__.py +130 -179
  6. package/autorest/codegen/models/__init__.py +122 -78
  7. package/autorest/codegen/models/base_builder.py +70 -72
  8. package/autorest/codegen/models/base_model.py +7 -5
  9. package/autorest/codegen/models/{base_schema.py → base_type.py} +62 -49
  10. package/autorest/codegen/models/client.py +195 -36
  11. package/autorest/codegen/models/code_model.py +165 -299
  12. package/autorest/codegen/models/combined_type.py +107 -0
  13. package/autorest/codegen/models/constant_type.py +122 -0
  14. package/autorest/codegen/models/credential_types.py +224 -0
  15. package/autorest/codegen/models/dictionary_type.py +116 -0
  16. package/autorest/codegen/models/enum_type.py +195 -0
  17. package/autorest/codegen/models/imports.py +95 -41
  18. package/autorest/codegen/models/list_type.py +134 -0
  19. package/autorest/codegen/models/lro_operation.py +90 -133
  20. package/autorest/codegen/models/lro_paging_operation.py +28 -12
  21. package/autorest/codegen/models/model_type.py +239 -0
  22. package/autorest/codegen/models/operation.py +415 -241
  23. package/autorest/codegen/models/operation_group.py +82 -88
  24. package/autorest/codegen/models/paging_operation.py +101 -117
  25. package/autorest/codegen/models/parameter.py +307 -322
  26. package/autorest/codegen/models/parameter_list.py +366 -357
  27. package/autorest/codegen/models/primitive_types.py +544 -0
  28. package/autorest/codegen/models/property.py +122 -134
  29. package/autorest/codegen/models/request_builder.py +138 -86
  30. package/autorest/codegen/models/request_builder_parameter.py +122 -79
  31. package/autorest/codegen/models/response.py +325 -0
  32. package/autorest/codegen/models/utils.py +17 -1
  33. package/autorest/codegen/serializers/__init__.py +242 -118
  34. package/autorest/codegen/serializers/builder_serializer.py +863 -1027
  35. package/autorest/codegen/serializers/client_serializer.py +148 -82
  36. package/autorest/codegen/serializers/general_serializer.py +44 -47
  37. package/autorest/codegen/serializers/import_serializer.py +96 -31
  38. package/autorest/codegen/serializers/metadata_serializer.py +39 -79
  39. package/autorest/codegen/serializers/model_base_serializer.py +65 -29
  40. package/autorest/codegen/serializers/model_generic_serializer.py +9 -10
  41. package/autorest/codegen/serializers/model_init_serializer.py +4 -2
  42. package/autorest/codegen/serializers/model_python3_serializer.py +29 -22
  43. package/autorest/codegen/serializers/operation_groups_serializer.py +21 -18
  44. package/autorest/codegen/serializers/operations_init_serializer.py +23 -11
  45. package/autorest/codegen/serializers/parameter_serializer.py +174 -0
  46. package/autorest/codegen/serializers/patch_serializer.py +14 -2
  47. package/autorest/codegen/serializers/request_builders_serializer.py +57 -0
  48. package/autorest/codegen/serializers/utils.py +0 -103
  49. package/autorest/codegen/templates/MANIFEST.in.jinja2 +1 -0
  50. package/autorest/codegen/templates/{service_client.py.jinja2 → client.py.jinja2} +7 -7
  51. package/autorest/codegen/templates/config.py.jinja2 +13 -13
  52. package/autorest/codegen/templates/enum.py.jinja2 +4 -4
  53. package/autorest/codegen/templates/enum_container.py.jinja2 +1 -2
  54. package/autorest/codegen/templates/init.py.jinja2 +9 -6
  55. package/autorest/codegen/templates/keywords.jinja2 +14 -1
  56. package/autorest/codegen/templates/lro_operation.py.jinja2 +6 -5
  57. package/autorest/codegen/templates/lro_paging_operation.py.jinja2 +6 -5
  58. package/autorest/codegen/templates/metadata.json.jinja2 +36 -35
  59. package/autorest/codegen/templates/model.py.jinja2 +23 -29
  60. package/autorest/codegen/templates/model_container.py.jinja2 +2 -1
  61. package/autorest/codegen/templates/model_init.py.jinja2 +9 -8
  62. package/autorest/codegen/templates/operation.py.jinja2 +10 -15
  63. package/autorest/codegen/templates/operation_group.py.jinja2 +14 -13
  64. package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -2
  65. package/autorest/codegen/templates/operation_tools.jinja2 +8 -2
  66. package/autorest/codegen/templates/operations_folder_init.py.jinja2 +4 -0
  67. package/autorest/codegen/templates/paging_operation.py.jinja2 +7 -8
  68. package/autorest/codegen/templates/patch.py.jinja2 +18 -29
  69. package/autorest/codegen/templates/request_builder.py.jinja2 +20 -13
  70. package/autorest/codegen/templates/setup.py.jinja2 +9 -3
  71. package/autorest/codegen/templates/vendor.py.jinja2 +12 -2
  72. package/autorest/jsonrpc/__init__.py +7 -12
  73. package/autorest/jsonrpc/localapi.py +4 -3
  74. package/autorest/jsonrpc/server.py +28 -9
  75. package/autorest/jsonrpc/stdstream.py +13 -6
  76. package/autorest/m2r/__init__.py +5 -8
  77. package/autorest/m4reformatter/__init__.py +1108 -0
  78. package/autorest/multiapi/__init__.py +24 -14
  79. package/autorest/multiapi/models/client.py +21 -11
  80. package/autorest/multiapi/models/code_model.py +23 -10
  81. package/autorest/multiapi/models/config.py +4 -1
  82. package/autorest/multiapi/models/constant_global_parameter.py +1 -0
  83. package/autorest/multiapi/models/global_parameter.py +2 -1
  84. package/autorest/multiapi/models/global_parameters.py +14 -8
  85. package/autorest/multiapi/models/imports.py +35 -18
  86. package/autorest/multiapi/models/mixin_operation.py +5 -5
  87. package/autorest/multiapi/models/operation_group.py +2 -1
  88. package/autorest/multiapi/models/operation_mixin_group.py +21 -10
  89. package/autorest/multiapi/serializers/__init__.py +20 -25
  90. package/autorest/multiapi/serializers/import_serializer.py +47 -15
  91. package/autorest/multiapi/serializers/multiapi_serializer.py +17 -17
  92. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +3 -3
  93. package/autorest/multiapi/templates/multiapi_init.py.jinja2 +2 -2
  94. package/autorest/multiapi/templates/multiapi_operations_mixin.py.jinja2 +4 -4
  95. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +9 -9
  96. package/autorest/multiapi/utils.py +3 -3
  97. package/autorest/postprocess/__init__.py +202 -0
  98. package/autorest/postprocess/get_all.py +19 -0
  99. package/autorest/postprocess/venvtools.py +73 -0
  100. package/autorest/preprocess/__init__.py +209 -0
  101. package/autorest/preprocess/helpers.py +54 -0
  102. package/autorest/{namer → preprocess}/python_mappings.py +25 -32
  103. package/package.json +3 -3
  104. package/run-python3.js +2 -3
  105. package/venvtools.py +1 -1
  106. package/autorest/codegen/models/constant_schema.py +0 -97
  107. package/autorest/codegen/models/credential_schema.py +0 -90
  108. package/autorest/codegen/models/credential_schema_policy.py +0 -77
  109. package/autorest/codegen/models/dictionary_schema.py +0 -103
  110. package/autorest/codegen/models/enum_schema.py +0 -246
  111. package/autorest/codegen/models/list_schema.py +0 -113
  112. package/autorest/codegen/models/object_schema.py +0 -249
  113. package/autorest/codegen/models/primitive_schemas.py +0 -476
  114. package/autorest/codegen/models/request_builder_parameter_list.py +0 -280
  115. package/autorest/codegen/models/rest.py +0 -42
  116. package/autorest/codegen/models/schema_request.py +0 -45
  117. package/autorest/codegen/models/schema_response.py +0 -123
  118. package/autorest/codegen/serializers/rest_serializer.py +0 -57
  119. package/autorest/namer/__init__.py +0 -25
  120. package/autorest/namer/name_converter.py +0 -412
@@ -18,12 +18,13 @@ __all__ = [
18
18
 
19
19
  _FILE_TO_TEMPLATE = {
20
20
  "init": "multiapi_init.py.jinja2",
21
- "service_client": "multiapi_service_client.py.jinja2",
21
+ "client": "multiapi_service_client.py.jinja2",
22
22
  "config": "multiapi_config.py.jinja2",
23
23
  "models": "multiapi_models.py.jinja2",
24
- "operations_mixin": "multiapi_operations_mixin.py.jinja2"
24
+ "operations_mixin": "multiapi_operations_mixin.py.jinja2",
25
25
  }
26
26
 
27
+
27
28
  def _get_file_path(filename: str, async_mode: bool) -> Path:
28
29
  filename += ".py"
29
30
  if async_mode:
@@ -43,44 +44,45 @@ class MultiAPISerializer(object):
43
44
  lstrip_blocks=True,
44
45
  )
45
46
 
46
-
47
47
  def _serialize_helper(self, code_model: CodeModel, async_mode: bool) -> None:
48
48
  def _render_template(file: str, **kwargs: Any) -> str:
49
49
  template = self.env.get_template(_FILE_TO_TEMPLATE[file])
50
- return template.render(code_model=code_model, async_mode=async_mode, **kwargs)
50
+ return template.render(
51
+ code_model=code_model, async_mode=async_mode, **kwargs
52
+ )
51
53
 
52
54
  # serialize init file
53
- self._autorestapi.write_file(_get_file_path("__init__", async_mode), _render_template("init"))
55
+ self._autorestapi.write_file(
56
+ _get_file_path("__init__", async_mode), _render_template("init")
57
+ )
54
58
 
55
59
  # serialize service client file
56
60
  imports = FileImportSerializer(
57
- code_model.service_client.imports(async_mode),
58
- is_python3_file=async_mode
61
+ code_model.client.imports(async_mode), is_python3_file=async_mode
59
62
  )
60
63
  self._autorestapi.write_file(
61
- _get_file_path(code_model.service_client.filename, async_mode),
62
- _render_template("service_client", imports=imports)
64
+ _get_file_path(code_model.client.filename, async_mode),
65
+ _render_template("client", imports=imports),
63
66
  )
64
67
 
65
68
  # serialize config file
66
69
  imports = FileImportSerializer(
67
- code_model.config.imports(async_mode),
68
- is_python3_file=async_mode
70
+ code_model.config.imports(async_mode), is_python3_file=async_mode
69
71
  )
70
72
  self._autorestapi.write_file(
71
73
  _get_file_path("_configuration", async_mode),
72
- _render_template("config", imports=imports)
74
+ _render_template("config", imports=imports),
73
75
  )
74
76
 
75
77
  # serialize mixins
76
78
  if code_model.operation_mixin_group.mixin_operations:
77
79
  imports = FileImportSerializer(
78
80
  code_model.operation_mixin_group.imports(async_mode),
79
- is_python3_file=async_mode
81
+ is_python3_file=async_mode,
80
82
  )
81
83
  self._autorestapi.write_file(
82
84
  _get_file_path("_operations_mixin", async_mode),
83
- _render_template("operations_mixin", imports=imports)
85
+ _render_template("operations_mixin", imports=imports),
84
86
  )
85
87
 
86
88
  # serialize models
@@ -89,21 +91,15 @@ class MultiAPISerializer(object):
89
91
  def _serialize_version_file(self) -> None:
90
92
  if self._autorestapi.read_file("_version.py"):
91
93
  self._autorestapi.write_file(
92
- "_version.py",
93
- self._autorestapi.read_file("_version.py")
94
+ "_version.py", self._autorestapi.read_file("_version.py")
94
95
  )
95
96
  elif self._autorestapi.read_file("version.py"):
96
97
  self._autorestapi.write_file(
97
- "_version.py",
98
- self._autorestapi.read_file("version.py")
98
+ "_version.py", self._autorestapi.read_file("version.py")
99
99
  )
100
100
  else:
101
101
  template = self.env.get_template("multiapi_version.py.jinja2")
102
- self._autorestapi.write_file(
103
- Path("_version.py"),
104
- template.render()
105
- )
106
-
102
+ self._autorestapi.write_file(Path("_version.py"), template.render())
107
103
 
108
104
  def serialize(self, code_model: CodeModel, no_async: Optional[bool]) -> None:
109
105
  self._serialize_helper(code_model, async_mode=False)
@@ -115,8 +111,7 @@ class MultiAPISerializer(object):
115
111
  # don't erase patch file
116
112
  if self._autorestapi.read_file("_patch.py"):
117
113
  self._autorestapi.write_file(
118
- "_patch.py",
119
- self._autorestapi.read_file("_patch.py")
114
+ "_patch.py", self._autorestapi.read_file("_patch.py")
120
115
  )
121
116
 
122
117
  self._autorestapi.write_file(Path("py.typed"), "# Marker file for PEP 561.")
@@ -7,19 +7,34 @@ from copy import deepcopy
7
7
  from typing import Dict, Set, Optional, List
8
8
  from ..models import ImportType, FileImport, TypingSection
9
9
 
10
- def _serialize_package(package_name: str, module_list: Set[Optional[str]], delimiter: str) -> str:
10
+
11
+ def _serialize_package(
12
+ package_name: str, module_list: Set[Optional[str]], delimiter: str
13
+ ) -> str:
11
14
  buffer = []
12
15
  if None in module_list:
13
16
  buffer.append(f"import {package_name}")
14
17
  if module_list != {None}:
15
18
  buffer.append(
16
19
  "from {} import {}".format(
17
- package_name, ", ".join(sorted([mod for mod in module_list if mod is not None]))
20
+ package_name,
21
+ ", ".join(
22
+ sorted(
23
+ [
24
+ mod if isinstance(mod, str) else f"{mod[0]} as {mod[1]}"
25
+ for mod in module_list
26
+ if mod is not None
27
+ ]
28
+ )
29
+ ),
18
30
  )
19
31
  )
20
32
  return delimiter.join(buffer)
21
33
 
22
- def _serialize_type(import_type_dict: Dict[str, Set[Optional[str]]], delimiter: str) -> str:
34
+
35
+ def _serialize_type(
36
+ import_type_dict: Dict[str, Set[Optional[str]]], delimiter: str
37
+ ) -> str:
23
38
  """Serialize a given import type."""
24
39
  import_list = []
25
40
  for package_name in sorted(list(import_type_dict.keys())):
@@ -27,7 +42,10 @@ def _serialize_type(import_type_dict: Dict[str, Set[Optional[str]]], delimiter:
27
42
  import_list.append(_serialize_package(package_name, module_list, delimiter))
28
43
  return delimiter.join(import_list)
29
44
 
30
- def _get_import_clauses(imports: Dict[ImportType, Dict[str, Set[Optional[str]]]], delimiter: str) -> List[str]:
45
+
46
+ def _get_import_clauses(
47
+ imports: Dict[ImportType, Dict[str, Set[Optional[str]]]], delimiter: str
48
+ ) -> List[str]:
31
49
  import_clause = []
32
50
  for import_type in ImportType:
33
51
  if import_type in imports:
@@ -42,33 +60,44 @@ class FileImportSerializer:
42
60
 
43
61
  def _switch_typing_section_key(self, new_key: TypingSection):
44
62
  switched_dictionary = {}
45
- switched_dictionary[new_key] = self._file_import.imports[TypingSection.CONDITIONAL]
63
+ switched_dictionary[new_key] = self._file_import.imports[
64
+ TypingSection.CONDITIONAL
65
+ ]
46
66
  return switched_dictionary
47
67
 
48
- def _get_imports_dict(self, baseline_typing_section: TypingSection, add_conditional_typing: bool):
68
+ def _get_imports_dict(
69
+ self, baseline_typing_section: TypingSection, add_conditional_typing: bool
70
+ ):
49
71
  # If this is a python 3 file, our regular imports include the CONDITIONAL category
50
72
  # If this is not a python 3 file, our typing imports include the CONDITIONAL category
51
73
  file_import_copy = deepcopy(self._file_import)
52
- if add_conditional_typing and self._file_import.imports.get(TypingSection.CONDITIONAL):
74
+ if add_conditional_typing and self._file_import.imports.get(
75
+ TypingSection.CONDITIONAL
76
+ ):
53
77
  # we switch the TypingSection key for the CONDITIONAL typing imports so we can merge
54
78
  # the imports together
55
- switched_imports_dictionary = self._switch_typing_section_key(baseline_typing_section)
79
+ switched_imports_dictionary = self._switch_typing_section_key(
80
+ baseline_typing_section
81
+ )
56
82
  switched_imports = FileImport(switched_imports_dictionary)
57
83
  file_import_copy.merge(switched_imports)
58
84
  return file_import_copy.imports.get(baseline_typing_section, {})
59
85
 
60
86
  def _add_type_checking_import(self):
61
- if (
62
- self._file_import.imports.get(TypingSection.TYPING) or
63
- (not self.is_python3_file and self._file_import.imports.get(TypingSection.CONDITIONAL))
87
+ if self._file_import.imports.get(TypingSection.TYPING) or (
88
+ not self.is_python3_file
89
+ and self._file_import.imports.get(TypingSection.CONDITIONAL)
64
90
  ):
65
- self._file_import.add_submodule_import("typing", "TYPE_CHECKING", ImportType.STDLIB)
91
+ self._file_import.add_submodule_import(
92
+ "typing", "TYPE_CHECKING", ImportType.STDLIB
93
+ )
66
94
 
67
95
  def __str__(self) -> str:
68
96
  self._add_type_checking_import()
69
97
  regular_imports = ""
70
98
  regular_imports_dict = self._get_imports_dict(
71
- baseline_typing_section=TypingSection.REGULAR, add_conditional_typing=self.is_python3_file
99
+ baseline_typing_section=TypingSection.REGULAR,
100
+ add_conditional_typing=self.is_python3_file,
72
101
  )
73
102
 
74
103
  if regular_imports_dict:
@@ -78,10 +107,13 @@ class FileImportSerializer:
78
107
 
79
108
  typing_imports = ""
80
109
  typing_imports_dict = self._get_imports_dict(
81
- baseline_typing_section=TypingSection.TYPING, add_conditional_typing=not self.is_python3_file
110
+ baseline_typing_section=TypingSection.TYPING,
111
+ add_conditional_typing=not self.is_python3_file,
82
112
  )
83
113
  if typing_imports_dict:
84
114
  typing_imports += "\n\nif TYPE_CHECKING:\n # pylint: disable=unused-import,ungrouped-imports\n "
85
- typing_imports += "\n\n ".join(_get_import_clauses(typing_imports_dict, "\n "))
115
+ typing_imports += "\n\n ".join(
116
+ _get_import_clauses(typing_imports_dict, "\n ")
117
+ )
86
118
 
87
119
  return regular_imports + typing_imports
@@ -12,7 +12,11 @@ from ...jsonrpc import AutorestAPI
12
12
 
13
13
  class MultiAPISerializer:
14
14
  def __init__(
15
- self, conf: Dict[str, Any], async_mode: bool, autorestapi: AutorestAPI, service_client_filename: str
15
+ self,
16
+ conf: Dict[str, Any],
17
+ async_mode: bool,
18
+ autorestapi: AutorestAPI,
19
+ service_client_filename: str,
16
20
  ):
17
21
  self.conf = conf
18
22
  self.async_mode = async_mode
@@ -33,64 +37,60 @@ class MultiAPISerializer:
33
37
  return Path(filename)
34
38
 
35
39
  def serialize(self):
36
- self._autorestapi.write_file(self._get_file_path("__init__.py"), self.serialize_multiapi_init())
40
+ self._autorestapi.write_file(
41
+ self._get_file_path("__init__.py"), self.serialize_multiapi_init()
42
+ )
37
43
 
38
44
  service_client_filename_with_py_extension = self.service_client_filename + ".py"
39
45
  self._autorestapi.write_file(
40
46
  self._get_file_path(service_client_filename_with_py_extension),
41
- self.serialize_multiapi_client()
47
+ self.serialize_multiapi_client(),
42
48
  )
43
49
 
44
50
  configuration_filename = "_configuration.py"
45
51
  self._autorestapi.write_file(
46
52
  self._get_file_path(configuration_filename),
47
- self.serialize_multiapi_config()
53
+ self.serialize_multiapi_config(),
48
54
  )
49
55
 
50
56
  operation_mixins_filename = "_operations_mixin.py"
51
57
  if self.conf["mixin_operations"]:
52
58
  self._autorestapi.write_file(
53
59
  self._get_file_path(operation_mixins_filename),
54
- self.serialize_multiapi_operation_mixins()
60
+ self.serialize_multiapi_operation_mixins(),
55
61
  )
56
62
 
57
63
  if self._autorestapi.read_file("_version.py"):
58
64
  self._autorestapi.write_file(
59
- "_version.py",
60
- self._autorestapi.read_file("_version.py")
65
+ "_version.py", self._autorestapi.read_file("_version.py")
61
66
  )
62
67
  elif self._autorestapi.read_file("version.py"):
63
68
  self._autorestapi.write_file(
64
- "_version.py",
65
- self._autorestapi.read_file("version.py")
69
+ "_version.py", self._autorestapi.read_file("version.py")
66
70
  )
67
71
  else:
68
72
  self._autorestapi.write_file(
69
- Path("_version.py"),
70
- self.serialize_multiapi_version()
73
+ Path("_version.py"), self.serialize_multiapi_version()
71
74
  )
72
75
 
73
76
  # don't erase patch file
74
77
  if self._autorestapi.read_file("_patch.py"):
75
78
  self._autorestapi.write_file(
76
- "_patch.py",
77
- self._autorestapi.read_file("_patch.py")
79
+ "_patch.py", self._autorestapi.read_file("_patch.py")
78
80
  )
79
81
 
80
82
  self._autorestapi.write_file(
81
- Path("models.py"),
82
- self.serialize_multiapi_models()
83
+ Path("models.py"), self.serialize_multiapi_models()
83
84
  )
84
85
 
85
86
  self._autorestapi.write_file(Path("py.typed"), "# Marker file for PEP 561.")
86
87
 
87
-
88
88
  def serialize_multiapi_init(self) -> str:
89
89
  template = self.env.get_template("multiapi_init.py.jinja2")
90
90
  return template.render(
91
91
  service_client_filename=self.service_client_filename,
92
92
  client_name=self.conf["client_name"],
93
- async_mode=self.async_mode
93
+ async_mode=self.async_mode,
94
94
  )
95
95
 
96
96
  def serialize_multiapi_client(self) -> str:
@@ -28,8 +28,8 @@ def __init__(
28
28
  # --------------------------------------------------------------------------
29
29
  {{ imports }}
30
30
 
31
- class {{ code_model.service_client.name }}Configuration(Configuration):
32
- """Configuration for {{ code_model.service_client.name }}.
31
+ class {{ code_model.client.name }}Configuration(Configuration):
32
+ """Configuration for {{ code_model.client.name }}.
33
33
 
34
34
  Note that all parameters used to create this instance are saved as instance
35
35
  attributes.
@@ -52,7 +52,7 @@ class {{ code_model.service_client.name }}Configuration(Configuration):
52
52
  raise ValueError("Parameter '{{ parameter.name }}' must not be None.")
53
53
  {% endif %}
54
54
  {% endfor %}
55
- super({{ code_model.service_client.name }}Configuration, self).__init__(**kwargs)
55
+ super({{ code_model.client.name }}Configuration, self).__init__(**kwargs)
56
56
 
57
57
  {% for parameter in code_model.global_parameters.parameters %}
58
58
  self.{{ parameter.name }} = {{ parameter.name }}
@@ -6,8 +6,8 @@
6
6
  # Changes may cause incorrect behavior and will be lost if the code is regenerated.
7
7
  # --------------------------------------------------------------------------
8
8
 
9
- from .{{ code_model.service_client.filename }} import {{ code_model.service_client.name }}
10
- __all__ = ['{{ code_model.service_client.name }}']
9
+ from .{{ code_model.client.filename }} import {{ code_model.client.name }}
10
+ __all__ = ['{{ code_model.client.name }}']
11
11
  {% if not async_mode %}
12
12
 
13
13
  try:
@@ -14,7 +14,7 @@ from msrest import Serializer, Deserializer
14
14
  {% endif %}
15
15
 
16
16
 
17
- class {{ code_model.service_client.name }}OperationsMixin(object):
17
+ class {{ code_model.client.name }}OperationsMixin(object):
18
18
  {% for mixin_operation in code_model.operation_mixin_group.mixin_operations %}
19
19
 
20
20
  {{ mixin_operation.signature(async_mode) | indent }} {{ mixin_operation.description(async_mode) | indent(8) }}
@@ -22,7 +22,7 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
22
22
  {% for api in mixin_operation.available_apis|sort %}
23
23
  {% set if_statement = "if" if loop.first else "elif" %}
24
24
  {{ if_statement }} api_version == '{{ code_model.mod_to_api_version[api] }}':
25
- from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.service_client.name }}OperationsMixin as OperationClass
25
+ from {{ ".." if async_mode else "." }}{{ api }}{{ ".aio" if async_mode else "" }}.operations import {{ code_model.client.name }}OperationsMixin as OperationClass
26
26
  {% endfor %}
27
27
  else:
28
28
  raise ValueError("API version {} does not have operation '{{ mixin_operation.name }}'".format(api_version))
@@ -30,9 +30,9 @@ class {{ code_model.service_client.name }}OperationsMixin(object):
30
30
  mixin_instance._client = self._client
31
31
  mixin_instance._config = self._config
32
32
  mixin_instance._serialize = Serializer(self._models_dict(api_version))
33
- {% if not code_model.service_client.client_side_validation %}
33
+ {% if not code_model.client.client_side_validation %}
34
34
  mixin_instance._serialize.client_side_validation = False
35
35
  {% endif %}
36
36
  mixin_instance._deserialize = Deserializer(self._models_dict(api_version))
37
- return {{ "await " if mixin_operation.coroutine(async_mode) }}mixin_instance.{{ mixin_operation.name }}({{ mixin_operation.call }}{{ ", **kwargs" if mixin_operation.call else "**kwargs" }})
37
+ return {{ "await " if mixin_operation.coroutine(async_mode) }}mixin_instance.{{ mixin_operation.name }}({{ mixin_operation.call(async_mode) }})
38
38
  {% endfor %}
@@ -43,8 +43,8 @@ class _SDKClient(object):
43
43
  """
44
44
  pass
45
45
 
46
- class {{ code_model.service_client.name }}({% if code_model.operation_mixin_group.mixin_operations %}{{ code_model.service_client.name }}OperationsMixin, {% endif %}MultiApiClientMixin, _SDKClient):
47
- """{{ code_model.service_client.description }}
46
+ class {{ code_model.client.name }}({% if code_model.operation_mixin_group.mixin_operations %}{{ code_model.client.name }}OperationsMixin, {% endif %}MultiApiClientMixin, _SDKClient):
47
+ """{{ code_model.client.description }}
48
48
 
49
49
  This ready contains multiple API versions, to help you deal with all of the Azure clouds
50
50
  (Azure Stack, Azure Government, Azure China, etc.).
@@ -64,13 +64,13 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
64
64
  :param {{ parameter.name }}: {{ parameter.description(async_mode) }}
65
65
  :type {{ parameter.name }}: {{ parameter.docstring_type(async_mode) }}
66
66
  {% endfor %}
67
- {% if code_model.service_client.has_lro_operations %}
67
+ {% if code_model.client.has_lro_operations %}
68
68
  :keyword int polling_interval: Default waiting time between two polls for LRO operations if no Retry-After header is present.
69
69
  {% endif %}
70
70
  """
71
71
 
72
72
  DEFAULT_API_VERSION = '{{ code_model.mod_to_api_version[code_model.default_api_version] }}'
73
- _PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.service_client.name }}"
73
+ _PROFILE_TAG = "{{ code_model.module_name }}.{{ code_model.client.name }}"
74
74
  LATEST_PROFILE = ProfileDefinition({
75
75
  _PROFILE_TAG: {
76
76
  None: DEFAULT_API_VERSION,
@@ -82,8 +82,8 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
82
82
  )
83
83
 
84
84
  {{ method_signature()|indent }}
85
- {% if not code_model.service_client.host_value %}
86
- {% for parameterized_host_template, api_versions in code_model.service_client.parameterized_host_template_to_api_version|dictsort %}
85
+ {% if not code_model.client.host_value %}
86
+ {% for parameterized_host_template, api_versions in code_model.client.parameterized_host_template_to_api_version|dictsort %}
87
87
  {% set if_statement = "if" if loop.first else "elif" %}
88
88
  {{ if_statement ~ " api_version == '" ~ api_versions|join("' or api_version == '") ~ "'" }}:
89
89
  base_url = {{ parameterized_host_template }}
@@ -91,9 +91,9 @@ class {{ code_model.service_client.name }}({% if code_model.operation_mixin_grou
91
91
  else:
92
92
  raise ValueError("API version {} is not available".format(api_version))
93
93
  {% endif %}
94
- self._config = {{ code_model.service_client.name }}Configuration({{ code_model.global_parameters.call }}{{ ", " if code_model.global_parameters.call }}**kwargs)
95
- self._client = {{ async_prefix }}{{ code_model.service_client.pipeline_client }}(base_url=base_url, config=self._config, **kwargs)
96
- super({{ code_model.service_client.name }}, self).__init__(
94
+ 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)
96
+ super({{ code_model.client.name }}, self).__init__(
97
97
  api_version=api_version,
98
98
  profile=profile
99
99
  )
@@ -12,14 +12,14 @@ _LOGGER = logging.getLogger(__name__)
12
12
  def _sync_or_async(async_mode: bool) -> str:
13
13
  return "async" if async_mode else "sync"
14
14
 
15
+
15
16
  def _get_default_api_version_from_list(
16
17
  mod_to_api_version: Dict[str, str],
17
18
  api_versions_list: List[str],
18
19
  preview_mode: bool,
19
- user_specified_default_api: Optional[str]
20
+ user_specified_default_api: Optional[str],
20
21
  ) -> str:
21
- """Get the floating latest, from a random list of API versions.
22
- """
22
+ """Get the floating latest, from a random list of API versions."""
23
23
 
24
24
  # I need user_specified_default_api to be v2019_06_07_preview shaped if it exists, let's be smart
25
25
  # and change it automatically so I can take both syntax as input
@@ -0,0 +1,202 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ from typing import Tuple
7
+ from pathlib import Path
8
+ import os
9
+ import shutil
10
+ from venv import EnvBuilder
11
+ import black
12
+ from .venvtools import ExtendedEnvBuilder, python_run
13
+
14
+ from .. import Plugin
15
+
16
+ _BLACK_MODE = black.Mode()
17
+ _BLACK_MODE.line_length = 120
18
+
19
+
20
+ def format_file(file: Path, file_content: str) -> str:
21
+ if not file.suffix == ".py":
22
+ return file_content
23
+ try:
24
+ file_content = black.format_file_contents(
25
+ file_content, fast=True, mode=_BLACK_MODE
26
+ )
27
+ except black.NothingChanged:
28
+ pass
29
+ return file_content
30
+
31
+
32
+ class PostProcessPlugin(Plugin):
33
+ def __init__(self, autorestapi):
34
+ super().__init__(autorestapi)
35
+ output_folder_uri = self._autorestapi.get_value("outputFolderUri")
36
+ if output_folder_uri.startswith("file:"):
37
+ output_folder_uri = output_folder_uri[5:]
38
+ if os.name == "nt" and output_folder_uri.startswith("///"):
39
+ output_folder_uri = output_folder_uri[3:]
40
+ self.output_folder = Path(output_folder_uri) # path to where the setup.py is
41
+ self.setup_venv()
42
+
43
+ # set up the venv
44
+ # base folder is where the code starts, i.e. where we
45
+ self.base_folder, self.namespace = self.get_namespace(self.output_folder, "")
46
+
47
+ def setup_venv(self):
48
+ venv_path = self.output_folder / Path(".temp_folder") / Path("temp_venv")
49
+
50
+ if venv_path.exists():
51
+ env_builder = EnvBuilder(with_pip=True)
52
+ self.venv_context = env_builder.ensure_directories(venv_path)
53
+ else:
54
+ env_builder = ExtendedEnvBuilder(with_pip=True)
55
+ env_builder.create(venv_path)
56
+ self.venv_context = env_builder.context
57
+ python_run(
58
+ self.venv_context,
59
+ "pip",
60
+ ["install", "-e", str(self.output_folder)],
61
+ directory=self.output_folder,
62
+ )
63
+
64
+ def get_namespace(self, dir: Path, namespace: str) -> Tuple[Path, str]:
65
+ try:
66
+ init_file = next(d for d in dir.iterdir() if d.name == "__init__.py")
67
+ # we don't care about pkgutil inits, we skip over them
68
+ file_content = self._autorestapi.read_file(
69
+ init_file.relative_to(self.output_folder)
70
+ )
71
+ if not "pkgutil" in file_content:
72
+ return dir, namespace
73
+ except StopIteration:
74
+ pass
75
+
76
+ try:
77
+ # first, see if we can get a folder that has the same name as the current output folder
78
+ start = self.output_folder.stem.split("-")[0]
79
+ next_dir = next(d for d in dir.iterdir() if d.is_dir() and d.name == start)
80
+ except StopIteration:
81
+ invalid_start_chars = [".", "_"]
82
+ invalid_dirs = [
83
+ "swagger",
84
+ "out",
85
+ "tests",
86
+ "samples",
87
+ ]
88
+
89
+ next_dir = next(
90
+ d
91
+ for d in dir.iterdir()
92
+ if d.is_dir()
93
+ and not str(d).endswith("egg-info")
94
+ and d.name[0] not in invalid_start_chars
95
+ and d.name not in invalid_dirs
96
+ )
97
+
98
+ namespace = f"{namespace}.{next_dir.name}" if namespace else next_dir.name
99
+ return self.get_namespace(next_dir, namespace)
100
+
101
+ def process(self) -> bool:
102
+ folders = [
103
+ f
104
+ for f in self.base_folder.glob("**/*")
105
+ if f.is_dir() and not f.stem.startswith("__")
106
+ ]
107
+ # will always have the root
108
+ self.fix_imports_in_init(
109
+ generated_file_name="_client",
110
+ folder_path=self.base_folder,
111
+ namespace=self.namespace,
112
+ )
113
+ try:
114
+ aio_folder = next(f for f in folders if f.stem == "aio")
115
+ self.fix_imports_in_init(
116
+ generated_file_name="_client",
117
+ folder_path=aio_folder,
118
+ namespace=f"{self.namespace}.aio",
119
+ )
120
+ except StopIteration:
121
+ pass
122
+
123
+ try:
124
+ models_folder = next(f for f in folders if f.stem == "models")
125
+ self.fix_imports_in_init(
126
+ generated_file_name="_models",
127
+ folder_path=models_folder,
128
+ namespace=f"{self.namespace}.models",
129
+ )
130
+ except StopIteration:
131
+ pass
132
+ operations_folders = [
133
+ f for f in folders if f.stem in ["operations", "_operations"]
134
+ ]
135
+ for operations_folder in operations_folders:
136
+ aio = ".aio" if operations_folder.parent.stem == "aio" else ""
137
+ self.fix_imports_in_init(
138
+ generated_file_name="_operations",
139
+ folder_path=operations_folder,
140
+ namespace=f"{self.namespace}{aio}.{operations_folder.stem}",
141
+ )
142
+ shutil.rmtree(f"{str(self.output_folder)}/.temp_folder")
143
+ return True
144
+
145
+ def fix_imports_in_init(
146
+ self, generated_file_name: str, folder_path: Path, namespace: str
147
+ ) -> None:
148
+ customized_objects_str = python_run(
149
+ self.venv_context,
150
+ command=[namespace, str(self.output_folder)],
151
+ module="get_all",
152
+ )
153
+
154
+ if not customized_objects_str:
155
+ return
156
+ customized_objects = {
157
+ k: None for k in customized_objects_str.split(",")
158
+ }.keys() # filter out duplicates
159
+ file = (folder_path / "__init__.py").relative_to(self.output_folder)
160
+ file_content = self._autorestapi.read_file(file)
161
+ added_objs = []
162
+ for obj in customized_objects:
163
+ if f" import {obj}\n" in file_content:
164
+ # means we're overriding a generated model
165
+ file_content = file_content.replace(
166
+ f"from .{generated_file_name} import {obj}\n",
167
+ f"from ._patch import {obj}\n",
168
+ )
169
+ else:
170
+ added_objs.append(obj)
171
+ file_content = file_content.replace(
172
+ "try:\n from ._patch import __all__ as _patch_all\n "
173
+ "from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import"
174
+ "\nexcept ImportError:\n _patch_all = []",
175
+ "",
176
+ )
177
+ file_content = file_content.replace(
178
+ "from ._patch import __all__ as _patch_all", ""
179
+ )
180
+ file_content = file_content.replace(
181
+ "from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import\n",
182
+ "",
183
+ )
184
+ file_content = file_content.replace(
185
+ "__all__.extend([p for p in _patch_all if p not in __all__])", ""
186
+ )
187
+ if added_objs:
188
+ # add import
189
+ patch_sdk_import = "from ._patch import patch_sdk as _patch_sdk"
190
+ imports = "\n".join([f"from ._patch import {obj}" for obj in added_objs])
191
+ if imports:
192
+ replacement = f"{imports}\n{patch_sdk_import}"
193
+ else:
194
+ replacement = patch_sdk_import
195
+ file_content = file_content.replace(patch_sdk_import, replacement)
196
+ # add to __all__
197
+ added_objs_all = "\n".join([f' "{obj}",' for obj in added_objs]) + "\n"
198
+ file_content = file_content.replace(
199
+ "__all__ = [", f"__all__ = [\n{added_objs_all}", 1
200
+ )
201
+ formatted_file = format_file(file, file_content)
202
+ self._autorestapi.write_file(file, formatted_file)