@typespec/http-client-python 0.5.1 → 0.6.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 (143) hide show
  1. package/dist/emitter/code-model.d.ts.map +1 -1
  2. package/dist/emitter/code-model.js +10 -7
  3. package/dist/emitter/code-model.js.map +1 -1
  4. package/dist/emitter/emitter.d.ts.map +1 -1
  5. package/dist/emitter/emitter.js +4 -1
  6. package/dist/emitter/emitter.js.map +1 -1
  7. package/dist/emitter/lib.d.ts +1 -0
  8. package/dist/emitter/lib.d.ts.map +1 -1
  9. package/dist/emitter/lib.js +1 -0
  10. package/dist/emitter/lib.js.map +1 -1
  11. package/dist/emitter/types.d.ts.map +1 -1
  12. package/dist/emitter/types.js +7 -4
  13. package/dist/emitter/types.js.map +1 -1
  14. package/dist/emitter/utils.d.ts +1 -0
  15. package/dist/emitter/utils.d.ts.map +1 -1
  16. package/dist/emitter/utils.js +19 -0
  17. package/dist/emitter/utils.js.map +1 -1
  18. package/emitter/src/code-model.ts +16 -6
  19. package/emitter/src/emitter.ts +4 -1
  20. package/emitter/src/lib.ts +4 -0
  21. package/emitter/src/types.ts +16 -4
  22. package/emitter/src/utils.ts +27 -0
  23. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  24. package/eng/scripts/ci/regenerate.ts +7 -0
  25. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  26. package/generator/build/lib/pygen/black.py +2 -2
  27. package/generator/build/lib/pygen/codegen/__init__.py +2 -0
  28. package/generator/build/lib/pygen/codegen/_utils.py +4 -0
  29. package/generator/build/lib/pygen/codegen/models/base.py +2 -3
  30. package/generator/build/lib/pygen/codegen/models/base_builder.py +5 -3
  31. package/generator/build/lib/pygen/codegen/models/client.py +28 -19
  32. package/generator/build/lib/pygen/codegen/models/code_model.py +200 -33
  33. package/generator/build/lib/pygen/codegen/models/combined_type.py +8 -5
  34. package/generator/build/lib/pygen/codegen/models/constant_type.py +2 -3
  35. package/generator/build/lib/pygen/codegen/models/credential_types.py +1 -2
  36. package/generator/build/lib/pygen/codegen/models/dictionary_type.py +2 -3
  37. package/generator/build/lib/pygen/codegen/models/enum_type.py +47 -24
  38. package/generator/build/lib/pygen/codegen/models/imports.py +14 -12
  39. package/generator/build/lib/pygen/codegen/models/list_type.py +2 -3
  40. package/generator/build/lib/pygen/codegen/models/lro_operation.py +8 -4
  41. package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +2 -2
  42. package/generator/build/lib/pygen/codegen/models/model_type.py +34 -19
  43. package/generator/build/lib/pygen/codegen/models/operation.py +66 -29
  44. package/generator/build/lib/pygen/codegen/models/operation_group.py +56 -11
  45. package/generator/build/lib/pygen/codegen/models/paging_operation.py +9 -6
  46. package/generator/build/lib/pygen/codegen/models/parameter.py +10 -10
  47. package/generator/build/lib/pygen/codegen/models/parameter_list.py +7 -7
  48. package/generator/build/lib/pygen/codegen/models/primitive_types.py +23 -43
  49. package/generator/build/lib/pygen/codegen/models/property.py +7 -7
  50. package/generator/build/lib/pygen/codegen/models/request_builder.py +9 -15
  51. package/generator/build/lib/pygen/codegen/models/response.py +6 -8
  52. package/generator/build/lib/pygen/codegen/models/utils.py +11 -0
  53. package/generator/build/lib/pygen/codegen/serializers/__init__.py +201 -242
  54. package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +19 -1
  55. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +53 -35
  56. package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +9 -5
  57. package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +17 -3
  58. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +26 -14
  59. package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +26 -8
  60. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +9 -4
  61. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +62 -22
  62. package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +19 -16
  63. package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +5 -10
  64. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +10 -7
  65. package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +10 -1
  66. package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +7 -10
  67. package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +24 -28
  68. package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +6 -1
  69. package/generator/build/lib/pygen/codegen/serializers/utils.py +1 -15
  70. package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +1 -1
  71. package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +1 -1
  72. package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +1 -1
  73. package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +1 -1
  74. package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +1 -1
  75. package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +2 -4
  76. package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +3 -3
  77. package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +2 -2
  78. package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +4 -4
  79. package/generator/build/lib/pygen/preprocess/__init__.py +0 -4
  80. package/generator/dev_requirements.txt +2 -2
  81. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  82. package/generator/pygen/black.py +2 -2
  83. package/generator/pygen/codegen/__init__.py +2 -0
  84. package/generator/pygen/codegen/_utils.py +4 -0
  85. package/generator/pygen/codegen/models/base.py +2 -3
  86. package/generator/pygen/codegen/models/base_builder.py +5 -3
  87. package/generator/pygen/codegen/models/client.py +28 -19
  88. package/generator/pygen/codegen/models/code_model.py +200 -33
  89. package/generator/pygen/codegen/models/combined_type.py +8 -5
  90. package/generator/pygen/codegen/models/constant_type.py +2 -3
  91. package/generator/pygen/codegen/models/credential_types.py +1 -2
  92. package/generator/pygen/codegen/models/dictionary_type.py +2 -3
  93. package/generator/pygen/codegen/models/enum_type.py +47 -24
  94. package/generator/pygen/codegen/models/imports.py +14 -12
  95. package/generator/pygen/codegen/models/list_type.py +2 -3
  96. package/generator/pygen/codegen/models/lro_operation.py +8 -4
  97. package/generator/pygen/codegen/models/lro_paging_operation.py +2 -2
  98. package/generator/pygen/codegen/models/model_type.py +34 -19
  99. package/generator/pygen/codegen/models/operation.py +66 -29
  100. package/generator/pygen/codegen/models/operation_group.py +56 -11
  101. package/generator/pygen/codegen/models/paging_operation.py +9 -6
  102. package/generator/pygen/codegen/models/parameter.py +10 -10
  103. package/generator/pygen/codegen/models/parameter_list.py +7 -7
  104. package/generator/pygen/codegen/models/primitive_types.py +23 -43
  105. package/generator/pygen/codegen/models/property.py +7 -7
  106. package/generator/pygen/codegen/models/request_builder.py +9 -15
  107. package/generator/pygen/codegen/models/response.py +6 -8
  108. package/generator/pygen/codegen/models/utils.py +11 -0
  109. package/generator/pygen/codegen/serializers/__init__.py +201 -242
  110. package/generator/pygen/codegen/serializers/base_serializer.py +19 -1
  111. package/generator/pygen/codegen/serializers/builder_serializer.py +53 -35
  112. package/generator/pygen/codegen/serializers/client_serializer.py +9 -5
  113. package/generator/pygen/codegen/serializers/enum_serializer.py +17 -3
  114. package/generator/pygen/codegen/serializers/general_serializer.py +26 -14
  115. package/generator/pygen/codegen/serializers/metadata_serializer.py +26 -8
  116. package/generator/pygen/codegen/serializers/model_init_serializer.py +9 -4
  117. package/generator/pygen/codegen/serializers/model_serializer.py +62 -22
  118. package/generator/pygen/codegen/serializers/operation_groups_serializer.py +19 -16
  119. package/generator/pygen/codegen/serializers/operations_init_serializer.py +5 -10
  120. package/generator/pygen/codegen/serializers/parameter_serializer.py +10 -7
  121. package/generator/pygen/codegen/serializers/request_builders_serializer.py +10 -1
  122. package/generator/pygen/codegen/serializers/sample_serializer.py +7 -10
  123. package/generator/pygen/codegen/serializers/test_serializer.py +24 -28
  124. package/generator/pygen/codegen/serializers/types_serializer.py +6 -1
  125. package/generator/pygen/codegen/serializers/utils.py +1 -15
  126. package/generator/pygen/codegen/templates/client_container.py.jinja2 +1 -1
  127. package/generator/pygen/codegen/templates/config_container.py.jinja2 +1 -1
  128. package/generator/pygen/codegen/templates/enum_container.py.jinja2 +1 -1
  129. package/generator/pygen/codegen/templates/init.py.jinja2 +1 -1
  130. package/generator/pygen/codegen/templates/model_container.py.jinja2 +1 -1
  131. package/generator/pygen/codegen/templates/operations_folder_init.py.jinja2 +2 -4
  132. package/generator/pygen/codegen/templates/test.py.jinja2 +3 -3
  133. package/generator/pygen/codegen/templates/testpreparer.py.jinja2 +2 -2
  134. package/generator/pygen/codegen/templates/vendor.py.jinja2 +4 -4
  135. package/generator/pygen/preprocess/__init__.py +0 -4
  136. package/generator/test/azure/mock_api_tests/asynctests/test_client_namespace_async.py +31 -0
  137. package/generator/test/azure/mock_api_tests/test_client_namespace.py +29 -0
  138. package/generator/test/azure/mock_api_tests/test_resiliency_srv_driven_async.py +1 -0
  139. package/generator/test/azure/requirements.txt +3 -2
  140. package/generator/test/azure/tox.ini +2 -2
  141. package/generator/test/unbranded/requirements.txt +2 -2
  142. package/generator/test/unbranded/tox.ini +2 -2
  143. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
