@azure-tools/typespec-python 0.24.2 → 0.25.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 (132) hide show
  1. package/dist/src/code-model.d.ts.map +1 -1
  2. package/dist/src/code-model.js +1 -4
  3. package/dist/src/code-model.js.map +1 -1
  4. package/dist/src/emitter.d.ts.map +1 -1
  5. package/dist/src/emitter.js +5 -4
  6. package/dist/src/emitter.js.map +1 -1
  7. package/dist/src/external-process.d.ts +0 -9
  8. package/dist/src/external-process.d.ts.map +1 -1
  9. package/dist/src/external-process.js +1 -25
  10. package/dist/src/external-process.js.map +1 -1
  11. package/dist/src/types.d.ts.map +1 -1
  12. package/dist/src/types.js +2 -0
  13. package/dist/src/types.js.map +1 -1
  14. package/generator/LICENSE +21 -0
  15. package/generator/README.md +1 -0
  16. package/generator/dev_requirements.txt +5 -0
  17. package/generator/pygen/__init__.py +107 -0
  18. package/generator/pygen/_version.py +7 -0
  19. package/generator/pygen/black.py +71 -0
  20. package/generator/pygen/codegen/__init__.py +334 -0
  21. package/generator/pygen/codegen/_utils.py +16 -0
  22. package/generator/pygen/codegen/models/__init__.py +202 -0
  23. package/generator/pygen/codegen/models/base.py +186 -0
  24. package/generator/pygen/codegen/models/base_builder.py +119 -0
  25. package/generator/pygen/codegen/models/client.py +429 -0
  26. package/generator/pygen/codegen/models/code_model.py +239 -0
  27. package/generator/pygen/codegen/models/combined_type.py +149 -0
  28. package/generator/pygen/codegen/models/constant_type.py +129 -0
  29. package/generator/pygen/codegen/models/credential_types.py +221 -0
  30. package/generator/pygen/codegen/models/dictionary_type.py +127 -0
  31. package/generator/pygen/codegen/models/enum_type.py +238 -0
  32. package/generator/pygen/codegen/models/imports.py +291 -0
  33. package/generator/pygen/codegen/models/list_type.py +143 -0
  34. package/generator/pygen/codegen/models/lro_operation.py +143 -0
  35. package/generator/pygen/codegen/models/lro_paging_operation.py +32 -0
  36. package/generator/pygen/codegen/models/model_type.py +361 -0
  37. package/generator/pygen/codegen/models/operation.py +518 -0
  38. package/generator/pygen/codegen/models/operation_group.py +184 -0
  39. package/generator/pygen/codegen/models/paging_operation.py +156 -0
  40. package/generator/pygen/codegen/models/parameter.py +402 -0
  41. package/generator/pygen/codegen/models/parameter_list.py +390 -0
  42. package/generator/pygen/codegen/models/primitive_types.py +626 -0
  43. package/generator/pygen/codegen/models/property.py +175 -0
  44. package/generator/pygen/codegen/models/request_builder.py +189 -0
  45. package/generator/pygen/codegen/models/request_builder_parameter.py +115 -0
  46. package/generator/pygen/codegen/models/response.py +348 -0
  47. package/generator/pygen/codegen/models/utils.py +23 -0
  48. package/generator/pygen/codegen/serializers/__init__.py +570 -0
  49. package/generator/pygen/codegen/serializers/base_serializer.py +21 -0
  50. package/generator/pygen/codegen/serializers/builder_serializer.py +1454 -0
  51. package/generator/pygen/codegen/serializers/client_serializer.py +295 -0
  52. package/generator/pygen/codegen/serializers/enum_serializer.py +15 -0
  53. package/generator/pygen/codegen/serializers/general_serializer.py +212 -0
  54. package/generator/pygen/codegen/serializers/import_serializer.py +127 -0
  55. package/generator/pygen/codegen/serializers/metadata_serializer.py +198 -0
  56. package/generator/pygen/codegen/serializers/model_init_serializer.py +33 -0
  57. package/generator/pygen/codegen/serializers/model_serializer.py +287 -0
  58. package/generator/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  59. package/generator/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  60. package/generator/pygen/codegen/serializers/parameter_serializer.py +221 -0
  61. package/generator/pygen/codegen/serializers/patch_serializer.py +19 -0
  62. package/generator/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  63. package/generator/pygen/codegen/serializers/sample_serializer.py +163 -0
  64. package/generator/pygen/codegen/serializers/test_serializer.py +287 -0
  65. package/generator/pygen/codegen/serializers/types_serializer.py +31 -0
  66. package/generator/pygen/codegen/serializers/utils.py +68 -0
  67. package/generator/pygen/codegen/templates/client.py.jinja2 +37 -0
  68. package/generator/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  69. package/generator/pygen/codegen/templates/config.py.jinja2 +73 -0
  70. package/generator/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  71. package/generator/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  72. package/generator/pygen/codegen/templates/enum.py.jinja2 +13 -0
  73. package/generator/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  74. package/generator/pygen/codegen/templates/init.py.jinja2 +24 -0
  75. package/generator/pygen/codegen/templates/keywords.jinja2 +19 -0
  76. package/generator/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  77. package/generator/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  78. package/generator/pygen/codegen/templates/macros.jinja2 +12 -0
  79. package/generator/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  80. package/generator/pygen/codegen/templates/model_base.py.jinja2 +899 -0
  81. package/generator/pygen/codegen/templates/model_container.py.jinja2 +13 -0
  82. package/generator/pygen/codegen/templates/model_dpg.py.jinja2 +92 -0
  83. package/generator/pygen/codegen/templates/model_init.py.jinja2 +28 -0
  84. package/generator/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  85. package/generator/pygen/codegen/templates/operation.py.jinja2 +21 -0
  86. package/generator/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  87. package/generator/pygen/codegen/templates/operation_groups_container.py.jinja2 +20 -0
  88. package/generator/pygen/codegen/templates/operation_tools.jinja2 +74 -0
  89. package/generator/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  90. package/generator/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  91. package/generator/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  92. package/generator/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  93. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  94. package/generator/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  95. package/generator/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  96. package/generator/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  97. package/generator/pygen/codegen/templates/patch.py.jinja2 +19 -0
  98. package/generator/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  99. package/generator/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  100. package/generator/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  101. package/generator/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  102. package/generator/pygen/codegen/templates/sample.py.jinja2 +44 -0
  103. package/generator/pygen/codegen/templates/serialization.py.jinja2 +2006 -0
  104. package/generator/pygen/codegen/templates/test.py.jinja2 +50 -0
  105. package/generator/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  106. package/generator/pygen/codegen/templates/types.py.jinja2 +8 -0
  107. package/generator/pygen/codegen/templates/validation.py.jinja2 +38 -0
  108. package/generator/pygen/codegen/templates/vendor.py.jinja2 +98 -0
  109. package/generator/pygen/codegen/templates/version.py.jinja2 +4 -0
  110. package/generator/pygen/m2r.py +65 -0
  111. package/generator/pygen/postprocess/__init__.py +183 -0
  112. package/generator/pygen/postprocess/get_all.py +19 -0
  113. package/generator/pygen/postprocess/venvtools.py +77 -0
  114. package/generator/pygen/preprocess/__init__.py +503 -0
  115. package/generator/pygen/preprocess/helpers.py +27 -0
  116. package/generator/pygen/preprocess/python_mappings.py +222 -0
  117. package/generator/pygen/utils.py +149 -0
  118. package/generator/pygen.egg-info/PKG-INFO +25 -0
  119. package/generator/pygen.egg-info/SOURCES.txt +66 -0
  120. package/generator/pygen.egg-info/dependency_links.txt +1 -0
  121. package/generator/pygen.egg-info/requires.txt +4 -0
  122. package/generator/pygen.egg-info/top_level.txt +1 -0
  123. package/generator/requirements.txt +12 -0
  124. package/generator/setup.py +55 -0
  125. package/package.json +10 -7
  126. package/scripts/__pycache__/venvtools.cpython-38.pyc +0 -0
  127. package/scripts/install.py +49 -0
  128. package/scripts/prepare.py +38 -0
  129. package/scripts/run-python3.cjs +22 -0
  130. package/scripts/run_tsp.py +40 -0
  131. package/scripts/system-requirements.cjs +180 -0
  132. package/scripts/venvtools.py +81 -0
