@typespec/http-client-python 0.4.4 → 0.5.1

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 (156) hide show
  1. package/dist/emitter/emitter.d.ts.map +1 -1
  2. package/dist/emitter/emitter.js +110 -24
  3. package/dist/emitter/emitter.js.map +1 -1
  4. package/dist/emitter/http.js +1 -1
  5. package/dist/emitter/http.js.map +1 -1
  6. package/dist/emitter/lib.d.ts +1 -0
  7. package/dist/emitter/lib.d.ts.map +1 -1
  8. package/dist/emitter/lib.js +1 -0
  9. package/dist/emitter/lib.js.map +1 -1
  10. package/dist/emitter/run-python3.d.ts +2 -0
  11. package/dist/emitter/run-python3.d.ts.map +1 -0
  12. package/dist/emitter/run-python3.js +19 -0
  13. package/dist/emitter/run-python3.js.map +1 -0
  14. package/dist/emitter/system-requirements.d.ts +17 -0
  15. package/dist/emitter/system-requirements.d.ts.map +1 -0
  16. package/dist/emitter/system-requirements.js +167 -0
  17. package/dist/emitter/system-requirements.js.map +1 -0
  18. package/emitter/src/emitter.ts +111 -23
  19. package/emitter/src/http.ts +1 -1
  20. package/emitter/src/lib.ts +2 -0
  21. package/emitter/src/run-python3.ts +20 -0
  22. package/emitter/src/system-requirements.ts +261 -0
  23. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  24. package/eng/scripts/Test-Packages.ps1 +1 -1
  25. package/eng/scripts/ci/regenerate.ts +20 -8
  26. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  27. package/eng/scripts/setup/build.ts +16 -0
  28. package/eng/scripts/setup/build_pygen_wheel.py +40 -0
  29. package/eng/scripts/setup/install.py +9 -8
  30. package/eng/scripts/setup/install.ts +12 -0
  31. package/eng/scripts/setup/prepare.py +3 -1
  32. package/eng/scripts/setup/prepare.ts +11 -0
  33. package/eng/scripts/setup/run-python3.ts +1 -6
  34. package/generator/build/lib/pygen/__init__.py +107 -0
  35. package/generator/build/lib/pygen/_version.py +7 -0
  36. package/generator/build/lib/pygen/black.py +71 -0
  37. package/generator/build/lib/pygen/codegen/__init__.py +357 -0
  38. package/generator/build/lib/pygen/codegen/_utils.py +17 -0
  39. package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
  40. package/generator/build/lib/pygen/codegen/models/base.py +186 -0
  41. package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
  42. package/generator/build/lib/pygen/codegen/models/client.py +435 -0
  43. package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
  44. package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
  45. package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
  46. package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
  47. package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
  48. package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
  49. package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
  50. package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
  51. package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
  52. package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
  53. package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
  54. package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
  55. package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
  56. package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
  57. package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
  58. package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
  59. package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
  60. package/generator/build/lib/pygen/codegen/models/property.py +170 -0
  61. package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
  62. package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
  63. package/generator/build/lib/pygen/codegen/models/response.py +348 -0
  64. package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
  65. package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
  66. package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
  67. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
  68. package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
  69. package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
  70. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
  71. package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
  72. package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
  73. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
  74. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
  75. package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  76. package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  77. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
  78. package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
  79. package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  80. package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
  81. package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
  82. package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
  83. package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
  84. package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
  85. package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  86. package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
  87. package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  88. package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  89. package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
  90. package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  91. package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
  92. package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
  93. package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  94. package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  95. package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
  96. package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  97. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
  98. package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
  99. package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
  100. package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
  101. package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  102. package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
  103. package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  104. package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
  105. package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
  106. package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  107. package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  108. package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  109. package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  110. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  111. package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  112. package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  113. package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  114. package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
  115. package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  116. package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  117. package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  118. package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  119. package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
  120. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
  121. package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
  122. package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  123. package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
  124. package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
  125. package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
  126. package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
  127. package/generator/build/lib/pygen/m2r.py +65 -0
  128. package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
  129. package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
  130. package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
  131. package/generator/build/lib/pygen/utils.py +163 -0
  132. package/generator/component-detection-pip-report.json +134 -0
  133. package/generator/dev_requirements.txt +0 -1
  134. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  135. package/generator/pygen/codegen/__init__.py +4 -4
  136. package/generator/pygen.egg-info/PKG-INFO +7 -4
  137. package/generator/pygen.egg-info/requires.txt +7 -4
  138. package/generator/setup.py +7 -4
  139. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +1 -1
  140. package/generator/test/{generic_mock_api_tests/asynctests/test_payload_pageable_async.py → azure/mock_api_tests/asynctests/test_azure_payload_pageable_async.py} +1 -1
  141. package/generator/test/azure/mock_api_tests/conftest.py +5 -4
  142. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +1 -1
  143. package/generator/test/{generic_mock_api_tests/test_payload_pageable.py → azure/mock_api_tests/test_azure_payload_pageable.py} +1 -1
  144. package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_resiliency_srv_driven.py +4 -2
  145. package/generator/test/{generic_mock_api_tests/asynctests → azure/mock_api_tests}/test_resiliency_srv_driven_async.py +3 -2
  146. package/generator/test/azure/requirements.txt +9 -8
  147. package/generator/test/generic_mock_api_tests/conftest.py +9 -4
  148. package/generator/test/unbranded/mock_api_tests/conftest.py +4 -4
  149. package/generator/test/unbranded/mock_api_tests/test_unbranded.py +1 -1
  150. package/generator/test/unbranded/requirements.txt +1 -8
  151. package/package.json +10 -10
  152. package/generator/requirements.txt +0 -12
  153. /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/asynctests/test_client_naming_async.py +0 -0
  154. /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/asynctests/test_client_structure_async.py +0 -0
  155. /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_client_naming.py +0 -0
  156. /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_client_structure.py +0 -0
