@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
@@ -4,7 +4,8 @@
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
6
  import logging
7
- from typing import List, Optional, Any, Union
7
+ from collections import namedtuple
8
+ from typing import List, Any, Union
8
9
  from pathlib import Path
9
10
  from jinja2 import PackageLoader, Environment, FileSystemLoader, StrictUndefined
10
11
 
@@ -15,6 +16,8 @@ from ..models import (
15
16
  OverloadedRequestBuilder,
16
17
  CodeModel,
17
18
  Client,
19
+ ModelType,
20
+ EnumType,
18
21
  )
19
22
  from .enum_serializer import EnumSerializer
20
23
  from .general_serializer import GeneralSerializer
@@ -34,7 +37,6 @@ from .utils import (
34
37
  extract_sample_name,
35
38
  get_namespace_from_package_name,
36
39
  get_namespace_config,
37
- get_all_operation_groups_recursively,
38
40
  )
39
41
 
40
42
  _LOGGER = logging.getLogger(__name__)
@@ -53,6 +55,7 @@ _PACKAGE_FILES = [
53
55
  ]
54
56
 
55
57
  _REGENERATE_FILES = {"setup.py", "MANIFEST.in"}
58
+ AsyncInfo = namedtuple("AsyncInfo", ["async_mode", "async_path"])
56
59
 
57
60
 
58
61
  # extract sub folders. For example, source_file_path is like:
@@ -85,54 +88,11 @@ class JinjaSerializer(ReaderAndWriter):
85
88
  def has_operations_folder(self) -> bool:
86
89
  return self.code_model.options["show_operations"] and bool(self.code_model.has_operations)
87
90
 
88
- def _serialize_namespace_level(self, env: Environment, namespace_path: Path, clients: List[Client]) -> None:
89
- # if there was a patch file before, we keep it
90
- self._keep_patch_file(namespace_path / Path("_patch.py"), env)
91
- if self.has_aio_folder:
92
- self._keep_patch_file(namespace_path / Path("aio") / Path("_patch.py"), env)
93
-
94
- if self.has_operations_folder:
95
- self._keep_patch_file(
96
- namespace_path / Path(self.code_model.operations_folder_name) / Path("_patch.py"),
97
- env,
98
- )
99
- if self.has_aio_folder:
100
- self._keep_patch_file(
101
- namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path("_patch.py"),
102
- env,
103
- )
104
- self._serialize_and_write_top_level_folder(env=env, namespace_path=namespace_path, clients=clients)
105
-
106
- if any(c for c in self.code_model.clients if c.operation_groups):
107
- if self.code_model.options["builders_visibility"] != "embedded":
108
- self._serialize_and_write_rest_layer(env=env, namespace_path=namespace_path)
109
- if self.has_aio_folder:
110
- self._serialize_and_write_aio_top_level_folder(
111
- env=env,
112
- namespace_path=namespace_path,
113
- clients=clients,
114
- )
115
-
116
- if self.has_operations_folder:
117
- self._serialize_and_write_operations_folder(clients, env=env, namespace_path=namespace_path)
118
- if self.code_model.options["multiapi"]:
119
- self._serialize_and_write_metadata(env=env, namespace_path=namespace_path)
120
- if self.code_model.options["package_mode"]:
121
- self._serialize_and_write_package_files(namespace_path=namespace_path)
122
-
123
- if (
124
- self.code_model.options["show_operations"]
125
- and self.code_model.has_operations
126
- and self.code_model.options["generate_sample"]
127
- ):
128
- self._serialize_and_write_sample(env, namespace_path)
129
-
130
- if (
131
- self.code_model.options["show_operations"]
132
- and self.code_model.has_operations
133
- and self.code_model.options["generate_test"]
134
- ):
135
- self._serialize_and_write_test(env, namespace_path)
91
+ @property
92
+ def serialize_loop(self) -> List[AsyncInfo]:
93
+ sync_loop = AsyncInfo(async_mode=False, async_path="")
94
+ async_loop = AsyncInfo(async_mode=True, async_path="aio/")
95
+ return [sync_loop, async_loop] if self.has_aio_folder else [sync_loop]
136
96
 
137
97
  def serialize(self) -> None:
138
98
  env = Environment(
@@ -144,54 +104,71 @@ class JinjaSerializer(ReaderAndWriter):
144
104
  lstrip_blocks=True,
145
105
  )
146
106
 
147
- namespace_path = (
148
- Path(".") if self.code_model.options["no_namespace_folders"] else Path(*self._name_space().split("."))
149
- )
150
-
151
- p = namespace_path.parent
152
107
  general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
153
- while p != Path("."):
154
- # write pkgutil init file
155
- self.write_file(
156
- p / Path("__init__.py"),
157
- general_serializer.serialize_pkgutil_init_file(),
158
- )
159
- p = p.parent
160
-
161
- # serialize main module
162
- self._serialize_namespace_level(
163
- env,
164
- namespace_path,
165
- [c for c in self.code_model.clients if c.has_operations],
166
- )
167
- # serialize sub modules
168
- for (
169
- subnamespace,
170
- clients,
171
- ) in self.code_model.subnamespace_to_clients.items():
172
- subnamespace_path = namespace_path / Path(subnamespace)
173
- self._serialize_namespace_level(env, subnamespace_path, [c for c in clients if c.has_operations])
174
-
175
- if self.code_model.options["models_mode"] and (self.code_model.model_types or self.code_model.enums):
176
- self._keep_patch_file(namespace_path / Path("models") / Path("_patch.py"), env)
177
-
178
- if self.code_model.options["models_mode"] and (self.code_model.model_types or self.code_model.enums):
179
- self._serialize_and_write_models_folder(env=env, namespace_path=namespace_path)
180
- if not self.code_model.options["models_mode"]:
181
- # keep models file if users ended up just writing a models file
182
- if self.read_file(namespace_path / Path("models.py")):
108
+ for client_namespace, client_namespace_type in self.code_model.client_namespace_types.items():
109
+ exec_path = self.exec_path(client_namespace)
110
+ if client_namespace == "":
111
+ # Write the setup file
112
+ if self.code_model.options["basic_setup_py"]:
113
+ self.write_file(exec_path / Path("setup.py"), general_serializer.serialize_setup_file())
114
+
115
+ # add packaging files in root namespace (e.g. setup.py, README.md, etc.)
116
+ if self.code_model.options["package_mode"]:
117
+ self._serialize_and_write_package_files(client_namespace)
118
+
119
+ # write apiview_mapping_python.json
120
+ if self.code_model.options.get("emit_cross_language_definition_file"):
121
+ self.write_file(
122
+ exec_path / Path("apiview_mapping_python.json"),
123
+ general_serializer.serialize_cross_language_definition_file(),
124
+ )
125
+
126
+ # add generated samples and generated tests
127
+ if self.code_model.options["show_operations"] and self.code_model.has_operations:
128
+ if self.code_model.options["generate_sample"]:
129
+ self._serialize_and_write_sample(env, namespace=client_namespace)
130
+ if self.code_model.options["generate_test"]:
131
+ self._serialize_and_write_test(env, namespace=client_namespace)
132
+ elif client_namespace_type.clients:
133
+ # add clients folder if there are clients in this namespace
134
+ self._serialize_client_and_config_files(client_namespace, client_namespace_type.clients, env)
135
+ else:
136
+ # add pkgutil init file if no clients in this namespace
183
137
  self.write_file(
184
- namespace_path / Path("models.py"),
185
- self.read_file(namespace_path / Path("models.py")),
138
+ exec_path / Path("__init__.py"),
139
+ general_serializer.serialize_pkgutil_init_file(),
186
140
  )
187
- if self.code_model.named_unions:
188
- self.write_file(
189
- namespace_path / Path("_types.py"),
190
- TypesSerializer(code_model=self.code_model, env=env).serialize(),
191
- )
192
141
 