- from typing import List, Dict, Any, Set, Union, Literal
6
+ from typing import List, Dict, Any, Set, Union, Literal, Optional, cast
7
7
 
8
8
  from .base import BaseType
9
9
  from .enum_type import EnumType
@@ -11,12 +11,40 @@ from .model_type import ModelType, UsageFlags
11
11
  from .combined_type import CombinedType
12
12
  from .client import Client
13
13
  from .request_builder import RequestBuilder, OverloadedRequestBuilder
14
+ from .operation_group import OperationGroup
15
+ from .utils import NamespaceType
14
16
 
15
17
 
16
18
  def _is_legacy(options) -> bool:
17
19
  return not (options.get("version_tolerant") or options.get("low_level_client"))
18
20
 
19
21
 
22
+ def get_all_operation_groups_recursively(clients: List[Client]) -> List[OperationGroup]:
23
+ operation_groups = []
24
+ queue = []
25
+ for client in clients:
26
+ queue.extend(client.operation_groups)
27
+ while queue:
28
+ operation_groups.append(queue.pop(0))
29
+ if operation_groups[-1].operation_groups:
30
+ queue.extend(operation_groups[-1].operation_groups)
31
+ return operation_groups
32
+
33
+
34
+ class ClientNamespaceType:
35
+ def __init__(
36
+ self,
37
+ clients: Optional[List[Client]] = None,
38
+ models: Optional[List[ModelType]] = None,
39
+ enums: Optional[List[EnumType]] = None,
40
+ operation_groups: Optional[List[OperationGroup]] = None,
41
+ ):
42
+ self.clients = clients or []
43
+ self.models = models or []
44
+ self.enums = enums or []
45
+ self.operation_groups = operation_groups or []
46
+
47
+
20
48
  class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-instance-attributes
