@azure-tools/typespec-python 0.24.3 → 0.26.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 (137) hide show
  1. package/dist/src/code-model.d.ts.map +1 -1
  2. package/dist/src/code-model.js +3 -6
  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 +6 -5
  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/http.d.ts.map +1 -1
  12. package/dist/src/http.js +2 -1
  13. package/dist/src/http.js.map +1 -1
  14. package/dist/src/types.d.ts.map +1 -1
  15. package/dist/src/types.js +2 -0
  16. package/dist/src/types.js.map +1 -1
  17. package/dist/src/utils.js +1 -1
  18. package/dist/src/utils.js.map +1 -1
  19. package/generator/LICENSE +21 -0
  20. package/generator/README.md +1 -0
  21. package/generator/dev_requirements.txt +5 -0
  22. package/generator/pygen/__init__.py +107 -0
  23. package/generator/pygen/_version.py +7 -0
  24. package/generator/pygen/black.py +71 -0
  25. package/generator/pygen/codegen/__init__.py +338 -0
  26. package/generator/pygen/codegen/_utils.py +16 -0
  27. package/generator/pygen/codegen/models/__init__.py +202 -0
  28. package/generator/pygen/codegen/models/base.py +186 -0
  29. package/generator/pygen/codegen/models/base_builder.py +119 -0
  30. package/generator/pygen/codegen/models/client.py +430 -0
  31. package/generator/pygen/codegen/models/code_model.py +239 -0
  32. package/generator/pygen/codegen/models/combined_type.py +149 -0
  33. package/generator/pygen/codegen/models/constant_type.py +129 -0
  34. package/generator/pygen/codegen/models/credential_types.py +221 -0
  35. package/generator/pygen/codegen/models/dictionary_type.py +127 -0
  36. package/generator/pygen/codegen/models/enum_type.py +238 -0
  37. package/generator/pygen/codegen/models/imports.py +291 -0
  38. package/generator/pygen/codegen/models/list_type.py +143 -0
  39. package/generator/pygen/codegen/models/lro_operation.py +143 -0
  40. package/generator/pygen/codegen/models/lro_paging_operation.py +32 -0
  41. package/generator/pygen/codegen/models/model_type.py +361 -0
  42. package/generator/pygen/codegen/models/operation.py +520 -0
  43. package/generator/pygen/codegen/models/operation_group.py +184 -0
  44. package/generator/pygen/codegen/models/paging_operation.py +156 -0
  45. package/generator/pygen/codegen/models/parameter.py +402 -0
  46. package/generator/pygen/codegen/models/parameter_list.py +390 -0
  47. package/generator/pygen/codegen/models/primitive_types.py +626 -0
  48. package/generator/pygen/codegen/models/property.py +175 -0
  49. package/generator/pygen/codegen/models/request_builder.py +189 -0
  50. package/generator/pygen/codegen/models/request_builder_parameter.py +115 -0
  51. package/generator/pygen/codegen/models/response.py +348 -0
  52. package/generator/pygen/codegen/models/utils.py +23 -0
  53. package/generator/pygen/codegen/serializers/__init__.py +574 -0
  54. package/generator/pygen/codegen/serializers/base_serializer.py +21 -0
  55. package/generator/pygen/codegen/serializers/builder_serializer.py +1473 -0
  56. package/generator/pygen/codegen/serializers/client_serializer.py +295 -0
  57. package/generator/pygen/codegen/serializers/enum_serializer.py +15 -0
  58. package/generator/pygen/codegen/serializers/general_serializer.py +212 -0
  59. package/generator/pygen/codegen/serializers/import_serializer.py +127 -0
  60. package/generator/pygen/codegen/serializers/metadata_serializer.py +198 -0
  61. package/generator/pygen/codegen/serializers/model_init_serializer.py +33 -0
  62. package/generator/pygen/codegen/serializers/model_serializer.py +287 -0
  63. package/generator/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  64. package/generator/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  65. package/generator/pygen/codegen/serializers/parameter_serializer.py +221 -0
  66. package/generator/pygen/codegen/serializers/patch_serializer.py +19 -0
  67. package/generator/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  68. package/generator/pygen/codegen/serializers/sample_serializer.py +163 -0
  69. package/generator/pygen/codegen/serializers/test_serializer.py +287 -0
  70. package/generator/pygen/codegen/serializers/types_serializer.py +31 -0
  71. package/generator/pygen/codegen/serializers/utils.py +68 -0
  72. package/generator/pygen/codegen/templates/client.py.jinja2 +37 -0
  73. package/generator/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  74. package/generator/pygen/codegen/templates/config.py.jinja2 +73 -0
  75. package/generator/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  76. package/generator/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  77. package/generator/pygen/codegen/templates/enum.py.jinja2 +13 -0
  78. package/generator/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  79. package/generator/pygen/codegen/templates/init.py.jinja2 +24 -0
  80. package/generator/pygen/codegen/templates/keywords.jinja2 +19 -0
  81. package/generator/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  82. package/generator/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  83. package/generator/pygen/codegen/templates/macros.jinja2 +12 -0
  84. package/generator/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  85. package/generator/pygen/codegen/templates/model_base.py.jinja2 +899 -0
  86. package/generator/pygen/codegen/templates/model_container.py.jinja2 +13 -0
  87. package/generator/pygen/codegen/templates/model_dpg.py.jinja2 +92 -0
  88. package/generator/pygen/codegen/templates/model_init.py.jinja2 +28 -0
  89. package/generator/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  90. package/generator/pygen/codegen/templates/operation.py.jinja2 +21 -0
  91. package/generator/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  92. package/generator/pygen/codegen/templates/operation_groups_container.py.jinja2 +20 -0
  93. package/generator/pygen/codegen/templates/operation_tools.jinja2 +74 -0
  94. package/generator/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  95. package/generator/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  96. package/generator/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  97. package/generator/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  98. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  99. package/generator/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  100. package/generator/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  101. package/generator/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  102. package/generator/pygen/codegen/templates/patch.py.jinja2 +19 -0
  103. package/generator/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  104. package/generator/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  105. package/generator/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  106. package/generator/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  107. package/generator/pygen/codegen/templates/sample.py.jinja2 +44 -0
  108. package/generator/pygen/codegen/templates/serialization.py.jinja2 +2006 -0
  109. package/generator/pygen/codegen/templates/test.py.jinja2 +50 -0
  110. package/generator/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  111. package/generator/pygen/codegen/templates/types.py.jinja2 +8 -0
  112. package/generator/pygen/codegen/templates/validation.py.jinja2 +38 -0
  113. package/generator/pygen/codegen/templates/vendor.py.jinja2 +98 -0
  114. package/generator/pygen/codegen/templates/version.py.jinja2 +4 -0
  115. package/generator/pygen/m2r.py +65 -0
  116. package/generator/pygen/postprocess/__init__.py +183 -0
  117. package/generator/pygen/postprocess/get_all.py +19 -0
  118. package/generator/pygen/postprocess/venvtools.py +77 -0
  119. package/generator/pygen/preprocess/__init__.py +509 -0
  120. package/generator/pygen/preprocess/helpers.py +27 -0
  121. package/generator/pygen/preprocess/python_mappings.py +224 -0
  122. package/generator/pygen/utils.py +153 -0
  123. package/generator/pygen.egg-info/PKG-INFO +25 -0
  124. package/generator/pygen.egg-info/SOURCES.txt +66 -0
  125. package/generator/pygen.egg-info/dependency_links.txt +1 -0
  126. package/generator/pygen.egg-info/requires.txt +4 -0
  127. package/generator/pygen.egg-info/top_level.txt +1 -0
  128. package/generator/requirements.txt +12 -0
  129. package/generator/setup.py +55 -0
  130. package/package.json +31 -26
  131. package/scripts/__pycache__/venvtools.cpython-38.pyc +0 -0
  132. package/scripts/install.py +49 -0
  133. package/scripts/prepare.py +38 -0
  134. package/scripts/run-python3.cjs +22 -0
  135. package/scripts/run_tsp.py +40 -0
  136. package/scripts/system-requirements.cjs +180 -0
  137. package/scripts/venvtools.py +81 -0