193
- def _serialize_and_write_package_files(self, namespace_path: Path) -> None:
194
- root_of_sdk = self._package_root_folder(namespace_path)
142
+ # _model_base.py/_serialization.py/_vendor.py/py.typed/_types.py/_validation.py
143
+ # is always put in top level namespace
144
+ if self.code_model.is_top_namespace(client_namespace):
145
+ self._serialize_and_write_top_level_folder(env=env, namespace=client_namespace)
146
+
147
+ # add models folder if there are models in this namespace
148
+ if (client_namespace_type.models or client_namespace_type.enums) and self.code_model.options["models_mode"]:
149
+ self._serialize_and_write_models_folder(
150
+ env=env,
151
+ namespace=client_namespace,
152
+ models=client_namespace_type.models,
153
+ enums=client_namespace_type.enums,
154
+ )
155
+
156
+ if not self.code_model.options["models_mode"]:
157
+ # keep models file if users ended up just writing a models file
158
+ model_path = exec_path / Path("models.py")
159
+ if self.read_file(model_path):
160
+ self.write_file(model_path, self.read_file(model_path))
161
+
162
+ # add operations folder if there are operations in this namespace
163
+ if client_namespace_type.operation_groups:
164
+ self._serialize_and_write_operations_folder(
165
+ client_namespace_type.operation_groups, env=env, namespace=client_namespace
166
+ )
167
+ if self.code_model.options["multiapi"]:
168
+ self._serialize_and_write_metadata(env=env, namespace=client_namespace)
169
+
170
+ def _serialize_and_write_package_files(self, client_namespace: str) -> None:
171
+ root_of_sdk = self.exec_path(client_namespace)
195
172
  if self.code_model.options["package_mode"] in VALID_PACKAGE_MODE:
196
173
  env = Environment(
197
174
  loader=PackageLoader("pygen.codegen", "templates/packaging_templates"),
@@ -230,25 +207,31 @@ class JinjaSerializer(ReaderAndWriter):
230
207
  PatchSerializer(env=env, code_model=self.code_model).serialize(),
231
208
  )
232
209
 
233
- def _serialize_and_write_models_folder(self, env: Environment, namespace_path: Path) -> None:
210
+ def _serialize_and_write_models_folder(
211
+ self, env: Environment, namespace: str, models: List[ModelType], enums: List[EnumType]
212
+ ) -> None:
234
213
  # Write the models folder
235
- models_path = namespace_path / Path("models")
214
+ models_path = self.exec_path(namespace + ".models")
236
215
  serializer = DpgModelSerializer if self.code_model.options["models_mode"] == "dpg" else MsrestModelSerializer
237
- if self.code_model.model_types:
216
+ if models:
238
217
  self.write_file(
239
218
  models_path / Path(f"{self.code_model.models_filename}.py"),
240
- serializer(code_model=self.code_model, env=env).serialize(),
219
+ serializer(code_model=self.code_model, env=env, client_namespace=namespace, models=models).serialize(),
241
220
  )
242
- if self.code_model.enums:
221
+ if enums:
243
222
  self.write_file(
244
223
  models_path / Path(f"{self.code_model.enums_filename}.py"),
245
- EnumSerializer(code_model=self.code_model, env=env).serialize(),
224
+ EnumSerializer(
225
+ code_model=self.code_model, env=env, client_namespace=namespace, enums=enums
226
+ ).serialize(),
246
227
  )
247
228
  self.write_file(
248
229
  models_path / Path("__init__.py"),
249
- ModelInitSerializer(code_model=self.code_model, env=env).serialize(),
230
+ ModelInitSerializer(code_model=self.code_model, env=env, models=models, enums=enums).serialize(),
250
231
  )
251
232
 
233
+ self._keep_patch_file(models_path / Path("_patch.py"), env)
234
+
252
235
  def _serialize_and_write_rest_layer(self, env: Environment, namespace_path: Path) -> None:
253
236
  rest_path = namespace_path / Path(self.code_model.rest_layer_name)
254
237
  group_names = {rb.group_name for c in self.code_model.clients for rb in c.request_builders}
@@ -292,89 +275,57 @@ class JinjaSerializer(ReaderAndWriter):
292
275
  ).serialize_init(),
293
276
  )
294
277
 