21
49
  """Top level code model
22
50
 
@@ -44,8 +72,6 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
44
72
  self,
45
73
  yaml_data: Dict[str, Any],
46
74
  options: Dict[str, Any],
47
- *,
48
- is_subnamespace: bool = False,
49
75
  ) -> None:
50
76
  self.yaml_data = yaml_data
51
77
  self.options = options
@@ -59,18 +85,113 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
59
85
  self.clients: List[Client] = [
60
86
  Client.from_yaml(client_yaml_data, self) for client_yaml_data in yaml_data["clients"]
61
87
  ]
62
- self.subnamespace_to_clients: Dict[str, List[Client]] = {
63
- subnamespace: [Client.from_yaml(client_yaml, self, is_subclient=True) for client_yaml in client_yamls]
64
- for subnamespace, client_yamls in yaml_data.get("subnamespaceToClients", {}).items()
65
- }
66
88
  if self.options["models_mode"] and self.model_types:
67
89
  self.sort_model_types()
68
- self.is_subnamespace = is_subnamespace
69
90
  self.named_unions: List[CombinedType] = [
70
91
  t for t in self.types_map.values() if isinstance(t, CombinedType) and t.name
71
92
  ]
72
93
  self.cross_language_package_id = self.yaml_data.get("crossLanguagePackageId")
73
94
  self.for_test: bool = False
95
+ # key is typespec namespace, value is models/clients/opeartion_groups/enums cache in the namespace
96
+ self._client_namespace_types: Dict[str, ClientNamespaceType] = {}
97
+ self.has_subnamespace = False
98
+ self._operations_folder_name: Dict[str, str] = {}
99
+ self._relative_import_path: Dict[str, str] = {}
100
+
101
+ @staticmethod
102
+ def get_imported_namespace_for_client(imported_namespace: str, async_mode: bool = False) -> str:
103
+ return imported_namespace + (".aio" if async_mode else "")
104
+
105
+ @staticmethod
106
+ def get_imported_namespace_for_model(imported_namespace: str) -> str:
107
+ return imported_namespace + ".models"
108
+
109
+ def get_imported_namespace_for_operation(self, imported_namespace: str, async_mode: bool = False) -> str:
110
+ module_namespace = f".{self.operations_folder_name(imported_namespace)}"
111
+ return self.get_imported_namespace_for_client(imported_namespace, async_mode) + module_namespace
112
+
113
+ # | serialize_namespace | imported_namespace | relative_import_path |
114
+ # |----------------------|----------------------|----------------------|
115
+ # |azure.test.operations | azure.test.operations| . |
116
+ # |azure.test.operations | azure.test | .. |
117
+ # |azure.test.operations | azure.test.subtest | ..subtest |
118
+ # |azure.test.operations | azure | ... |
119
+ # |azure.test.aio.operations | azure.test | ... |
120
+ # |azure.test.subtest.aio.operations|azure.test | .... |
121
+ # |azure.test |azure.test.subtest | .subtest |
122
+ def get_relative_import_path(
123
+ self,
124
+ serialize_namespace: str,
125
+ imported_namespace: Optional[str] = None,
126
+ module_name: Optional[str] = None,
127
+ ) -> str:
128
+ if imported_namespace is None:
129
+ imported_namespace = self.namespace
130
+
131
+ key = f"{serialize_namespace}-{imported_namespace}"
132
+ if key not in self._relative_import_path:
133
+ idx = 0
134
+ serialize_namespace_split = serialize_namespace.split(".")
135
+ imported_namespace_split = cast(str, imported_namespace).split(".")
136
+ while idx < min(len(serialize_namespace_split), len(imported_namespace_split)):
137
+ if serialize_namespace_split[idx] != imported_namespace_split[idx]:
138
+ break
139
+ idx += 1
140
+ self._relative_import_path[key] = "." * (len(serialize_namespace_split[idx:]) + 1) + ".".join(
141
+ imported_namespace_split[idx:]
142
+ )
143
+ result = self._relative_import_path[key]
144
+ if module_name is None:
145
+ return result
146
+ return f"{result}{module_name}" if result.endswith(".") else f"{result}.{module_name}"
147
+
148
+ @property
149
+ def need_unique_model_alias(self) -> bool:
150
+ return self.has_subnamespace and self.options["enable_typespec_namespace"]
151
+
152
+ def get_unique_models_alias(self, serialize_namespace: str, imported_namespace: str) -> str:
153
+ if not self.need_unique_model_alias:
154
+ return "_models"
155
+ relative_path = self.get_relative_import_path(
156
+ serialize_namespace, self.get_imported_namespace_for_model(imported_namespace)
157
+ )
158
+ dot_num = max(relative_path.count(".") - 1, 0)
159
+ parts = [""] + ([p for p in relative_path.split(".") if p] or ["models"])
160
+ return "_".join(parts) + (str(dot_num) if dot_num > 0 else "")
161
+
162
+ @property
163
+ def client_namespace_types(self) -> Dict[str, ClientNamespaceType]:
164
+ if not self._client_namespace_types:
165
+ # calculate client namespace types for each kind of client namespace
166
+ for client in self.clients:
167
+ if client.client_namespace not in self._client_namespace_types:
168
+ self._client_namespace_types[client.client_namespace] = ClientNamespaceType()
169
+ self._client_namespace_types[client.client_namespace].clients.append(client)
170
+ for model in self.model_types:
171
+ if model.client_namespace not in self._client_namespace_types:
172
+ self._client_namespace_types[model.client_namespace] = ClientNamespaceType()
173
+ self._client_namespace_types[model.client_namespace].models.append(model)
174
+ for enum in self.enums:
175
+ if enum.client_namespace not in self._client_namespace_types:
176
+ self._client_namespace_types[enum.client_namespace] = ClientNamespaceType()
177
+ self._client_namespace_types[enum.client_namespace].enums.append(enum)
178
+ for operation_group in get_all_operation_groups_recursively(self.clients):
179
+ if operation_group.client_namespace not in self._client_namespace_types:
180
+ self._client_namespace_types[operation_group.client_namespace] = ClientNamespaceType()
181
+ self._client_namespace_types[operation_group.client_namespace].operation_groups.append(operation_group)
182
+
183
+ # here we can check and record whether there are multi kinds of client namespace
184
+ if len(self._client_namespace_types.keys()) > 1:
185
+ self.has_subnamespace = True
186
+
187
+ # insert namespace to make sure it is continuous(e.g. ("", "azure", "azure.mgmt", "azure.mgmt.service"))
188
+ longest_namespace = sorted(self._client_namespace_types.keys())[-1]
189
+ namespace_parts = longest_namespace.split(".")
190
+ for idx in range(len(namespace_parts) + 1):
191
+ namespace = ".".join(namespace_parts[:idx])
192
+ if namespace not in self._client_namespace_types:
193
+ self._client_namespace_types[namespace] = ClientNamespaceType()
194
+ return self._client_namespace_types
74
195
 
75
196
  @property
76
197
  def has_form_data(self) -> bool:
@@ -80,17 +201,17 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
80
201
  def has_etag(self) -> bool:
81
202
  return any(client.has_etag for client in self.clients)
82
203
 
204
+ @staticmethod
205
+ def clients_has_operations(clients: List[Client]) -> bool:
206
+ return any(c for c in clients if c.has_operations)
207
+
83
208
  @property
84
209
  def has_operations(self) -> bool:
85
- if any(c for c in self.clients if c.has_operations):
86
- return True
87
- return any(c for clients in self.subnamespace_to_clients.values() for c in clients if c.has_operations)
210
+ return self.clients_has_operations(self.clients)
88
211
 
89
212
  @property
90
213
  def has_non_abstract_operations(self) -> bool:
91
- return any(c for c in self.clients if c.has_non_abstract_operations) or any(
92
- c for cs in self.subnamespace_to_clients.values() for c in cs if c.has_non_abstract_operations
93
- )
214
+ return any(c for c in self.clients if c.has_non_abstract_operations)
94
215
 
95
216
  def lookup_request_builder(self, request_builder_id: int) -> Union[RequestBuilder, OverloadedRequestBuilder]:
96
217
  """Find the request builder based off of id"""
@@ -114,31 +235,73 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
114
235
  def client_filename(self) -> str:
115
236
  return self.clients[0].filename
116
237
 
117
- def need_vendored_code(self, async_mode: bool) -> bool:
118
- """Whether we need to vendor code in the _vendor.py file for this SDK"""
119
- if self.has_abstract_operations:
120
- return True
121
- if async_mode:
122
- return self.need_mixin_abc
123
- return self.need_mixin_abc or self.has_etag or self.has_form_data
238
+ def get_clients(self, client_namespace: str) -> List[Client]:
239
+ """Get all clients in specific namespace"""
240
+ return self.client_namespace_types.get(client_namespace, ClientNamespaceType()).clients
124
241
 
125
- @property
126
- def need_mixin_abc(self) -> bool:
127
- return any(c for c in self.clients if c.has_mixin)
242
+ def is_top_namespace(self, client_namespace: str) -> bool:
243
+ """Whether the namespace is the top namespace. For example, a package named 'azure-mgmt-service',
244
+ 'azure.mgmt.service' is the top namespace.
245
+ """
246
+ return client_namespace == self.namespace
247
+
248
+ def need_vendored_code(self, async_mode: bool, client_namespace: str) -> bool:
249
+ """Whether we need to vendor code in the _vendor.py in specific namespace"""
250
+ return (
251
+ self.need_vendored_form_data(async_mode, client_namespace)
252
+ or self.need_vendored_etag(client_namespace)
253
+ or self.need_vendored_abstract(client_namespace)
254
+ or self.need_vendored_mixin(client_namespace)
255
+ )
256
+
257
+ def need_vendored_form_data(self, async_mode: bool, client_namespace: str) -> bool:
258
+ return (
259
+ (not async_mode)
260
+ and self.is_top_namespace(client_namespace)
261
+ and self.has_form_data
262
+ and self.options["models_mode"] == "dpg"
263
+ )
264
+
265
+ def need_vendored_etag(self, client_namespace: str) -> bool:
266
+ return self.is_top_namespace(client_namespace) and self.has_etag
267
+
268
+ def need_vendored_abstract(self, client_namespace: str) -> bool:
269
+ return self.is_top_namespace(client_namespace) and self.has_abstract_operations
270
+
271
+ def need_vendored_mixin(self, client_namespace: str) -> bool:
272
+ return self.has_mixin(client_namespace)
273
+
274
+ def has_mixin(self, client_namespace: str) -> bool:
275
+ return any(c for c in self.get_clients(client_namespace) if c.has_mixin)
128
276
 
129
277
  @property
130
278
  def has_abstract_operations(self) -> bool:
131
279
  return any(c for c in self.clients if c.has_abstract_operations)
132
280
 
133
- @property
134
- def operations_folder_name(self) -> str:
281
+ def operations_folder_name(self, client_namespace: str) -> str:
135
282
  """Get the name of the operations folder that holds operations."""
136
- name = "operations"
137
- if self.options["version_tolerant"] and not any(
138
- og for client in self.clients for og in client.operation_groups if not og.is_mixin
139
- ):
140
- name = f"_{name}"
141
- return name
283
+ if client_namespace not in self._operations_folder_name:
284
+ name = "operations"
285
+ operation_groups = self.client_namespace_types.get(client_namespace, ClientNamespaceType()).operation_groups
286
+ if self.options["version_tolerant"] and all(og.is_mixin for og in operation_groups):
287
+ name = f"_{name}"
288
+ self._operations_folder_name[client_namespace] = name
289
+ return self._operations_folder_name[client_namespace]
290
+
291
+ def get_serialize_namespace(
292
+ self,
293
+ client_namespace: str,
294
+ async_mode: bool = False,
295
+ client_namespace_type: NamespaceType = NamespaceType.CLIENT,
296
+ ) -> str:
297
+ """calculate the namespace for serialization from client namespace"""
298
+ if client_namespace_type == NamespaceType.CLIENT:
299
+ return client_namespace + (".aio" if async_mode else "")
300
+ if client_namespace_type == NamespaceType.MODEL:
301
+ return client_namespace + ".models"
302
+
303
+ operations_folder_name = self.operations_folder_name(client_namespace)
304
+ return client_namespace + (".aio." if async_mode else ".") + operations_folder_name
142
305
 
143
306
  @property
144
307
  def description(self) -> str:
@@ -170,9 +333,13 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
170
333
  def model_types(self, val: List[ModelType]) -> None:
171
334
  self._model_types = val
172
335
 
336
+ @staticmethod
337
+ def get_public_model_types(models: List[ModelType]) -> List[ModelType]:
338
+ return [m for m in models if not m.internal and not m.base == "json"]
339
+
173
340
  @property
174
341
  def public_model_types(self) -> List[ModelType]:
175
- return [m for m in self.model_types if not m.internal and not m.base == "json"]
342
+ return self.get_public_model_types(self.model_types)
176
343
 
177
344
  @property
178
345
  def enums(self) -> List[EnumType]:
@@ -8,6 +8,7 @@ import re
8
8
  from .imports import FileImport, ImportType, TypingSection
9
9
  from .base import BaseType
10
10
  from .model_type import ModelType
11
+ from .utils import NamespaceType
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from .code_model import CodeModel
@@ -30,9 +31,9 @@ class CombinedType(BaseType):
30
31
  self.types = types # the types that this type is combining
31
32
  self.name = yaml_data.get("name")
32
33
  self._is_union_of_literals = all(i.type == "constant" for i in self.types)
34
+ self.client_namespace: str = self.yaml_data.get("clientNamespace", code_model.namespace)
33
35
 
34
- @property
35
- def serialization_type(self) -> str:
36
+ def serialization_type(self, **kwargs: Any) -> str:
36
37
  """The tag recognized by 'msrest' as a serialization/deserialization.