@@ -0,0 +1,509 @@
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, extract_original_name
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
+ pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
239
+ name = self.pad_reserved_words(type["name"], pad_type)
240
+ type["name"] = name[0].upper() + name[1:]
241
+ type["description"] = update_description(type.get("description", ""), type["name"])
242
+ type["snakeCaseName"] = to_snake_case(type["name"])
243
+ if type.get("values"):
244
+ # we're enums
245
+ values_to_add = []
246
+ for value in type["values"]:
247
+ padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE).upper()
248
+ if self.version_tolerant:
249
+ if padded_name[0] in "0123456789":
250
+ padded_name = "ENUM_" + padded_name
251
+ value["name"] = padded_name
252
+ else:
253
+ if value["name"] != padded_name:
254
+ values_to_add.append(
255
+ update_enum_value(
256
+ name=padded_name,
257
+ value=value["value"],
258
+ description=value["description"],
259
+ enum_type=value["enumType"],
260
+ )
261
+ )
262
+ type["values"].extend(values_to_add)
263
+
264
+ # add type for reference
265
+ for v in HEADERS_CONVERT_IN_METHOD.values():
266
+ if isinstance(v, dict) and "type" in v:
267
+ yaml_data.append(v["type"])
268
+
269
+ def update_client(self, yaml_data: Dict[str, Any]) -> None:
270
+ yaml_data["description"] = update_description(yaml_data["description"], default_description=yaml_data["name"])
271
+ yaml_data["legacyFilename"] = to_snake_case(yaml_data["name"].replace(" ", "_"))
272
+ parameters = yaml_data["parameters"]
273
+ for parameter in parameters:
274
+ self.update_parameter(parameter)
275
+ if parameter["clientName"] == "credential":
276
+ policy = parameter["type"].get("policy")
277
+ if policy and policy["type"] == "BearerTokenCredentialPolicy" and self.azure_arm:
278
+ policy["type"] = "ARMChallengeAuthenticationPolicy"
279
+ policy["credentialScopes"] = ["https://management.azure.com/.default"]
280
+ if (
281
+ (not self.version_tolerant or self.azure_arm)
282
+ and parameters
283
+ and parameters[-1]["clientName"] == "credential"
284
+ ):
285
+ # we need to move credential to the front in mgmt mode for backcompat reasons
286
+ yaml_data["parameters"] = [parameters[-1]] + parameters[:-1]
287
+ prop_name = yaml_data["name"]
288
+ if prop_name.endswith("Client"):
289
+ prop_name = prop_name[: len(prop_name) - len("Client")]
290
+ yaml_data["builderPadName"] = to_snake_case(prop_name)
291
+ for og in yaml_data.get("operationGroups", []):
292
+ for o in og["operations"]:
293
+ property_if_match = None
294
+ property_if_none_match = None
295
+ for p in o["parameters"]:
296
+ wire_name_lower = get_wire_name_lower(p)
297
+ if p["location"] == "header" and wire_name_lower == "client-request-id":
298
+ yaml_data["requestIdHeaderName"] = wire_name_lower
299
+ if self.version_tolerant and p["location"] == "header":
300
+ if wire_name_lower == "if-match":
301
+ property_if_match = p
302
+ elif wire_name_lower == "if-none-match":
303
+ property_if_none_match = p
304
+ # pylint: disable=line-too-long
305
+ # 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)
306
+ # only has one, so we need to add "if-none-match" or "if-match" if it's missing
307
+ if not property_if_match and property_if_none_match:
308
+ property_if_match = property_if_none_match.copy()
309
+ property_if_match["wireName"] = "if-match"
310
+ if not property_if_none_match and property_if_match:
311
+ property_if_none_match = property_if_match.copy()
312
+ property_if_none_match["wireName"] = "if-none-match"
313
+
314
+ if property_if_match and property_if_none_match:
315
+ # arrange if-match and if-none-match to the end of parameters
316
+ o["parameters"] = [
317
+ item
318
+ for item in o["parameters"]
319
+ if get_wire_name_lower(item) not in ("if-match", "if-none-match")
320
+ ] + [property_if_match, property_if_none_match]
321
+
322
+ o["hasEtag"] = True
323
+ yaml_data["hasEtag"] = True
324
+
325
+ def get_operation_updater(self, yaml_data: Dict[str, Any]) -> Callable[[Dict[str, Any], Dict[str, Any]], None]:
326
+ if yaml_data["discriminator"] == "lropaging":
327
+ return self.update_lro_paging_operation
328
+ if yaml_data["discriminator"] == "lro":
329
+ return self.update_lro_operation
330
+ if yaml_data["discriminator"] == "paging":
331
+ return self.update_paging_operation
332
+ return self.update_operation
333
+
334
+ def update_parameter(self, yaml_data: Dict[str, Any]) -> None:
335
+ yaml_data["description"] = update_description(yaml_data.get("description", ""))
336
+ if not (yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")):
337
+ yaml_data["clientName"] = self.pad_reserved_words(yaml_data["clientName"].lower(), PadType.PARAMETER)
338
+ if yaml_data.get("propertyToParameterName"):
339
+ # need to create a new one with padded keys and values
340
+ yaml_data["propertyToParameterName"] = {
341
+ self.pad_reserved_words(prop, PadType.PROPERTY): self.pad_reserved_words(
342
+ param_name, PadType.PARAMETER
343
+ ).lower()
344
+ for prop, param_name in yaml_data["propertyToParameterName"].items()
345
+ }
346
+ wire_name_lower = (yaml_data.get("wireName") or "").lower()
347
+ if yaml_data["location"] == "header" and (
348
+ wire_name_lower in HEADERS_HIDE_IN_METHOD or yaml_data.get("clientDefaultValue") == "multipart/form-data"
349
+ ):
350
+ yaml_data["hideInMethod"] = True
351
+ if self.version_tolerant and yaml_data["location"] == "header" and wire_name_lower in HEADERS_CONVERT_IN_METHOD:
352
+ headers_convert(yaml_data, HEADERS_CONVERT_IN_METHOD[wire_name_lower])
353
+ if wire_name_lower in ["$host", "content-type", "accept"] and yaml_data["type"]["type"] == "constant":
354
+ yaml_data["clientDefaultValue"] = yaml_data["type"]["value"]
355
+
356
+ def update_operation(
357
+ self,
358
+ code_model: Dict[str, Any],
359
+ yaml_data: Dict[str, Any],
360
+ *,
361
+ is_overload: bool = False,
362
+ ) -> None:
363
+ yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP)
364
+ yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
365
+ yaml_data["name"] = yaml_data["name"].lower()
366
+ if yaml_data.get("isLroInitialOperation") is True:
367
+ yaml_data["name"] = (
368
+ "_" + self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD) + "_initial"
369
+ )
370
+ else:
371
+ yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
372
+ yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
373
+ yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
374
+ body_parameter = yaml_data.get("bodyParameter")
375
+ for parameter in yaml_data["parameters"]:
376
+ self.update_parameter(parameter)
377
+ if yaml_data.get("bodyParameter"):
378
+ self.update_parameter(yaml_data["bodyParameter"])
379
+ for entry in yaml_data["bodyParameter"].get("entries", []):
380
+ self.update_parameter(entry)
381
+ for overload in yaml_data.get("overloads", []):
382
+ self.update_operation(code_model, overload, is_overload=True)
383
+ for response in yaml_data.get("responses", []):
384
+ response["discriminator"] = "operation"
385
+ if body_parameter and not is_overload:
386
+ # if we have a JSON body, we add a binary overload
387
+ self.add_body_param_type(code_model, body_parameter)
388
+ add_overloads_for_body_param(yaml_data)
389
+
390
+ def _update_lro_operation_helper(self, yaml_data: Dict[str, Any]) -> None:
391
+ for response in yaml_data.get("responses", []):
392
+ response["discriminator"] = "lro"
393
+ response["pollerSync"] = response.get("pollerSync") or "azure.core.polling.LROPoller"
394
+ response["pollerAsync"] = response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
395
+ if not response.get("pollingMethodSync"):
396
+ response["pollingMethodSync"] = (
397
+ "azure.mgmt.core.polling.arm_polling.ARMPolling"
398
+ if self.azure_arm
399
+ else "azure.core.polling.base_polling.LROBasePolling"
400
+ )
401
+ if not response.get("pollingMethodAsync"):
402
+ response["pollingMethodAsync"] = (
403
+ "azure.mgmt.core.polling.async_arm_polling.AsyncARMPolling"
404
+ if self.azure_arm
405
+ else "azure.core.polling.async_base_polling.AsyncLROBasePolling"
406
+ )
407
+
408
+ def update_lro_paging_operation(
409
+ self,
410
+ code_model: Dict[str, Any],
411
+ yaml_data: Dict[str, Any],
412
+ is_overload: bool = False,
413
+ item_type: Optional[Dict[str, Any]] = None,
414
+ ) -> None:
415
+ self.update_lro_operation(code_model, yaml_data, is_overload=is_overload)
416
+ self.update_paging_operation(code_model, yaml_data, is_overload=is_overload, item_type=item_type)
417
+ yaml_data["discriminator"] = "lropaging"
418
+ for response in yaml_data.get("responses", []):
419
+ response["discriminator"] = "lropaging"
420
+ for overload in yaml_data.get("overloads", []):
421
+ self.update_lro_paging_operation(
422
+ code_model,
423
+ overload,
424
+ is_overload=True,
425
+ item_type=yaml_data["responses"][0]["itemType"],
426
+ )
427
+
428
+ def update_lro_operation(
429
+ self,
430
+ code_model: Dict[str, Any],
431
+ yaml_data: Dict[str, Any],
432
+ is_overload: bool = False,
433
+ ) -> None:
434
+ def convert_initial_operation_response_type(data: Dict[str, Any]) -> None:
435
+ for response in data.get("responses", []):
436
+ response["type"] = KNOWN_TYPES["binary"]
437
+
438
+ self.update_operation(code_model, yaml_data, is_overload=is_overload)
439
+ self.update_operation(code_model, yaml_data["initialOperation"], is_overload=is_overload)
440
+ convert_initial_operation_response_type(yaml_data["initialOperation"])
441
+ self._update_lro_operation_helper(yaml_data)
442
+ for overload in yaml_data.get("overloads", []):
443
+ self._update_lro_operation_helper(overload)
444
+ self.update_operation(code_model, overload["initialOperation"], is_overload=True)
445
+ convert_initial_operation_response_type(overload["initialOperation"])
446
+
447
+ def update_paging_operation(
448
+ self,
449
+ code_model: Dict[str, Any],
450
+ yaml_data: Dict[str, Any],
451
+ is_overload: bool = False,
452
+ item_type: Optional[Dict[str, Any]] = None,
453
+ ) -> None:
454
+ self.update_operation(code_model, yaml_data, is_overload=is_overload)
455
+ item_type = item_type or yaml_data["itemType"]["elementType"]
456
+ if yaml_data.get("nextOperation"):
457
+ yaml_data["nextOperation"]["groupName"] = self.pad_reserved_words(
458
+ yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP
459
+ )
460
+ yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
461
+ for response in yaml_data["nextOperation"].get("responses", []):
462
+ update_paging_response(response)
463
+ response["itemType"] = item_type
464
+ for response in yaml_data.get("responses", []):
465
+ update_paging_response(response)
466
+ response["itemType"] = item_type
467
+ for overload in yaml_data.get("overloads", []):
468
+ self.update_paging_operation(code_model, overload, is_overload=True, item_type=item_type)
469
+
470
+ def update_operation_groups(self, code_model: Dict[str, Any], client: Dict[str, Any]) -> None:
471
+ operation_groups_yaml_data = client.get("operationGroups", [])
472
+ for operation_group in operation_groups_yaml_data:
473
+ operation_group["identifyName"] = self.pad_reserved_words(
474
+ operation_group.get("name", operation_group["propertyName"]),
475
+ PadType.OPERATION_GROUP,
476
+ )
477
+ operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
478
+ operation_group["propertyName"] = self.pad_reserved_words(
479
+ operation_group["propertyName"], PadType.OPERATION_GROUP
480
+ )
481
+ operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
482
+ operation_group["className"] = update_operation_group_class_name(
483
+ client["name"], operation_group["className"]
484
+ )
485
+ for operation in operation_group["operations"]:
486
+ self.get_operation_updater(operation)(code_model, operation)
487
+
488
+ if operation_group.get("operationGroups"):
489
+ self.update_operation_groups(code_model, operation_group)
490
+
491
+ def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
492
+ """Convert in place the YAML str."""
493
+ self.update_types(yaml_data["types"])
494
+ yaml_data["types"] += KNOWN_TYPES.values()
495
+ for client in yaml_data["clients"]:
496
+ self.update_client(client)
497
+ self.update_operation_groups(yaml_data, client)
498
+ for clients in yaml_data["subnamespaceToClients"].values():
499
+ for client in clients:
500
+ self.update_client(client)
501
+ self.update_operation_groups(yaml_data, client)
502
+ if yaml_data.get("namespace"):
503
+ yaml_data["namespace"] = pad_builtin_namespaces(yaml_data["namespace"])
504
+
505
+
506
+ if __name__ == "__main__":
507
+ # CADL pipeline will call this
508
+ args, unknown_args = parse_args()
509
+ 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)