295
- def _serialize_and_write_operations_file(
296
- self,
297
- env: Environment,
298
- clients: List[Client],
299
- namespace_path: Path,
300
- operation_group: Optional[OperationGroup] = None,
301
- ) -> None:
302
- filename = operation_group.filename if operation_group else "_operations"
303
- # write first sync file
304
- operation_group_serializer = OperationGroupsSerializer(
305
- code_model=self.code_model,
306
- clients=clients,
307
- env=env,
308
- async_mode=False,
309
- operation_group=operation_group,
310
- )
311
- self.write_file(
312
- namespace_path / Path(self.code_model.operations_folder_name) / Path(f"{filename}.py"),
313
- operation_group_serializer.serialize(),
314
- )
315
-
316
- if self.has_aio_folder:
317
- # write async operation group and operation files
318
- operation_group_async_serializer = OperationGroupsSerializer(
319
- code_model=self.code_model,
320
- clients=clients,
321
- env=env,
322
- async_mode=True,
323
- operation_group=operation_group,
324
- )
325
- self.write_file(
326
- (namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path(f"{filename}.py")),
327
- operation_group_async_serializer.serialize(),
328
- )
329
-
330
278
  def _serialize_and_write_operations_folder(
331
- self, clients: List[Client], env: Environment, namespace_path: Path
279
+ self, operation_groups: List[OperationGroup], env: Environment, namespace: str
332
280
  ) -> None:
333
- # write sync operations init file
334
- operations_init_serializer = OperationsInitSerializer(
335
- code_model=self.code_model, clients=clients, env=env, async_mode=False
336
- )
337
- self.write_file(
338
- namespace_path / Path(self.code_model.operations_folder_name) / Path("__init__.py"),
339
- operations_init_serializer.serialize(),
340
- )
341
-
342
- # write async operations init file
343
- if self.has_aio_folder:
344
- operations_async_init_serializer = OperationsInitSerializer(
345
- code_model=self.code_model, clients=clients, env=env, async_mode=True
281
+ operations_folder_name = self.code_model.operations_folder_name(namespace)
282
+ exec_path = self.exec_path(namespace)
283
+ for async_mode, async_path in self.serialize_loop:
284
+ prefix_path = f"{async_path}{operations_folder_name}"
285
+ # write init file
286
+ operations_init_serializer = OperationsInitSerializer(
287
+ code_model=self.code_model, operation_groups=operation_groups, env=env, async_mode=async_mode
346
288
  )
347
289
  self.write_file(
348
- namespace_path / Path("aio") / Path(self.code_model.operations_folder_name) / Path("__init__.py"),
349
- operations_async_init_serializer.serialize(),
290
+ exec_path / Path(f"{prefix_path}/__init__.py"),
291
+ operations_init_serializer.serialize(),
350
292
  )
351
293
 
352
- if self.code_model.options["combine_operation_files"]:
353
- self._serialize_and_write_operations_file(
354
- env=env,
355
- namespace_path=namespace_path,
356
- clients=clients,
357
- )
358
- else:
359
- for operation_group in get_all_operation_groups_recursively(self.code_model.clients):
360
- self._serialize_and_write_operations_file(
294
+ # write operations file
295
+ OgLoop = namedtuple("OgLoop", ["operation_groups", "filename"])
296
+ if self.code_model.options["combine_operation_files"]:
297
+ loops = [OgLoop(operation_groups, "_operations")]
298
+ else:
299
+ loops = [OgLoop([og], og.filename) for og in operation_groups]
300
+ for ogs, filename in loops:
301
+ operation_group_serializer = OperationGroupsSerializer(
302
+ code_model=self.code_model,
303
+ operation_groups=ogs,
361
304
  env=env,
362
- namespace_path=namespace_path,
363
- operation_group=operation_group,
364
- clients=clients,
305
+ async_mode=async_mode,
306
+ client_namespace=namespace,
365
307
  )
308
+ self.write_file(
309
+ exec_path / Path(f"{prefix_path}/{filename}.py"),
310
+ operation_group_serializer.serialize(),
311
+ )
312
+
313
+ # if there was a patch file before, we keep it
314
+ self._keep_patch_file(exec_path / Path(f"{prefix_path}/_patch.py"), env)
366
315
 
367
316
  def _serialize_and_write_version_file(
368
317
  self,
369
- namespace_path: Path,
318
+ namespace: str,
370
319
  general_serializer: GeneralSerializer,
371
320
  ):
321
+ exec_path = self.exec_path(namespace)
322
+
372
323
  def _read_version_file(original_version_file_name: str) -> str:
373
- return self.read_file(namespace_path / original_version_file_name)
324
+ return self.read_file(exec_path / original_version_file_name)
374
325
 
375
326
  def _write_version_file(original_version_file_name: str) -> None:
376
327
  self.write_file(
377
- namespace_path / Path("_version.py"),
328
+ exec_path / Path("_version.py"),
378
329
  _read_version_file(original_version_file_name),
379
330
  )
380
331
 
@@ -385,103 +336,103 @@ class JinjaSerializer(ReaderAndWriter):
385
336
  _write_version_file(original_version_file_name="version.py")
386
337
  elif self.code_model.options["package_version"]:
387
338
  self.write_file(
388
- namespace_path / Path("_version.py"),
339
+ exec_path / Path("_version.py"),
389
340
  general_serializer.serialize_version_file(),
390
341
  )
391
342
 
392
343
  def _serialize_client_and_config_files(
393
344
  self,
394
- namespace_path: Path,
395
- general_serializer: GeneralSerializer,
396
- async_mode: bool,
345
+ namespace: str,
397
346
  clients: List[Client],
347
+ env: Environment,
398
348
  ) -> None:
399
- if self.code_model.has_operations:
400
- namespace_path = namespace_path / Path("aio") if async_mode else namespace_path
401
- self.write_file(
402
- namespace_path / Path(f"{self.code_model.client_filename}.py"),
403
- general_serializer.serialize_service_client_file(clients),
349
+ exec_path = self.exec_path(namespace)
350
+ for async_mode, async_path in self.serialize_loop:
351
+ general_serializer = GeneralSerializer(
352
+ code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace
404
353
  )
354
+ # when there is client.py, there must be __init__.py
405
355
  self.write_file(
406
- namespace_path / Path("_configuration.py"),
407
- general_serializer.serialize_config_file(clients),
356
+ exec_path / Path(f"{async_path}__init__.py"),
357
+ general_serializer.serialize_init_file([c for c in clients if c.has_operations]),
408
358
  )
409
359
 
410
- def _serialize_and_write_top_level_folder(
411
- self, env: Environment, namespace_path: Path, clients: List[Client]
412
- ) -> None:
413
- general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
360
+ # if there was a patch file before, we keep it
361
+ self._keep_patch_file(exec_path / Path(f"{async_path}_patch.py"), env)
414
362
 
415
- self.write_file(
416
- namespace_path / Path("__init__.py"),
417
- general_serializer.serialize_init_file(clients),
418
- )
363
+ if self.code_model.clients_has_operations(clients):
419
364
 
420
- # Write the service client
421
- self._serialize_client_and_config_files(namespace_path, general_serializer, async_mode=False, clients=clients)
422
- if self.code_model.need_vendored_code(async_mode=False):
423
- self.write_file(
424
- namespace_path / Path("_vendor.py"),
425
- general_serializer.serialize_vendor_file(clients),
426
- )
365
+ # write client file
366
+ self.write_file(
367
+ exec_path / Path(f"{async_path}{self.code_model.client_filename}.py"),
368
+ general_serializer.serialize_service_client_file(clients),
369
+ )
370
+
371
+ # write config file
372
+ self.write_file(
373
+ exec_path / Path(f"{async_path}_configuration.py"),
374
+ general_serializer.serialize_config_file(clients),
375
+ )
376
+
377
+ # sometimes we need define additional Mixin class for client in _vendor.py
378
+ self._serialize_and_write_vendor_file(env, namespace)
427
379
 
428
- self._serialize_and_write_version_file(namespace_path, general_serializer)
380
+ def _serialize_and_write_vendor_file(self, env: Environment, namespace: str) -> None:
381
+ exec_path = self.exec_path(namespace)
382
+ # write _vendor.py
383
+ for async_mode, async_path in self.serialize_loop:
384
+ if self.code_model.need_vendored_code(async_mode=async_mode, client_namespace=namespace):
385
+ self.write_file(
386
+ exec_path / Path(f"{async_path}_vendor.py"),
387
+ GeneralSerializer(
388
+ code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace
389
+ ).serialize_vendor_file(),
390
+ )
391
+
392
+ def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None:
393
+ exec_path = self.exec_path(namespace)
394
+ # write _vendor.py
395
+ self._serialize_and_write_vendor_file(env, namespace)
396
+
397
+ general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False)
398
+
399
+ # write _version.py
400
+ self._serialize_and_write_version_file(namespace, general_serializer)
429
401
 
430
402
  # write the empty py.typed file
431
- self.write_file(namespace_path / Path("py.typed"), "# Marker file for PEP 561.")
403
+ self.write_file(exec_path / Path("py.typed"), "# Marker file for PEP 561.")
432
404
 
405
+ # write _serialization.py
433
406
  if not self.code_model.options["client_side_validation"] and not self.code_model.options["multiapi"]:
434
407
  self.write_file(
435
- namespace_path / Path("_serialization.py"),
408
+ exec_path / Path("_serialization.py"),
436
409
  general_serializer.serialize_serialization_file(),
437
410
  )
411
+
412
+ # write _model_base.py
438
413
  if self.code_model.options["models_mode"] == "dpg":
439
414
  self.write_file(
440
- namespace_path / Path("_model_base.py"),
415
+ exec_path / Path("_model_base.py"),
441
416
  general_serializer.serialize_model_base_file(),
442
417
  )
443
418
 
419
+ # write _validation.py
444
420
  if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation):
445
421
  self.write_file(
446
- namespace_path / Path("_validation.py"),
422
+ exec_path / Path("_validation.py"),
447
423
  general_serializer.serialize_validation_file(),
448
424
  )
449
- if self.code_model.options.get("emit_cross_language_definition_file"):
450
- self.write_file(
451
- Path("./apiview_mapping_python.json"),
452
- general_serializer.serialize_cross_language_definition_file(),
453
- )
454
-
455
- # Write the setup file
456
- if self.code_model.options["basic_setup_py"]:
457
- self.write_file(Path("setup.py"), general_serializer.serialize_setup_file())
458
-
459
- def _serialize_and_write_aio_top_level_folder(
460
- self, env: Environment, namespace_path: Path, clients: List[Client]
461
- ) -> None:
462
- aio_general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=True)
463
-
464
- aio_path = namespace_path / Path("aio")
465
-
466
- # Write the __init__ file
467
- self.write_file(
468
- aio_path / Path("__init__.py"),
469
- aio_general_serializer.serialize_init_file(clients),
470
- )
471
425
 