37
38
 
38
39
  'str', 'int', 'float', 'bool' or
@@ -45,7 +46,7 @@ class CombinedType(BaseType):
45
46
  """
46
47
  if not all(t for t in self.types if t.type == "constant"):
47
48
  raise ValueError("Shouldn't get serialization type of a combinedtype")
48
- return self.types[0].serialization_type
49
+ return self.types[0].serialization_type(**kwargs)
49
50
 
50
51
  @property
51
52
  def client_default_value(self) -> Any:
@@ -112,9 +113,11 @@ class CombinedType(BaseType):
112
113
 
113
114
  def imports(self, **kwargs: Any) -> FileImport:
114
115
  file_import = FileImport(self.code_model)
115
- if self.name and not kwargs.get("is_types_file"):
116
+ serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
117
+ serialize_namespace_type = kwargs.get("serialize_namespace_type")
118
+ if self.name and serialize_namespace_type != NamespaceType.TYPES_FILE:
116
119
  file_import.add_submodule_import(
117
- kwargs.pop("relative_path"),
120
+ self.code_model.get_relative_import_path(serialize_namespace),
118
121
  "_types",
119
122
  ImportType.LOCAL,
120
123
  TypingSection.TYPING,
@@ -56,14 +56,13 @@ class ConstantType(BaseType):
56
56
  f"Default value is {self.get_declaration()}.",
57
57
  )
58
58
 
59
- @property
60
- def serialization_type(self) -> str:
59
+ def serialization_type(self, **kwargs: Any) -> str:
61
60
  """Returns the serialization value for msrest.