@@ -0,0 +1,503 @@
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
+ """The preprocessing autorest plugin.
7
+ """
8
+ import copy
9
+ from typing import Callable, Dict, Any, List, Optional
10
+
11
+ from ..utils import to_snake_case
12
+ from .helpers import (
13
+ add_redefined_builtin_info,
14
+ pad_builtin_namespaces,
15
+ pad_special_chars,
16
+ )
17
+ from .python_mappings import CADL_RESERVED_WORDS, RESERVED_WORDS, PadType
18
+
19
+ from .. import YamlUpdatePlugin
20
+ from ..utils import parse_args, get_body_type_for_description, JSON_REGEXP, KNOWN_TYPES, update_enum_value
21
+
22
+
23
+ def update_overload_section(
24
+ overload: Dict[str, Any],
25
+ yaml_data: Dict[str, Any],
26
+ section: str,
27
+ ):
28
+ try:
29
+ for overload_s, original_s in zip(overload[section], yaml_data[section]):
30
+ if overload_s.get("type"):
31
+ overload_s["type"] = original_s["type"]
32
+ if overload_s.get("headers"):
33
+ for overload_h, original_h in zip(overload_s["headers"], original_s["headers"]):
34
+ if overload_h.get("type"):
35
+ overload_h["type"] = original_h["type"]
36
+ except KeyError as exc:
37
+ raise ValueError(overload["name"]) from exc
38
+
39
+
40
+ def add_overload(yaml_data: Dict[str, Any], body_type: Dict[str, Any], for_flatten_params=False):
41
+ overload = copy.deepcopy(yaml_data)
42
+ overload["isOverload"] = True
43
+ overload["bodyParameter"]["type"] = body_type
44
+ overload["bodyParameter"]["defaultToUnsetSentinel"] = False
45
+ overload["overloads"] = []
46
+ if yaml_data.get("initialOperation"):
47
+ overload["initialOperation"] = yaml_data["initialOperation"]
48
+
49
+ if for_flatten_params:
50
+ overload["bodyParameter"]["flattened"] = True
51
+ else:
52
+ overload["parameters"] = [p for p in overload["parameters"] if not p.get("inFlattenedBody")]
53
+ # for yaml sync, we need to make sure all of the responses, parameters, and exceptions' types have the same yaml id
54
+ for overload_p, original_p in zip(overload["parameters"], yaml_data["parameters"]):
55
+ overload_p["type"] = original_p["type"]
56
+ update_overload_section(overload, yaml_data, "responses")
57
+ update_overload_section(overload, yaml_data, "exceptions")
58
+
59
+ # update content type to be an overloads content type
60
+ content_type_param = next(p for p in overload["parameters"] if p["wireName"].lower() == "content-type")
61
+ content_type_param["inOverload"] = True
62
+ content_type_param["inDocstring"] = True
63
+ body_type_description = get_body_type_for_description(overload["bodyParameter"])
64
+ content_type_param["description"] = (
65
+ f"Body Parameter content-type. Content type parameter for {body_type_description} body."
66
+ )
67
+ content_types = yaml_data["bodyParameter"]["contentTypes"]
68
+ if body_type["type"] == "binary" and len(content_types) > 1:
69
+ content_types = "'" + "', '".join(content_types) + "'"
70
+ content_type_param["description"] += f" Known values are: {content_types}."
71
+ overload["bodyParameter"]["inOverload"] = True
72
+ for parameter in overload["parameters"]:
73
+ parameter["inOverload"] = True
74
+ parameter["defaultToUnsetSentinel"] = False
75
+ return overload
76
+
77
+
78
+ def add_overloads_for_body_param(yaml_data: Dict[str, Any]) -> None:
79
+ """If we added a body parameter type, add overloads for that type"""
80
+ body_parameter = yaml_data["bodyParameter"]
81
+ if not (
82
+ body_parameter["type"]["type"] == "combined"
83
+ and len(yaml_data["bodyParameter"]["type"]["types"]) > len(yaml_data["overloads"])
84
+ ):
85
+ return
86
+ for body_type in body_parameter["type"]["types"]:
87
+ if any(o for o in yaml_data["overloads"] if id(o["bodyParameter"]["type"]) == id(body_type)):
88
+ continue
89
+ yaml_data["overloads"].append(add_overload(yaml_data, body_type))
90
+ if body_type.get("type") == "model" and body_type.get("base") == "json":
91
+ yaml_data["overloads"].append(add_overload(yaml_data, body_type, for_flatten_params=True))
92
+ content_type_param = next(p for p in yaml_data["parameters"] if p["wireName"].lower() == "content-type")
93
+ content_type_param["inOverload"] = False
94
+ content_type_param["inOverriden"] = True
95
+ content_type_param["inDocstring"] = True
96
+ content_type_param["clientDefaultValue"] = (
97
+ None # make it none bc it will be overriden, we depend on default of overloads
98
+ )
99
+ content_type_param["optional"] = True
100
+
101
+
102
+ def update_description(description: Optional[str], default_description: str = "") -> str:
103
+ if not description:
104
+ description = default_description
105
+ description.rstrip(" ")
106
+ if description and description[-1] != ".":
107
+ description += "."
108
+ return description
109
+
110
+
111
+ def update_operation_group_class_name(prefix: str, class_name: str) -> str:
112
+ if class_name == "":
113
+ return prefix + "OperationsMixin"
114
+ if class_name == "Operations":
115
+ return "Operations"
116
+ return class_name + "Operations"
117
+
118
+
119
+ def update_paging_response(yaml_data: Dict[str, Any]) -> None:
120
+ yaml_data["discriminator"] = "paging"
121
+
122
+
123
+ HEADERS_HIDE_IN_METHOD = (
124
+ "repeatability-request-id",
125
+ "repeatability-first-sent",
126
+ "x-ms-client-request-id",
127
+ "client-request-id",
128
+ "return-client-request-id",
129
+ )
130
+ HEADERS_CONVERT_IN_METHOD = {
131
+ "if-match": {
132
+ "clientName": "etag",
133
+ "wireName": "etag",
134
+ "description": "check if resource is changed. Set None to skip checking etag.",
135
+ },
136
+ "if-none-match": {
137
+ "clientName": "match_condition",
138
+ "wireName": "match-condition",
139
+ "description": "The match condition to use upon the etag.",
140
+ "type": {
141
+ "type": "sdkcore",
142
+ "name": "MatchConditions",
143
+ },
144
+ },
145
+ }
146
+
147
+
148
+ def get_wire_name_lower(parameter: Dict[str, Any]) -> str:
149
+ return (parameter.get("wireName") or "").lower()
150
+
151
+
152
+ def headers_convert(yaml_data: Dict[str, Any], replace_data: Any) -> None:
153
+ if isinstance(replace_data, dict):
154
+ for k, v in replace_data.items():
155
+ yaml_data[k] = v
156
+
157
+
158
+ def has_json_content_type(yaml_data: Dict[str, Any]) -> bool:
159
+ return any(ct for ct in yaml_data.get("contentTypes", []) if JSON_REGEXP.match(ct))
160
+
161
+
162
+ def has_multi_part_content_type(yaml_data: Dict[str, Any]) -> bool:
163
+ return any(ct for ct in yaml_data.get("contentTypes", []) if ct == "multipart/form-data")
164
+
165
+
166
+ class PreProcessPlugin(YamlUpdatePlugin): # pylint: disable=abstract-method
167
+ """Add Python naming information."""
168
+
169
+ @property
170
+ def azure_arm(self) -> bool:
171
+ return self.options.get("azure-arm", False)
172
+
173
+ @property
174
+ def version_tolerant(self) -> bool:
175
+ return self.options.get("version-tolerant", True)
176
+
177
+ @property
178
+ def models_mode(self) -> Optional[str]:
179
+ return self.options.get("models-mode", "dpg" if self.is_cadl else None)
180
+
181
+ @property
182
+ def is_cadl(self) -> bool:
183
+ return self.options.get("cadl_file", False)
184
+
185
+ def add_body_param_type(
186
+ self,
187
+ code_model: Dict[str, Any],
188
+ body_parameter: Dict[str, Any],
189
+ ):
190
+ # only add overload for special content type
191
+ if ( # pylint: disable=too-many-boolean-expressions
192
+ body_parameter
193
+ and body_parameter["type"]["type"] in ("model", "dict", "list")
194
+ and (
195
+ has_json_content_type(body_parameter) or (self.is_cadl and has_multi_part_content_type(body_parameter))
196
+ )
197
+ and not body_parameter["type"].get("xmlMetadata")
198
+ and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
199
+ ):
200
+ origin_type = body_parameter["type"]["type"]
201
+ is_dpg_model = body_parameter["type"].get("base") == "dpg"
202
+ body_parameter["type"] = {
203
+ "type": "combined",
204
+ "types": [body_parameter["type"]],
205
+ }
206
+ # don't add binary overload for multipart content type
207
+ if not (self.is_cadl and has_multi_part_content_type(body_parameter)):
208
+ body_parameter["type"]["types"].append(KNOWN_TYPES["binary"])
209
+
210
+ if origin_type == "model" and is_dpg_model and self.models_mode == "dpg":
211
+ body_parameter["type"]["types"].insert(1, KNOWN_TYPES["any-object"])
212
+ code_model["types"].append(body_parameter["type"])
213
+
214
+ def pad_reserved_words(self, name: str, pad_type: PadType):
215
+ # we want to pad hidden variables as well
216
+ if not name:
217
+ # we'll pass in empty operation groups sometime etc.
218
+ return name
219
+
220
+ if self.is_cadl:
221
+ reserved_words = {k: (v + CADL_RESERVED_WORDS.get(k, [])) for k, v in RESERVED_WORDS.items()}
222
+ else:
223
+ reserved_words = RESERVED_WORDS
224
+ name = pad_special_chars(name)
225
+ name_prefix = "_" if name[0] == "_" else ""
226
+ name = name[1:] if name[0] == "_" else name
227
+ if name.lower() in reserved_words[pad_type]:
228
+ return name_prefix + name + pad_type
229
+ return name_prefix + name
230
+
231
+ def update_types(self, yaml_data: List[Dict[str, Any]]) -> None:
232
+ for type in yaml_data:
233
+ for property in type.get("properties", []):
234
+ property["description"] = update_description(property.get("description", ""))
235
+ property["clientName"] = self.pad_reserved_words(property["clientName"].lower(), PadType.PROPERTY)
236
+ add_redefined_builtin_info(property["clientName"], property)
237
+ if type.get("name"):
238
+ name = self.pad_reserved_words(type["name"], PadType.MODEL)
239
+ type["name"] = name[0].upper() + name[1:]
240
+ type["description"] = update_description(type.get("description", ""), type["name"])
241
+ type["snakeCaseName"] = to_snake_case(type["name"])
242
+ if type.get("values"):
243
+ # we're enums
244
+ values_to_add = []
245
+ for value in type["values"]:
246
+ padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM).upper()
247
+ if self.version_tolerant:
248
+ if padded_name[0] in "0123456789":
249
+ padded_name = "ENUM_" + padded_name
250
+ value["name"] = padded_name
251
+ else:
252
+ if value["name"] != padded_name:
253
+ values_to_add.append(
254
+ update_enum_value(
255
+ name=padded_name,
256
+ value=value["value"],
257
+ description=value["description"],
258
+ enum_type=value["enumType"],
259
+ )
260
+ )
261
+ type["values"].extend(values_to_add)
262
+
263
+ # add type for reference
264
+ for v in HEADERS_CONVERT_IN_METHOD.values():
265
+ if isinstance(v, dict) and "type" in v:
266
+ yaml_data.append(v["type"])
267
+
268
+ def update_client(self, yaml_data: Dict[str, Any]) -> None:
269
+ yaml_data["description"] = update_description(yaml_data["description"], default_description=yaml_data["name"])
270
+ yaml_data["legacyFilename"] = to_snake_case(yaml_data["name"].replace(" ", "_"))
271
+ parameters = yaml_data["parameters"]
272
+ for parameter in parameters:
273
+ self.update_parameter(parameter)
274
+ if parameter["clientName"] == "credential":
275
+ policy = parameter["type"].get("policy")
276
+ if policy and policy["type"] == "BearerTokenCredentialPolicy" and self.azure_arm:
277
+ policy["type"] = "ARMChallengeAuthenticationPolicy"
278
+ policy["credentialScopes"] = ["https://management.azure.com/.default"]
279
+ if (
280
+ (not self.version_tolerant or self.azure_arm)
281
+ and parameters
282
+ and parameters[-1]["clientName"] == "credential"
283
+ ):
284
+ # we need to move credential to the front in mgmt mode for backcompat reasons
285
+ yaml_data["parameters"] = [parameters[-1]] + parameters[:-1]
286
+ prop_name = yaml_data["name"]
287
+ if prop_name.endswith("Client"):
288
+ prop_name = prop_name[: len(prop_name) - len("Client")]
289
+ yaml_data["builderPadName"] = to_snake_case(prop_name)
290
+ for og in yaml_data.get("operationGroups", []):
291
+ for o in og["operations"]:
292
+ property_if_match = None
293
+ property_if_none_match = None
294
+ for p in o["parameters"]:
295
+ wire_name_lower = get_wire_name_lower(p)
296
+ if p["location"] == "header" and wire_name_lower == "client-request-id":
297
+ yaml_data["requestIdHeaderName"] = wire_name_lower
298
+ if self.version_tolerant and p["location"] == "header":
299
+ if wire_name_lower == "if-match":
300
+ property_if_match = p
301
+ elif wire_name_lower == "if-none-match":
302
+ property_if_none_match = p
303
+ # pylint: disable=line-too-long
304
+ # some service(e.g. https://github.com/Azure/azure-rest-api-specs/blob/main/specification/cosmos-db/data-plane/Microsoft.Tables/preview/2019-02-02/table.json)
305
+ # only has one, so we need to add "if-none-match" or "if-match" if it's missing
306
+ if not property_if_match and property_if_none_match:
307
+ property_if_match = property_if_none_match.copy()
308
+ property_if_match["wireName"] = "if-match"
309
+ if not property_if_none_match and property_if_match:
310
+ property_if_none_match = property_if_match.copy()
311
+ property_if_none_match["wireName"] = "if-none-match"
312
+
313
+ if property_if_match and property_if_none_match:
314
+ # arrange if-match and if-none-match to the end of parameters
315
+ o["parameters"] = [
316
+ item
317
+ for item in o["parameters"]
318
+ if get_wire_name_lower(item) not in ("if-match", "if-none-match")
319
+ ] + [property_if_match, property_if_none_match]
320
+
321
+ o["hasEtag"] = True
322
+ yaml_data["hasEtag"] = True
323
+
324
+ def get_operation_updater(self, yaml_data: Dict[str, Any]) -> Callable[[Dict[str, Any], Dict[str, Any]], None]:
325
+ if yaml_data["discriminator"] == "lropaging":
326
+ return self.update_lro_paging_operation
327
+ if yaml_data["discriminator"] == "lro":
328
+ return self.update_lro_operation
329
+ if yaml_data["discriminator"] == "paging":
330
+ return self.update_paging_operation
331
+ return self.update_operation
332
+
333
+ def update_parameter(self, yaml_data: Dict[str, Any]) -> None:
334
+ yaml_data["description"] = update_description(yaml_data.get("description", ""))
335
+ if not (yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")):
336
+ yaml_data["clientName"] = self.pad_reserved_words(yaml_data["clientName"].lower(), PadType.PARAMETER)
337
+ if yaml_data.get("propertyToParameterName"):
338
+ # need to create a new one with padded keys and values
339
+ yaml_data["propertyToParameterName"] = {
340
+ self.pad_reserved_words(prop, PadType.PROPERTY): self.pad_reserved_words(
341
+ param_name, PadType.PARAMETER
342
+ ).lower()
343
+ for prop, param_name in yaml_data["propertyToParameterName"].items()
344
+ }
345
+ wire_name_lower = (yaml_data.get("wireName") or "").lower()
346
+ if yaml_data["location"] == "header" and (
347
+ wire_name_lower in HEADERS_HIDE_IN_METHOD or yaml_data.get("clientDefaultValue") == "multipart/form-data"
348
+ ):
349
+ yaml_data["hideInMethod"] = True
350
+ if self.version_tolerant and yaml_data["location"] == "header" and wire_name_lower in HEADERS_CONVERT_IN_METHOD:
351
+ headers_convert(yaml_data, HEADERS_CONVERT_IN_METHOD[wire_name_lower])
352
+ if wire_name_lower in ["$host", "content-type", "accept"] and yaml_data["type"]["type"] == "constant":
353
+ yaml_data["clientDefaultValue"] = yaml_data["type"]["value"]
354
+
355
+ def update_operation(
356
+ self,
357
+ code_model: Dict[str, Any],
358
+ yaml_data: Dict[str, Any],
359
+ *,
360
+ is_overload: bool = False,
361
+ ) -> None:
362
+ yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP)
363
+ yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
364
+ yaml_data["name"] = yaml_data["name"].lower()
365
+ yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
366
+ yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
367
+ yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
368
+ body_parameter = yaml_data.get("bodyParameter")
369
+ for parameter in yaml_data["parameters"]:
370
+ self.update_parameter(parameter)
371
+ if yaml_data.get("bodyParameter"):
372
+ self.update_parameter(yaml_data["bodyParameter"])
373
+ for entry in yaml_data["bodyParameter"].get("entries", []):
374
+ self.update_parameter(entry)
375
+ for overload in yaml_data.get("overloads", []):
376
+ self.update_operation(code_model, overload, is_overload=True)
377
+ for response in yaml_data.get("responses", []):
378
+ response["discriminator"] = "operation"
379
+ if body_parameter and not is_overload:
380
+ # if we have a JSON body, we add a binary overload
381
+ self.add_body_param_type(code_model, body_parameter)
382
+ add_overloads_for_body_param(yaml_data)
383
+
384
+ def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
385
+ for response in yaml_data.get("responses", []):
386
+ response["discriminator"] = "lro"
387
+ response["pollerSync"] = response.get("pollerSync") or "azure.core.polling.LROPoller"
388
+ response["pollerAsync"] = response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
389
+ if not response.get("pollingMethodSync"):
390
+ response["pollingMethodSync"] = (
391
+ "azure.mgmt.core.polling.arm_polling.ARMPolling"
392
+ if self.azure_arm
393
+ else "azure.core.polling.base_polling.LROBasePolling"
394
+ )
395
+ if not response.get("pollingMethodAsync"):
396
+ response["pollingMethodAsync"] = (
397
+ "azure.mgmt.core.polling.async_arm_polling.AsyncARMPolling"
398
+ if self.azure_arm
399
+ else "azure.core.polling.async_base_polling.AsyncLROBasePolling"
400
+ )
401
+
402
+ def update_lro_paging_operation(
403
+ self,
404
+ code_model: Dict[str, Any],
405
+ yaml_data: Dict[str, Any],
406
+ is_overload: bool = False,
407
+ item_type: Optional[Dict[str, Any]] = None,
408
+ ) -> None:
409
+ self.update_lro_operation(code_model, yaml_data, is_overload=is_overload)
410
+ self.update_paging_operation(code_model, yaml_data, is_overload=is_overload, item_type=item_type)
411
+ yaml_data["discriminator"] = "lropaging"
412
+ for response in yaml_data.get("responses", []):
413
+ response["discriminator"] = "lropaging"
414
+ for overload in yaml_data.get("overloads", []):
415
+ self.update_lro_paging_operation(
416
+ code_model,
417
+ overload,
418
+ is_overload=True,
419
+ item_type=yaml_data["responses"][0]["itemType"],
420
+ )
421
+
422
+ def update_lro_operation(
423
+ self,
424
+ code_model: Dict[str, Any],
425
+ yaml_data: Dict[str, Any],
426
+ is_overload: bool = False,
427
+ ) -> None:
428
+ def convert_initial_operation_response_type(data: Dict[str, Any]) -> None:
429
+ for response in data.get("responses", []):
430
+ response["type"] = KNOWN_TYPES["binary"]
431
+
432
+ self.update_operation(code_model, yaml_data, is_overload=is_overload)
433
+ self.update_operation(code_model, yaml_data["initialOperation"], is_overload=is_overload)
434
+ convert_initial_operation_response_type(yaml_data["initialOperation"])
435
+ self._update_lro_operation_helper(yaml_data)
436
+ for overload in yaml_data.get("overloads", []):
437
+ self._update_lro_operation_helper(overload)
438
+ self.update_operation(code_model, overload["initialOperation"], is_overload=True)
439
+ convert_initial_operation_response_type(overload["initialOperation"])
440
+
441
+ def update_paging_operation(
442
+ self,
443
+ code_model: Dict[str, Any],
444
+ yaml_data: Dict[str, Any],
445
+ is_overload: bool = False,
446
+ item_type: Optional[Dict[str, Any]] = None,
447
+ ) -> None:
448
+ self.update_operation(code_model, yaml_data, is_overload=is_overload)
449
+ item_type = item_type or yaml_data["itemType"]["elementType"]
450
+ if yaml_data.get("nextOperation"):
451
+ yaml_data["nextOperation"]["groupName"] = self.pad_reserved_words(
452
+ yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP
453
+ )
454
+ yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
455
+ for response in yaml_data["nextOperation"].get("responses", []):
456
+ update_paging_response(response)
457
+ response["itemType"] = item_type
458
+ for response in yaml_data.get("responses", []):
459
+ update_paging_response(response)
460
+ response["itemType"] = item_type
461
+ for overload in yaml_data.get("overloads", []):
462
+ self.update_paging_operation(code_model, overload, is_overload=True, item_type=item_type)
463
+
464
+ def update_operation_groups(self, code_model: Dict[str, Any], client: Dict[str, Any]) -> None:
465
+ operation_groups_yaml_data = client.get("operationGroups", [])
466
+ for operation_group in operation_groups_yaml_data:
467
+ operation_group["identifyName"] = self.pad_reserved_words(
468
+ operation_group.get("name", operation_group["propertyName"]),
469
+ PadType.OPERATION_GROUP,
470
+ )
471
+ operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
472
+ operation_group["propertyName"] = self.pad_reserved_words(
473
+ operation_group["propertyName"], PadType.OPERATION_GROUP
474
+ )
475
+ operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
476
+ operation_group["className"] = update_operation_group_class_name(
477
+ client["name"], operation_group["className"]
478
+ )
479
+ for operation in operation_group["operations"]:
480
+ self.get_operation_updater(operation)(code_model, operation)
481
+
482
+ if operation_group.get("operationGroups"):
483
+ self.update_operation_groups(code_model, operation_group)
484
+
485
+ def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
486
+ """Convert in place the YAML str."""
487
+ self.update_types(yaml_data["types"])
488
+ yaml_data["types"] += KNOWN_TYPES.values()
489
+ for client in yaml_data["clients"]:
490
+ self.update_client(client)
491
+ self.update_operation_groups(yaml_data, client)
492
+ for clients in yaml_data["subnamespaceToClients"].values():
493
+ for client in clients:
494
+ self.update_client(client)
495
+ self.update_operation_groups(yaml_data, client)
496
+ if yaml_data.get("namespace"):
497
+ yaml_data["namespace"] = pad_builtin_namespaces(yaml_data["namespace"])
498
+
499
+
500
+ if __name__ == "__main__":
501
+ # CADL pipeline will call this
502
+ args, unknown_args = parse_args()
503
+ PreProcessPlugin(output_folder=args.output_folder, cadl_file=args.cadl_file, **unknown_args).process()
@@ -0,0 +1,27 @@
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 re
7
+ from typing import Any, Dict
8
+ from .python_mappings import (
9
+ REDEFINED_BUILTINS,
10
+ BUILTIN_PACKAGES,
11
+ )
12
+
13
+
14
+ def add_redefined_builtin_info(name: str, yaml_data: Dict[str, Any]) -> None:
15
+ if name in REDEFINED_BUILTINS:
16
+ yaml_data["pylintDisable"] = "redefined-builtin"
17
+
18
+
19
+ def pad_builtin_namespaces(namespace: str) -> str:
20
+ items = namespace.split(".")
21
+ if items[0] in BUILTIN_PACKAGES:
22
+ items[0] = items[0] + "_"
23
+ return ".".join(items)
24
+
25
+
26
+ def pad_special_chars(name: str) -> str:
27
+ return re.sub(r"[^A-z0-9_]", "_", name)