472
- # Write the service client
473
- self._serialize_client_and_config_files(
474
- namespace_path, aio_general_serializer, async_mode=True, clients=clients
475
- )
476
- if self.code_model.need_vendored_code(async_mode=True):
426
+ # write _types.py
427
+ if self.code_model.named_unions:
477
428
  self.write_file(
478
- aio_path / Path("_vendor.py"),
479
- aio_general_serializer.serialize_vendor_file(clients),
429
+ exec_path / Path("_types.py"),
430
+ TypesSerializer(code_model=self.code_model, env=env).serialize(),
480
431
  )
481
432
 
482
- def _serialize_and_write_metadata(self, env: Environment, namespace_path: Path) -> None:
483
- metadata_serializer = MetadataSerializer(self.code_model, env)
484
- self.write_file(namespace_path / Path("_metadata.json"), metadata_serializer.serialize())
433
+ def _serialize_and_write_metadata(self, env: Environment, namespace: str) -> None:
434
+ metadata_serializer = MetadataSerializer(self.code_model, env, client_namespace=namespace)
435
+ self.write_file(self.exec_path(namespace) / Path("_metadata.json"), metadata_serializer.serialize())
485
436
 
486
437
  @property
487
438
  def _namespace_from_package_name(self) -> str:
@@ -493,9 +444,17 @@ class JinjaSerializer(ReaderAndWriter):
493
444
 
494
445
  return self._namespace_from_package_name
495
446
 