62
61
 
63
62
  :return: The serialization value for msrest
64
63
  :rtype: str
65
64
  """
66
- return self.value_type.serialization_type
65
+ return self.value_type.serialization_type(**kwargs)
67
66
 
68
67
  def docstring_text(self, **kwargs: Any) -> str:
69
68
  return "constant"
@@ -131,8 +131,7 @@ class CredentialType(Generic[CredentialPolicyType], BaseType):
131
131
  def docstring_text(self, **kwargs: Any) -> str:
132
132
  return "credential"
133
133
 
134
- @property
135
- def serialization_type(self) -> str:
134
+ def serialization_type(self, **kwargs: Any) -> str:
136
135
  return self.docstring_type()
137
136
 
138
137
  @classmethod
@@ -34,14 +34,13 @@ class DictionaryType(BaseType):
34
34
  def encode(self) -> Optional[str]:
35
35
  return self.element_type.encode if hasattr(self.element_type, "encode") else None # type: ignore
36
36
 
37
- @property
38
- def serialization_type(self) -> str:
37
+ def serialization_type(self, **kwargs: Any) -> str:
39
38
  """Returns the serialization value for msrest.
40
39
 
41
40
  :return: The serialization value for msrest
42
41
  :rtype: str
43
42
  """
44
- return f"{{{self.element_type.serialization_type}}}"
43
+ return f"{{{self.element_type.serialization_type(**kwargs)}}}"
45
44
 
46
45
  def type_annotation(self, **kwargs: Any) -> str:
47
46
  """The python type used for type annotation