@@ -0,0 +1,198 @@
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
+ import functools
7
+ import json
8
+ from typing import List, Optional, Set, Tuple, Dict, Union, Any
9
+ from jinja2 import Environment
10
+ from ..models import (
11
+ OperationGroup,
12
+ LROOperation,
13
+ PagingOperation,
14
+ TypingSection,
15
+ ImportType,
16
+ CodeModel,
17
+ )
18
+ from .builder_serializer import get_operation_serializer
19
+ from .import_serializer import FileImportSerializer
20
+
21
+
22
+ def _to_string(data: Union[Tuple[Any], List[Any], str]) -> str:
23
+ if isinstance(data, (list, tuple)):
24
+ return "".join([_to_string(item) for item in data])
25
+ return str(data)
26
+
27
+
28
+ def _json_serialize_imports(
29
+ imports: Dict[
30
+ TypingSection,
31
+ Dict[
32
+ ImportType,
33
+ Dict[
34
+ str,
35
+ Set[
36
+ Optional[
37
+ Union[
38
+ str,
39
+ Tuple[str, str],
40
+ Tuple[
41
+ str,
42
+ Optional[str],
43
+ Tuple[Tuple[Tuple[int, int], str, Optional[str]]],
44
+ ],
45
+ ]
46
+ ]
47
+ ],
48
+ ],
49
+ ],
50
+ ]
51
+ ) -> str:
52
+ if not imports:
53
+ return ""
54
+
55
+ json_serialize_imports = {}
56
+ # need to make name_import set -> list to make the dictionary json serializable
57
+ # not using an OrderedDict since we're iterating through a set and the order there varies
58
+ # going to sort the list instead
59
+
60
+ for typing_section_key, typing_section_value in imports.items():
61
+ json_import_type_dictionary = {}
62
+ for import_type_key, import_type_value in typing_section_value.items():
63
+ json_package_name_dictionary = {}
64
+ for package_name, name_imports in import_type_value.items():
65
+ name_import_ordered_list = []
66
+ if name_imports:
67
+ name_import_ordered_list = list(name_imports)
68
+ name_import_ordered_list.sort(
69
+ key=lambda e: (
70
+ _to_string(e) # type: ignore
71
+ if isinstance(e, (list, tuple))
72
+ else e if isinstance(e, str) else ""
73
+ )
74
+ )
75
+ json_package_name_dictionary[package_name] = name_import_ordered_list
76
+ json_import_type_dictionary[import_type_key] = json_package_name_dictionary
77
+ json_serialize_imports[typing_section_key] = json_import_type_dictionary
78
+ return json.dumps(json_serialize_imports)
79
+
80
+
81
+ def _mixin_imports(
82
+ mixin_operation_group: Optional[OperationGroup],
83
+ ) -> Tuple[Optional[str], Optional[str]]:
84
+ if not mixin_operation_group:
85
+ return None, None
86
+
87
+ sync_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=False)
88
+ async_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=True)
89
+
90
+ return _json_serialize_imports(sync_mixin_imports.to_dict()), _json_serialize_imports(async_mixin_imports.to_dict())
91
+
92
+
93
+ def _mixin_typing_definitions(
94
+ mixin_operation_group: Optional[OperationGroup],
95
+ ) -> Tuple[Optional[str], Optional[str]]:
96
+ if not mixin_operation_group:
97
+ return None, None
98
+
99
+ sync_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=False)
100
+ async_mixin_imports = mixin_operation_group.imports_for_multiapi(async_mode=True)
101
+ sync_mixin_typing_definitions = FileImportSerializer(sync_mixin_imports, False).get_typing_definitions()
102
+ async_mixin_typing_definitions = FileImportSerializer(async_mixin_imports, True).get_typing_definitions()
103
+
104
+ return sync_mixin_typing_definitions, async_mixin_typing_definitions
105
+
106
+
107
+ class MetadataSerializer:
108
+ def __init__(self, code_model: CodeModel, env: Environment) -> None:
109
+ self.code_model = code_model
110
+ self.client = self.code_model.clients[0] # we only do one client for multiapi
111
+ self.env = env
112
+
113
+ def _choose_api_version(self) -> Tuple[str, List[str]]:
114
+ chosen_version = ""
115
+ total_api_version_set: Set[str] = set()
116
+ for client in self.code_model.clients:
117
+ for operation_group in client.operation_groups:
118
+ total_api_version_set.update(operation_group.api_versions)
119
+
120
+ total_api_version_list = list(total_api_version_set)
121
+ total_api_version_list.sort()
122
+
123
+ # switching ' to " so json can decode the dict we end up writing to file
124
+ total_api_version_list = [str(api_version).replace("'", '"') for api_version in total_api_version_list]
125
+ if len(total_api_version_list) == 1:
126
+ chosen_version = total_api_version_list[0]
127
+ elif len(total_api_version_list) > 1:
128
+ module_version = self.code_model.namespace.split(".")[-1]
129
+ for api_version in total_api_version_list:
130
+ if "v{}".format(api_version.replace("-", "_")) == module_version:
131
+ chosen_version = api_version
132
+
133
+ return chosen_version, total_api_version_list
134
+
135
+ def serialize(self) -> str:
136
+ def _is_lro(operation):
137
+ return isinstance(operation, LROOperation)
138
+
139
+ def _is_paging(operation):
140
+ return isinstance(operation, PagingOperation)
141
+
142
+ mixin_operation_group: Optional[OperationGroup] = next(
143
+ (
144
+ operation_group
145
+ for client in self.code_model.clients
146
+ for operation_group in client.operation_groups
147
+ if operation_group.is_mixin
148
+ ),
149
+ None,
150
+ )
151
+ mixin_operations = mixin_operation_group.operations if mixin_operation_group else []
152
+ sync_mixin_imports, async_mixin_imports = _mixin_imports(mixin_operation_group)
153
+ (
154
+ sync_mixin_typing_definitions,
155
+ async_mixin_typing_definitions,
156
+ ) = _mixin_typing_definitions(mixin_operation_group)
157
+
158
+ chosen_version, total_api_version_list = self._choose_api_version()
159
+
160
+ # setting to true, because for multiapi we always generate with a version file with version 0.1.0
161
+ self.code_model.options["package_version"] = "0.1.0"
162
+ template = self.env.get_template("metadata.json.jinja2")
163
+
164
+ return template.render(
165
+ code_model=self.code_model,
166
+ chosen_version=chosen_version,
167
+ total_api_version_list=total_api_version_list,
168
+ client=self.client,
169
+ global_parameters=self.client.parameters,
170
+ mixin_operations=mixin_operations,
171
+ any=any,
172
+ is_lro=_is_lro,
173
+ is_paging=_is_paging,
174
+ str=str,
175
+ sync_mixin_imports=sync_mixin_imports,
176
+ async_mixin_imports=async_mixin_imports,
177
+ sync_mixin_typing_definitions=sync_mixin_typing_definitions,
178
+ async_mixin_typing_definitions=async_mixin_typing_definitions,
179
+ sync_client_imports=_json_serialize_imports(self.client.imports_for_multiapi(async_mode=False).to_dict()),
180
+ async_client_imports=_json_serialize_imports(self.client.imports_for_multiapi(async_mode=True).to_dict()),
181
+ sync_config_imports=_json_serialize_imports(
182
+ self.client.config.imports_for_multiapi(async_mode=False).to_dict()
183
+ ),
184
+ async_config_imports=_json_serialize_imports(
185
+ self.client.config.imports_for_multiapi(async_mode=True).to_dict()
186
+ ),
187
+ get_async_operation_serializer=functools.partial(
188
+ get_operation_serializer,
189
+ code_model=self.client.code_model,
190
+ async_mode=True,
191
+ ),
192
+ get_sync_operation_serializer=functools.partial(
193
+ get_operation_serializer,
194
+ code_model=self.client.code_model,
195
+ async_mode=False,
196
+ ),
197
+ has_credential=bool(self.client.credential),
198
+ )
@@ -0,0 +1,33 @@
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 jinja2 import Environment
7
+ from ..models import CodeModel
8
+
9
+
10
+ class ModelInitSerializer:
11
+ def __init__(self, code_model: CodeModel, env: Environment) -> None:
12
+ self.code_model = code_model
13
+ self.env = env
14
+
15
+ def serialize(self) -> str:
16
+ schemas = [s.name for s in self.code_model.public_model_types]
17
+ schemas.sort()
18
+ enums = [e.name for e in self.code_model.enums if not e.internal] if self.code_model.enums else None
19
+
20
+ if enums:
21
+ enums.sort()
22
+
23
+ # check to see if we have any duplicate names between enum and object schemas
24
+ model_enum_name_intersection = set(schemas).intersection(set(enums))
25
+ if model_enum_name_intersection:
26
+ raise ValueError(
27
+ "We have models and enums sharing the following names: {}".format(
28
+ ", ".join(model_enum_name_intersection)
29
+ )
30
+ )
31
+
32
+ template = self.env.get_template("model_init.py.jinja2")
33
+ return template.render(code_model=self.code_model, schemas=schemas, enums=enums)
@@ -0,0 +1,335 @@
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 List
7
+ from abc import ABC, abstractmethod
8
+
9
+ from ..models import ModelType, Property, ConstantType, EnumValue
10
+ from ..models.imports import FileImport, TypingSection, MsrestImportType, ImportType
11
+ from .import_serializer import FileImportSerializer
12
+ from .base_serializer import BaseSerializer
13
+
14
+
15
+ def _documentation_string(prop: Property, description_keyword: str, docstring_type_keyword: str) -> List[str]:
16
+ retval: List[str] = []
17
+ sphinx_prefix = f":{description_keyword} {prop.client_name}:"
18
+ description = prop.description(is_operation_file=False).replace("\\", "\\\\")
19
+ retval.append(f"{sphinx_prefix} {description}" if description else sphinx_prefix)
20
+ retval.append(f":{docstring_type_keyword} {prop.client_name}: {prop.type.docstring_type()}")
21
+ return retval
22
+
23
+
24
+ class _ModelSerializer(BaseSerializer, ABC):
25
+ @abstractmethod
26
+ def imports(self) -> FileImport: ...
27
+
28
+ def serialize(self) -> str:
29
+ # Generate the models
30
+ template = self.env.get_template("model_container.py.jinja2")
31
+ return template.render(
32
+ code_model=self.code_model,
33
+ imports=FileImportSerializer(self.imports()),
34
+ str=str,
35
+ serializer=self,
36
+ )
37
+
38
+ @abstractmethod
39
+ def declare_model(self, model: ModelType) -> str: ...
40
+
41
+ @staticmethod
42
+ def escape_dot(s: str):
43
+ return s.replace(".", "\\\\.")
44
+
45
+ @staticmethod
46
+ def input_documentation_string(prop: Property) -> List[str]:
47
+ # building the param line of the property doc
48
+ return _documentation_string(prop, "keyword", "paramtype")
49
+
50
+ @staticmethod
51
+ def variable_documentation_string(prop: Property) -> List[str]:
52
+ return _documentation_string(prop, "ivar", "vartype")
53
+
54
+ def super_call(self, model: ModelType) -> List[str]:
55
+ return [f"super().__init__({self.properties_to_pass_to_super(model)})"]
56
+
57
+ @staticmethod
58
+ def initialize_discriminator_property(model: ModelType, prop: Property) -> str:
59
+ discriminator_value = f"'{model.discriminator_value}'" if model.discriminator_value else None
60
+ if not discriminator_value:
61
+ typing = "Optional[str]"
62
+ else:
63
+ typing = "str"
64
+ return f"self.{prop.client_name}: {typing} = {discriminator_value}"
65
+
66
+ @staticmethod
67
+ def initialize_standard_property(prop: Property):
68
+ if not (prop.optional or prop.client_default_value is not None):
69
+ return f"{prop.client_name}: {prop.type_annotation()},{prop.pylint_disable()}"
70
+ return (
71
+ f"{prop.client_name}: {prop.type_annotation()} = "
72
+ f"{prop.client_default_value_declaration},{prop.pylint_disable()}"
73
+ )
74
+
75
+ @staticmethod
76
+ def discriminator_docstring(model: ModelType) -> str:
77
+ return (
78
+ "You probably want to use the sub-classes and not this class directly. "
79
+ f"Known sub-classes are: {', '.join(v.name for v in model.discriminated_subtypes.values())}"
80
+ )
81
+
82
+ @staticmethod
83
+ def _init_line_parameters(model: ModelType):
84
+ return [p for p in model.properties if not p.readonly and not p.is_discriminator and not p.constant]
85
+
86
+ def init_line(self, model: ModelType) -> List[str]:
87
+ init_properties_declaration = []
88
+ init_line_parameters = self._init_line_parameters(model)
89
+ init_line_parameters.sort(key=lambda x: x.optional)
90
+ if init_line_parameters:
91
+ init_properties_declaration.append("*,")
92
+ for param in init_line_parameters:
93
+ init_properties_declaration.append(self.initialize_standard_property(param))
94
+
95
+ return init_properties_declaration
96
+
97
+ @staticmethod
98
+ def properties_to_pass_to_super(model: ModelType) -> str:
99
+ properties_to_pass_to_super = []
100
+ for parent in model.parents:
101
+ for prop in model.properties:
102
+ if prop in parent.properties and not prop.is_discriminator and not prop.constant and not prop.readonly:
103
+ properties_to_pass_to_super.append(f"{prop.client_name}={prop.client_name}")
104
+ properties_to_pass_to_super.append("**kwargs")
105
+ return ", ".join(properties_to_pass_to_super)
106
+
107
+ @abstractmethod
108
+ def initialize_properties(self, model: ModelType) -> List[str]: ...
109
+
110
+ def need_init(self, model: ModelType) -> bool:
111
+ return (not model.internal) and bool(self.init_line(model) or model.discriminator)
112
+
113
+ def pylint_disable_items(self, model: ModelType) -> List[str]:
114
+ if model.flattened_property or self.initialize_properties(model):
115
+ return [""]
116
+ if any(p for p in model.properties if p.is_discriminator and model.discriminator_value):
117
+ return [""]
118
+ if model.parents and any(
119
+ "=" in prop for parent in model.parents for prop in self.init_line(parent) if self.need_init(parent)
120
+ ):
121
+ return [""]
122
+ return ["useless-super-delegation"]
123
+
124
+ def pylint_disable(self, model: ModelType) -> str:
125
+ return " # pylint: disable=" + ", ".join(self.pylint_disable_items(model))
126
+
127
+ def global_pylint_disables(self) -> str:
128
+ return ""
129
+
130
+
131
+ class MsrestModelSerializer(_ModelSerializer):
132
+ def imports(self) -> FileImport:
133
+ file_import = FileImport(self.code_model)
134
+ file_import.add_msrest_import(
135
+ relative_path="..",
136
+ msrest_import_type=MsrestImportType.Module,
137
+ typing_section=TypingSection.REGULAR,
138
+ )
139
+ for model in self.code_model.model_types:
140
+ file_import.merge(model.imports(is_operation_file=False))
141
+ for param in self._init_line_parameters(model):
142
+ file_import.merge(param.imports())
143
+
144
+ return file_import
145
+
146
+ def declare_model(self, model: ModelType) -> str:
147
+ basename = (
148
+ "msrest.serialization.Model"
149
+ if self.code_model.options["client_side_validation"]
150
+ else "_serialization.Model"
151
+ )
152
+ if model.parents:
153
+ basename = ", ".join([m.name for m in model.parents])
154
+ return f"class {model.name}({basename}):{model.pylint_disable()}"
155
+
156
+ @staticmethod
157
+ def get_properties_to_initialize(model: ModelType) -> List[Property]:
158
+ if model.parents:
159
+ properties_to_initialize = list(
160
+ {
161
+ p.client_name: p
162
+ for bm in model.parents
163
+ for p in model.properties
164
+ if p not in bm.properties or p.is_discriminator or p.constant
165
+ }.values()
166
+ )
167
+ else:
168
+ properties_to_initialize = model.properties
169
+ return properties_to_initialize
170
+
171
+ def initialize_properties(self, model: ModelType) -> List[str]:
172
+ init_args = []
173
+ for prop in self.get_properties_to_initialize(model):
174
+ if prop.is_discriminator:
175
+ init_args.append(self.initialize_discriminator_property(model, prop))
176
+ elif prop.readonly:
177
+ init_args.append(f"self.{prop.client_name} = None")
178
+ elif not prop.constant:
179
+ init_args.append(f"self.{prop.client_name} = {prop.client_name}")
180
+ return init_args
181
+
182
+ @staticmethod
183
+ def declare_property(prop: Property) -> str:
184
+ if prop.flattened_names:
185
+ attribute_key = ".".join(_ModelSerializer.escape_dot(n) for n in prop.flattened_names)
186
+ else:
187
+ attribute_key = _ModelSerializer.escape_dot(prop.wire_name)
188
+ if prop.type.xml_serialization_ctxt:
189
+ xml_metadata = f", 'xml': {{{prop.type.xml_serialization_ctxt}}}"
190
+ else:
191
+ xml_metadata = ""
192
+ return (
193
+ f'"{prop.client_name}": {{"key": "{attribute_key}",'
194
+ f' "type": "{prop.msrest_deserialization_key}"{xml_metadata}}},'
195
+ )
196
+
197
+
198
+ class DpgModelSerializer(_ModelSerializer):
199
+ def super_call(self, model: ModelType) -> List[str]:
200
+ super_call = f"super().__init__({self.properties_to_pass_to_super(model)})"
201
+ if model.flattened_property:
202
+ return [
203
+ "_flattened_input = {k: kwargs.pop(k) for k in kwargs.keys() & self.__flattened_items}",
204
+ super_call,
205
+ "for k, v in _flattened_input.items():",
206
+ " setattr(self, k, v)",
207
+ ]
208
+ return [super_call]
209
+
210
+ def imports(self) -> FileImport:
211
+ file_import = FileImport(self.code_model)
212
+ file_import.add_submodule_import(
213
+ "..",
214
+ "_model_base",
215
+ ImportType.LOCAL,
216
+ TypingSection.REGULAR,
217
+ )
218
+
219
+ for model in self.code_model.model_types:
220
+ if model.base == "json":
221
+ continue
222
+ file_import.merge(model.imports(is_operation_file=False))
223
+ for prop in model.properties:
224
+ file_import.merge(prop.imports())
225
+ if model.is_polymorphic:
226
+ file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB)
227
+ if not model.internal and self.init_line(model):
228
+ file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
229
+ file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
230
+ file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
231
+ return file_import
232
+
233
+ def declare_model(self, model: ModelType) -> str:
234
+ basename = "_model_base.Model"
235
+ if model.parents:
236
+ basename = ", ".join([m.name for m in model.parents])
237
+ if model.discriminator_value:
238
+ basename += f", discriminator='{model.discriminator_value}'"
239
+ return f"class {model.name}({basename}):{model.pylint_disable()}"
240
+
241
+ @staticmethod
242
+ def get_properties_to_declare(model: ModelType) -> List[Property]:
243
+ if model.parents:
244
+ parent_properties = [p for bm in model.parents for p in bm.properties]
245
+ properties_to_declare = [
246
+ p
247
+ for p in model.properties
248
+ if not any(
249
+ p.client_name == pp.client_name
250
+ and p.type_annotation() == pp.type_annotation()
251
+ and not p.is_base_discriminator
252
+ for pp in parent_properties
253
+ )
254
+ ]
255
+ else:
256
+ properties_to_declare = model.properties
257
+ if any(p for p in properties_to_declare if p.client_name == "_"):
258
+ raise ValueError("We do not generate anonymous properties")
259
+ return properties_to_declare
260
+
261
+ @staticmethod
262
+ def declare_property(prop: Property) -> str:
263
+ args = []
264
+ if prop.client_name != prop.wire_name or prop.is_discriminator:
265
+ args.append(f'name="{prop.wire_name}"')
266
+ if prop.visibility:
267
+ v_list = ", ".join(f'"{x}"' for x in prop.visibility)
268
+ args.append(f"visibility=[{v_list}]")
269
+ if prop.client_default_value is not None:
270
+ args.append(f"default={prop.client_default_value_declaration}")
271
+
272
+ if prop.is_multipart_file_input:
273
+ args.append("is_multipart_file_input=True")
274
+ elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
275
+ args.append(f'format="{prop.type.encode}"') # type: ignore
276
+
277
+ if prop.xml_metadata:
278
+ args.append(f"xml={prop.xml_metadata}")
279
+
280
+ field = "rest_discriminator" if prop.is_discriminator else "rest_field"
281
+ type_ignore = (
282
+ " # type: ignore"
283
+ if prop.is_discriminator and isinstance(prop.type, (ConstantType, EnumValue)) and prop.type.value
284
+ else ""
285
+ )
286
+ generated_code = f'{prop.client_name}: {prop.type_annotation()} = {field}({", ".join(args)})'
287
+ # there is 4 spaces indentation so original line length limit 120 - 4 = 116
288
+ pylint_disable = (
289
+ " # pylint: disable=line-too-long"
290
+ if len(generated_code) <= 116 < (len(generated_code) + len(type_ignore))
291
+ else ""
292
+ )
293
+ return f"{generated_code}{type_ignore}{pylint_disable}"
294
+
295
+ def initialize_properties(self, model: ModelType) -> List[str]:
296
+ init_args = []
297
+ for prop in self.get_properties_to_declare(model):
298
+ if prop.constant and not prop.is_base_discriminator:
299
+ init_args.append(f"self.{prop.client_name}: {prop.type_annotation()} = " f"{prop.get_declaration()}")
300
+ return init_args
301
+
302
+ @staticmethod
303
+ def _init_line_parameters(model: ModelType):
304
+ return [
305
+ p
306
+ for p in model.properties
307
+ if p.is_base_discriminator or not p.is_discriminator and not p.constant and p.visibility != ["read"]
308
+ ]
309
+
310
+ @staticmethod
311
+ def properties_to_pass_to_super(model: ModelType) -> str:
312
+ properties_to_pass_to_super = ["*args"]
313
+ for parent in model.parents:
314
+ for prop in model.properties:
315
+ if (
316
+ prop.client_name in [prop.client_name for prop in parent.properties if prop.is_base_discriminator]
317
+ and prop.is_discriminator
318
+ and not prop.constant
319
+ and not prop.readonly
320
+ ):
321
+ properties_to_pass_to_super.append(f"{prop.client_name}={prop.get_declaration()}")
322
+ properties_to_pass_to_super.append("**kwargs")
323
+ return ", ".join(properties_to_pass_to_super)
324
+
325
+ def global_pylint_disables(self) -> str:
326
+ result = []
327
+ for model in self.code_model.model_types:
328
+ if self.need_init(model):
329
+ for item in self.pylint_disable_items(model):
330
+ if item:
331
+ result.append(item)
332
+ final_result = set(result)
333
+ if final_result:
334
+ return "# pylint: disable=" + ", ".join(final_result)
335
+ return ""
@@ -0,0 +1,89 @@
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 Optional, List, Union
7
+ import functools
8
+ from jinja2 import Environment
9
+
10
+ from .utils import get_all_operation_groups_recursively
11
+ from ..models import (
12
+ CodeModel,
13
+ OperationGroup,
14
+ RequestBuilder,
15
+ OverloadedRequestBuilder,
16
+ Client,
17
+ FileImport,
18
+ )
19
+ from .import_serializer import FileImportSerializer
20
+ from .builder_serializer import (
21
+ get_operation_serializer,
22
+ RequestBuilderSerializer,
23
+ )
24
+ from .base_serializer import BaseSerializer
25
+
26
+
27
+ class OperationGroupsSerializer(BaseSerializer):
28
+ def __init__(
29
+ self,
30
+ code_model: CodeModel,
31
+ clients: List[Client],
32
+ env: Environment,
33
+ async_mode: bool,
34
+ operation_group: Optional[OperationGroup] = None,
35
+ ):
36
+ super().__init__(code_model, env)
37
+ self.clients = clients
38
+ self.async_mode = async_mode
39
+ self.operation_group = operation_group
40
+
41
+ def _get_request_builders(
42
+ self, operation_group: OperationGroup
43
+ ) -> List[Union[OverloadedRequestBuilder, RequestBuilder]]:
44
+ return [
45
+ r
46
+ for client in self.clients
47
+ for r in client.request_builders
48
+ if r.client.name == operation_group.client.name
49
+ and r.group_name == operation_group.identify_name
50
+ and not r.is_overload
51
+ and not r.abstract
52
+ and not r.is_lro # lro has already initial builder
53
+ ]
54
+
55
+ def serialize(self) -> str:
56
+ if self.operation_group:
57
+ operation_groups = [self.operation_group]
58
+ else:
59
+ operation_groups = get_all_operation_groups_recursively(self.clients)
60
+
61
+ imports = FileImport(self.code_model)
62
+ for operation_group in operation_groups:
63
+ imports.merge(
64
+ operation_group.imports(
65
+ async_mode=self.async_mode,
66
+ )
67
+ )
68
+
69
+ template = self.env.get_or_select_template("operation_groups_container.py.jinja2")
70
+
71
+ return template.render(
72
+ code_model=self.code_model,
73
+ operation_groups=operation_groups,
74
+ imports=FileImportSerializer(
75
+ imports,
76
+ async_mode=self.async_mode,
77
+ ),
78
+ async_mode=self.async_mode,
79
+ get_operation_serializer=functools.partial(
80
+ get_operation_serializer,
81
+ code_model=self.code_model,
82
+ async_mode=self.async_mode,
83
+ ),
84
+ request_builder_serializer=RequestBuilderSerializer(
85
+ self.code_model,
86
+ async_mode=False,
87
+ ),
88
+ get_request_builders=self._get_request_builders,
89
+ )