496
- # find root folder where "setup.py" is
497
- def _package_root_folder(self, namespace_path: Path) -> Path:
498
- return namespace_path / Path("../" * (self._name_space().count(".") + 1))
447
+ @property
448
+ def exec_path_compensation(self) -> Path:
449
+ """Assume the process is running in the root folder of the package. If not, we need the path compensation."""
450
+ return (
451
+ Path("../" * (self._name_space().count(".") + 1))
452
+ if self.code_model.options["no_namespace_folders"]
453
+ else Path(".")
454
+ )
455
+
456
+ def exec_path(self, namespace: str) -> Path:
457
+ return self.exec_path_compensation / Path(*namespace.split("."))
499
458
 
500
459
  @property
501
460
  def _additional_folder(self) -> Path:
@@ -506,8 +465,8 @@ class JinjaSerializer(ReaderAndWriter):
506
465
  return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
507
466
  return Path("")
508
467
 
509
- def _serialize_and_write_sample(self, env: Environment, namespace_path: Path):
510
- out_path = self._package_root_folder(namespace_path) / Path("generated_samples")
468
+ def _serialize_and_write_sample(self, env: Environment, namespace: str):
469
+ out_path = self.exec_path(namespace) / Path("generated_samples")
511
470
  for client in self.code_model.clients:
512
471
  for op_group in client.operation_groups:
513
472
  for operation in op_group.operations:
@@ -539,15 +498,15 @@ class JinjaSerializer(ReaderAndWriter):
539
498
  log_error = f"error happens in sample {file}: {e}"
540
499
  _LOGGER.error(log_error)
541
500
 
542
- def _serialize_and_write_test(self, env: Environment, namespace_path: Path):
501
+ def _serialize_and_write_test(self, env: Environment, namespace: str):
543
502
  self.code_model.for_test = True
544
- out_path = self._package_root_folder(namespace_path) / Path("generated_tests")
503
+ out_path = self.exec_path(namespace) / Path("generated_tests")
545
504
  general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
546
505
  self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
547
506
  if not self.code_model.options["azure_arm"]:
548
- for is_async in (True, False):
549
- async_suffix = "_async" if is_async else ""
550
- general_serializer.is_async = is_async
507
+ for async_mode in (True, False):
508
+ async_suffix = "_async" if async_mode else ""
509
+ general_serializer.async_mode = async_mode
551
510
  self.write_file(
552
511
  out_path / f"testpreparer{async_suffix}.py",
553
512
  general_serializer.serialize_testpreparer(),
@@ -560,9 +519,9 @@ class JinjaSerializer(ReaderAndWriter):
560
519
  ):
561
520
  continue
562
521
  test_serializer = TestSerializer(self.code_model, env, client=client, operation_group=og)
563
- for is_async in (True, False):
522
+ for async_mode in (True, False):
564
523
  try:
565
- test_serializer.is_async = is_async
524
+ test_serializer.async_mode = async_mode
566
525
  self.write_file(
567
526
  out_path / f"{to_snake_case(test_serializer.test_class_name)}.py",
568
527
  test_serializer.serialize_test(),
@@ -3,6 +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 Optional
6
7
  from jinja2 import Environment
7
8
  from ..models import (
8
9
  FileImport,
@@ -13,9 +14,26 @@ from ..models import (
13
14
  class BaseSerializer:
14
15
  """Base serializer for SDK root level files"""
15
16
 
16
- def __init__(self, code_model: CodeModel, env: Environment):
17
+ def __init__(
18
+ self,
19
+ code_model: CodeModel,
20
+ env: Environment,
21
+ async_mode: bool = False,
22
+ *,
23
+ client_namespace: Optional[str] = None
24
+ ):
17
25
  self.code_model = code_model
18
26
  self.env = env
27
+ self.async_mode = async_mode
28
+ self.client_namespace = code_model.namespace if client_namespace is None else client_namespace
19
29
 
20
30
  def init_file_import(self) -> FileImport:
21
31
  return FileImport(self.code_model)
32
+
33
+ # get namespace of serialize file from client namespace.
34
+ # For async API, serialize namespace will have additional suffix '.aio' compared with client namespace;
35
+ # For models, there will be additional '.models';
36
+ # For operations, there will be additional '.operations' or '._operations';
37
+ @property
38
+ def serialize_namespace(self) -> str:
39
+ return self.code_model.get_serialize_namespace(self.client_namespace, async_mode=self.async_mode)