@@ -7,6 +7,8 @@ from typing import Any, Dict, List, TYPE_CHECKING, Optional, cast
7
7
 
8
8
  from .base import BaseType
9
9
  from .imports import FileImport, ImportType, TypingSection
10
+ from .utils import NamespaceType
11
+
10
12
 
11
13
  if TYPE_CHECKING:
12
14
  from .code_model import CodeModel
@@ -63,9 +65,8 @@ class EnumValue(BaseType):
63
65
  client_default_value_declaration=client_default_value_declaration,
64
66
  )
65
67
 
66
- @property
67
- def serialization_type(self) -> str:
68
- return self.value_type.serialization_type
68
+ def serialization_type(self, **kwargs: Any) -> str:
69
+ return self.value_type.serialization_type(**kwargs)
69
70
 
70
71
  @property
71
72
  def instance_check_template(self) -> str:
@@ -75,7 +76,17 @@ class EnumValue(BaseType):
75
76
  file_import = FileImport(self.code_model)
76
77
  file_import.merge(self.value_type.imports(**kwargs))
77
78
  file_import.add_submodule_import("typing", "Literal", ImportType.STDLIB, TypingSection.REGULAR)
78
- file_import.add_submodule_import("._enums", self.enum_type.name, ImportType.LOCAL, TypingSection.REGULAR)
79
+ serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
80
+ file_import.add_submodule_import(
81
+ self.code_model.get_relative_import_path(
82
+ serialize_namespace,
83
+ self.code_model.get_imported_namespace_for_model(self.enum_type.client_namespace),
84
+ module_name=self.code_model.enums_filename,
85
+ ),
86
+ self.enum_type.name,
87
+ ImportType.LOCAL,
88
+ TypingSection.REGULAR,
89
+ )
79
90
 
80
91
  return file_import
81
92
 
@@ -124,18 +135,18 @@ class EnumType(BaseType):
124
135
  self.value_type = value_type
125
136
  self.internal: bool = self.yaml_data.get("internal", False)
126
137
  self.cross_language_definition_id: Optional[str] = self.yaml_data.get("crossLanguageDefinitionId")
138
+ self.client_namespace: str = self.yaml_data.get("clientNamespace", code_model.namespace)
127
139
 
128
140
  def __lt__(self, other):
129
141
  return self.name.lower() < other.name.lower()
130
142
 
131
- @property
132
- def serialization_type(self) -> str:
143
+ def serialization_type(self, **kwargs: Any) -> str:
133
144
  """Returns the serialization value for msrest.
134
145
 
135
146
  :return: The serialization value for msrest
136
147
  :rtype: str
137
148
  """
138
- return self.value_type.serialization_type
149
+ return self.value_type.serialization_type(**kwargs)
139
150
 
140
151
  def description(self, *, is_operation_file: bool) -> str:
141
152
  possible_values = [self.get_declaration(v.value) for v in self.values]
@@ -160,7 +171,12 @@ class EnumType(BaseType):
160
171
  :rtype: str
161
172
  """
162
173
  if self.code_model.options["models_mode"]:
163
- module_name = "_models." if kwargs.get("need_module_name", True) else ""
174
+
175
+ module_name = ""
176
+ if kwargs.get("need_model_alias", True):
177
+ serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
178
+ model_alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
179
+ module_name = f"{model_alias}."
164
180
  file_name = f"{self.code_model.enums_filename}." if self.internal else ""
165
181
  model_name = module_name + file_name + self.name
166
182
  # we don't need quoted annotation in operation files, and need it in model folder files.
@@ -212,27 +228,34 @@ class EnumType(BaseType):
212
228
  )
213
229
 
214
230
  def imports(self, **kwargs: Any) -> FileImport:
215
- operation = kwargs.pop("operation", False)
216
231
  file_import = FileImport(self.code_model)
232
+ file_import.merge(self.value_type.imports(**kwargs))
217
233
  if self.code_model.options["models_mode"]:
218
234
  file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
219
- if not operation:
235
+
236
+ serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
237
+ relative_path = self.code_model.get_relative_import_path(serialize_namespace, self.client_namespace)
238
+ alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
239
+ serialize_namespace_type = kwargs.get("serialize_namespace_type")
240
+ called_by_property = kwargs.get("called_by_property", False)
241
+ if serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
220
242
  file_import.add_submodule_import(
221
- "..",
243
+ relative_path,
222
244
  "models",
223
245
  ImportType.LOCAL,
224
- TypingSection.TYPING,
225
- alias="_models",
246
+ alias=alias,
247
+ typing_section=TypingSection.REGULAR,
226
248
  )
227
- file_import.merge(self.value_type.imports(operation=operation, **kwargs))
228
- relative_path = kwargs.pop("relative_path", None)
229
- if self.code_model.options["models_mode"] and relative_path:
230
- # add import for enums in operations file
231
- file_import.add_submodule_import(
232
- relative_path,
233
- "models",
234
- ImportType.LOCAL,
235
- alias="_models",
236
- typing_section=(TypingSection.TYPING if kwargs.get("model_typing") else TypingSection.REGULAR),
237
- )
249
+ elif serialize_namespace_type == NamespaceType.TYPES_FILE or (
250
+ serialize_namespace_type == NamespaceType.MODEL and called_by_property
251
+ ):
252
+ file_import.add_submodule_import(
253
+ relative_path,
254
+ "models",
255
+ ImportType.LOCAL,
256
+ alias=alias,
257
+ typing_section=TypingSection.TYPING,
258
+ )
259
+
260
+ file_import.merge(self.value_type.imports(**kwargs))
238
261
  return file_import
@@ -5,6 +5,7 @@
5
5
  # --------------------------------------------------------------------------
6
6
  from enum import Enum, auto
7
7
  from typing import Dict, List, Optional, Tuple, Union, Set, TYPE_CHECKING
8
+ from .._utils import get_parent_namespace
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from .code_model import CodeModel
@@ -259,7 +260,7 @@ class FileImport:
259
260
  def add_msrest_import(
260
261
  self,
261
262
  *,
262
- relative_path: str,
263
+ serialize_namespace: str,
263
264
  msrest_import_type: MsrestImportType,
264
265
  typing_section: TypingSection,
265
266
  ):
@@ -271,21 +272,22 @@ class FileImport:
271
272
  if msrest_import_type == MsrestImportType.SerializerDeserializer:
272
273
  self.add_submodule_import("msrest", "Deserializer", ImportType.THIRDPARTY, typing_section)
273
274
  else:
275
+ # _serialization.py is always in root namespace
276
+ imported_namespace = self.code_model.namespace
274
277
  if self.code_model.options["multiapi"]:
275
- relative_path += "."
278
+ # for multiapi, the namespace is azure.mgmt.xxx.v20XX_XX_XX while _serialization.py is in azure.mgmt.xxx
279
+ imported_namespace = get_parent_namespace(imported_namespace)
276
280
  if msrest_import_type == MsrestImportType.Module:
277
- self.add_submodule_import(relative_path, "_serialization", ImportType.LOCAL, typing_section)
278
- else:
279
281
  self.add_submodule_import(
280
- f"{relative_path}_serialization",
281
- "Serializer",
282
+ self.code_model.get_relative_import_path(serialize_namespace, imported_namespace),
283
+ "_serialization",
282
284
  ImportType.LOCAL,
283
285
  typing_section,
284
286
  )
287
+ else:
288
+ relative_path = self.code_model.get_relative_import_path(
289
+ serialize_namespace, imported_namespace, module_name="_serialization"
290
+ )
291
+ self.add_submodule_import(relative_path, "Serializer", ImportType.LOCAL, typing_section)
285
292
  if msrest_import_type == MsrestImportType.SerializerDeserializer:
286
- self.add_submodule_import(
287
- f"{relative_path}_serialization",
288
- "Deserializer",
289
- ImportType.LOCAL,
290
- typing_section,
291
- )
293
+ self.add_submodule_import(relative_path, "Deserializer", ImportType.LOCAL, typing_section)
@@ -29,9 +29,8 @@ class ListType(BaseType):
29
29
  def encode(self) -> Optional[str]:
30
30
  return self.element_type.encode if hasattr(self.element_type, "encode") else None # type: ignore
31
31
 
32
- @property
33
- def serialization_type(self) -> str:
34
- return f"[{self.element_type.serialization_type}]"
32
+ def serialization_type(self, **kwargs: Any) -> str:
33
+ return f"[{self.element_type.serialization_type(**kwargs)}]"
35
34
 
36
35
  def type_annotation(self, **kwargs: Any) -> str:
37
36
  if (
@@ -92,9 +92,9 @@ class LROOperationBase(OperationBase[LROResponseType]):
92
92
  return lro_response.type_annotation(**kwargs)
93
93
  return "None"
94
94
 
95
- def cls_type_annotation(self, *, async_mode: bool) -> str:
95
+ def cls_type_annotation(self, *, async_mode: bool, **kwargs: Any) -> str:
96
96
  """We don't want the poller to show up in ClsType, so we call super() on response type annotation"""
97
- return f"ClsType[{Response.type_annotation(self.responses[0], async_mode=async_mode)}]"
97
+ return f"ClsType[{Response.type_annotation(self.responses[0], async_mode=async_mode, **kwargs)}]"
98
98
 
99
99
  def get_poller_with_response_type(self, async_mode: bool) -> str:
100
100
  return self.response_type_annotation(async_mode=async_mode)
@@ -132,8 +132,12 @@ class LROOperationBase(OperationBase[LROResponseType]):
132
132
  ):
133
133
  # used in the case if initial operation returns none
134
134
  # but final call returns a model
135
- relative_path = "..." if async_mode else ".."
136
- file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
135
+ serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
136
+ file_import.add_submodule_import(
137
+ self.code_model.get_relative_import_path(serialize_namespace, module_name="_model_base"),
138
+ "_deserialize",
139
+ ImportType.LOCAL,
140
+ )
137
141
  file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
138
142
  file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
139
143
  return file_import
@@ -20,8 +20,8 @@ class LROPagingOperation(LROOperationBase[LROPagingResponse], PagingOperationBas
20
20
  def operation_type(self) -> str:
21
21
  return "lropaging"
22
22
 
23
- def cls_type_annotation(self, *, async_mode: bool) -> str:
24
- return f"ClsType[{Response.type_annotation(self.responses[0], async_mode=async_mode)}]"
23
+ def cls_type_annotation(self, *, async_mode: bool, **kwargs: Any) -> str:
24
+ return f"ClsType[{Response.type_annotation(self.responses[0], async_mode=async_mode, **kwargs)}]"
25
25
 
26
26
  def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
27
27
  lro_imports = LROOperationBase.imports(self, async_mode, **kwargs)