@typespec/http-client-python 0.33.0 → 0.34.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.
- package/README.md +25 -3
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +2 -11
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/external-process.d.ts +14 -0
- package/dist/emitter/external-process.d.ts.map +1 -1
- package/dist/emitter/external-process.js +17 -1
- package/dist/emitter/external-process.js.map +1 -1
- package/dist/emitter/lib.js +1 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/node-runner.d.ts.map +1 -1
- package/dist/emitter/node-runner.js +2 -1
- package/dist/emitter/node-runner.js.map +1 -1
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +2 -0
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts +10 -0
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +12 -0
- package/dist/emitter/utils.js.map +1 -1
- package/emitter/src/emitter.ts +2 -14
- package/emitter/src/external-process.ts +18 -1
- package/emitter/src/lib.ts +1 -1
- package/emitter/src/node-runner.ts +2 -1
- package/emitter/src/types.ts +2 -0
- package/emitter/src/utils.ts +13 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/emitter/test/external-process.test.ts +28 -0
- package/emitter/test/utils.test.ts +12 -1
- package/eng/scripts/ci/regenerate-common.ts +40 -15
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/generator/build/lib/pygen/__init__.py +3 -3
- package/generator/build/lib/pygen/codegen/models/__init__.py +4 -2
- package/generator/build/lib/pygen/codegen/models/code_model.py +27 -5
- package/generator/build/lib/pygen/codegen/models/combined_type.py +3 -3
- package/generator/build/lib/pygen/codegen/models/enum_type.py +75 -25
- package/generator/build/lib/pygen/codegen/models/list_type.py +10 -4
- package/generator/build/lib/pygen/codegen/models/model_type.py +149 -4
- package/generator/build/lib/pygen/codegen/models/operation.py +1 -0
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +5 -1
- package/generator/build/lib/pygen/codegen/models/parameter.py +2 -2
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +30 -6
- package/generator/build/lib/pygen/codegen/models/property.py +21 -8
- package/generator/build/lib/pygen/codegen/models/response.py +16 -3
- package/generator/build/lib/pygen/codegen/models/utils.py +3 -6
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +46 -11
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +48 -15
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +1 -1
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +127 -3
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +356 -12
- package/generator/build/lib/pygen/codegen/serializers/unions_serializer.py +44 -0
- package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +1 -1
- package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +51 -2
- package/generator/build/lib/pygen/codegen/templates/unions.py.jinja2 +9 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +175 -162
- package/generator/build/lib/pygen/utils.py +6 -24
- package/generator/component-detection-pip-report.json +9 -9
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +3 -3
- package/generator/pygen/codegen/models/__init__.py +4 -2
- package/generator/pygen/codegen/models/code_model.py +27 -5
- package/generator/pygen/codegen/models/combined_type.py +3 -3
- package/generator/pygen/codegen/models/enum_type.py +75 -25
- package/generator/pygen/codegen/models/list_type.py +10 -4
- package/generator/pygen/codegen/models/model_type.py +149 -4
- package/generator/pygen/codegen/models/operation.py +1 -0
- package/generator/pygen/codegen/models/paging_operation.py +5 -1
- package/generator/pygen/codegen/models/parameter.py +2 -2
- package/generator/pygen/codegen/models/primitive_types.py +30 -6
- package/generator/pygen/codegen/models/property.py +21 -8
- package/generator/pygen/codegen/models/response.py +16 -3
- package/generator/pygen/codegen/models/utils.py +3 -6
- package/generator/pygen/codegen/serializers/__init__.py +46 -11
- package/generator/pygen/codegen/serializers/builder_serializer.py +48 -15
- package/generator/pygen/codegen/serializers/model_init_serializer.py +1 -1
- package/generator/pygen/codegen/serializers/model_serializer.py +127 -3
- package/generator/pygen/codegen/serializers/types_serializer.py +356 -12
- package/generator/pygen/codegen/serializers/unions_serializer.py +44 -0
- package/generator/pygen/codegen/templates/model_container.py.jinja2 +1 -1
- package/generator/pygen/codegen/templates/types.py.jinja2 +51 -2
- package/generator/pygen/codegen/templates/unions.py.jinja2 +9 -0
- package/generator/pygen/preprocess/__init__.py +175 -162
- package/generator/pygen/utils.py +6 -24
- package/generator/pygen.egg-info/SOURCES.txt +2 -0
- package/package.json +5 -5
|
@@ -36,18 +36,14 @@ def update_overload_section(
|
|
|
36
36
|
if overload_s.get("type"):
|
|
37
37
|
overload_s["type"] = original_s["type"]
|
|
38
38
|
if overload_s.get("headers"):
|
|
39
|
-
for overload_h, original_h in zip(
|
|
40
|
-
overload_s["headers"], original_s["headers"]
|
|
41
|
-
):
|
|
39
|
+
for overload_h, original_h in zip(overload_s["headers"], original_s["headers"]):
|
|
42
40
|
if overload_h.get("type"):
|
|
43
41
|
overload_h["type"] = original_h["type"]
|
|
44
42
|
except KeyError as exc:
|
|
45
43
|
raise ValueError(overload["name"]) from exc
|
|
46
44
|
|
|
47
45
|
|
|
48
|
-
def add_overload(
|
|
49
|
-
yaml_data: dict[str, Any], body_type: dict[str, Any], for_flatten_params=False
|
|
50
|
-
):
|
|
46
|
+
def add_overload(yaml_data: dict[str, Any], body_type: dict[str, Any], for_flatten_params=False):
|
|
51
47
|
overload = copy.deepcopy(yaml_data)
|
|
52
48
|
overload["isOverload"] = True
|
|
53
49
|
overload["bodyParameter"]["type"] = body_type
|
|
@@ -59,9 +55,7 @@ def add_overload(
|
|
|
59
55
|
if for_flatten_params:
|
|
60
56
|
overload["bodyParameter"]["flattened"] = True
|
|
61
57
|
else:
|
|
62
|
-
overload["parameters"] = [
|
|
63
|
-
p for p in overload["parameters"] if not p.get("inFlattenedBody")
|
|
64
|
-
]
|
|
58
|
+
overload["parameters"] = [p for p in overload["parameters"] if not p.get("inFlattenedBody")]
|
|
65
59
|
# for yaml sync, we need to make sure all of the responses, parameters, and exceptions' types have the same yaml id
|
|
66
60
|
for overload_p, original_p in zip(overload["parameters"], yaml_data["parameters"]):
|
|
67
61
|
overload_p["type"] = original_p["type"]
|
|
@@ -69,9 +63,7 @@ def add_overload(
|
|
|
69
63
|
update_overload_section(overload, yaml_data, "exceptions")
|
|
70
64
|
|
|
71
65
|
# update content type to be an overloads content type
|
|
72
|
-
content_type_param = next(
|
|
73
|
-
p for p in overload["parameters"] if p["wireName"].lower() == "content-type"
|
|
74
|
-
)
|
|
66
|
+
content_type_param = next(p for p in overload["parameters"] if p["wireName"].lower() == "content-type")
|
|
75
67
|
content_type_param["inOverload"] = True
|
|
76
68
|
content_type_param["inDocstring"] = True
|
|
77
69
|
body_type_description = get_body_type_for_description(overload["bodyParameter"])
|
|
@@ -94,25 +86,18 @@ def add_overloads_for_body_param(yaml_data: dict[str, Any]) -> None:
|
|
|
94
86
|
body_parameter = yaml_data["bodyParameter"]
|
|
95
87
|
if not (
|
|
96
88
|
body_parameter["type"]["type"] == "combined"
|
|
97
|
-
and len(yaml_data["bodyParameter"]["type"]["types"])
|
|
98
|
-
> len(yaml_data["overloads"])
|
|
89
|
+
and len(yaml_data["bodyParameter"]["type"]["types"]) > len(yaml_data["overloads"])
|
|
99
90
|
):
|
|
100
91
|
return
|
|
101
92
|
for body_type in body_parameter["type"]["types"]:
|
|
102
|
-
if any(
|
|
103
|
-
o
|
|
104
|
-
for o in yaml_data["overloads"]
|
|
105
|
-
if id(o["bodyParameter"]["type"]) == id(body_type)
|
|
106
|
-
):
|
|
93
|
+
if any(o for o in yaml_data["overloads"] if id(o["bodyParameter"]["type"]) == id(body_type)):
|
|
107
94
|
continue
|
|
108
95
|
if body_type.get("type") == "model" and body_type.get("base") == "json":
|
|
109
|
-
yaml_data["overloads"].append(
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
yaml_data["overloads"].append(add_overload(yaml_data, body_type, for_flatten_params=True))
|
|
97
|
+
# Skip single-body JSON overload; the TypedDict overload replaces it
|
|
98
|
+
continue
|
|
112
99
|
yaml_data["overloads"].append(add_overload(yaml_data, body_type))
|
|
113
|
-
content_type_param = next(
|
|
114
|
-
p for p in yaml_data["parameters"] if p["wireName"].lower() == "content-type"
|
|
115
|
-
)
|
|
100
|
+
content_type_param = next(p for p in yaml_data["parameters"] if p["wireName"].lower() == "content-type")
|
|
116
101
|
content_type_param["inOverload"] = False
|
|
117
102
|
content_type_param["inOverridden"] = True
|
|
118
103
|
content_type_param["inDocstring"] = True
|
|
@@ -122,19 +107,13 @@ def add_overloads_for_body_param(yaml_data: dict[str, Any]) -> None:
|
|
|
122
107
|
content_type_param["optional"] = True
|
|
123
108
|
|
|
124
109
|
|
|
125
|
-
def update_description(
|
|
126
|
-
description: Optional[str], default_description: str = ""
|
|
127
|
-
) -> str:
|
|
110
|
+
def update_description(description: Optional[str], default_description: str = "") -> str:
|
|
128
111
|
if not description:
|
|
129
112
|
description = default_description
|
|
130
113
|
description.rstrip(" ")
|
|
131
114
|
# Don't append a trailing period when the description ends with a code block: the
|
|
132
115
|
# period would land inside the rendered literal block (e.g. "]." ) and break Sphinx.
|
|
133
|
-
if (
|
|
134
|
-
description
|
|
135
|
-
and description[-1] != "."
|
|
136
|
-
and not description_ends_with_code_block(description)
|
|
137
|
-
):
|
|
116
|
+
if description and description[-1] != "." and not description_ends_with_code_block(description):
|
|
138
117
|
description += "."
|
|
139
118
|
return description
|
|
140
119
|
|
|
@@ -197,9 +176,7 @@ def _get_etag_role(parameter: dict[str, Any]) -> Optional[str]:
|
|
|
197
176
|
return parameter.get("etagRole")
|
|
198
177
|
|
|
199
178
|
|
|
200
|
-
def _pick_etag_slot(
|
|
201
|
-
candidates: list[dict[str, Any]], standard_wire_name: str
|
|
202
|
-
) -> Optional[dict[str, Any]]:
|
|
179
|
+
def _pick_etag_slot(candidates: list[dict[str, Any]], standard_wire_name: str) -> Optional[dict[str, Any]]:
|
|
203
180
|
"""Choose which etag-typed header should be promoted to the etag/match_condition slot.
|
|
204
181
|
|
|
205
182
|
When more than one etag-typed header is present in an operation, prefer the
|
|
@@ -227,25 +204,16 @@ def _resolve_etag_pair(
|
|
|
227
204
|
Returns (property_if_match, property_if_none_match) — both None when
|
|
228
205
|
there are no etag candidates.
|
|
229
206
|
"""
|
|
230
|
-
property_if_match = _pick_etag_slot(
|
|
231
|
-
|
|
232
|
-
)
|
|
233
|
-
property_if_none_match = _pick_etag_slot(
|
|
234
|
-
if_none_match_candidates, STANDARD_IF_NONE_MATCH_WIRE_NAME
|
|
235
|
-
)
|
|
207
|
+
property_if_match = _pick_etag_slot(if_match_candidates, STANDARD_IF_MATCH_WIRE_NAME)
|
|
208
|
+
property_if_none_match = _pick_etag_slot(if_none_match_candidates, STANDARD_IF_NONE_MATCH_WIRE_NAME)
|
|
236
209
|
|
|
237
210
|
# Ensure the promoted pair come from the same family. When one slot is
|
|
238
211
|
# standard and the other custom (cross-family), replace the custom slot
|
|
239
212
|
# with a synthetic standard partner. Also synthesize the missing partner
|
|
240
213
|
# when only one side is present.
|
|
241
214
|
if property_if_match and property_if_none_match:
|
|
242
|
-
match_is_std = (
|
|
243
|
-
|
|
244
|
-
)
|
|
245
|
-
none_match_is_std = (
|
|
246
|
-
get_wire_name_lower(property_if_none_match)
|
|
247
|
-
== STANDARD_IF_NONE_MATCH_WIRE_NAME
|
|
248
|
-
)
|
|
215
|
+
match_is_std = get_wire_name_lower(property_if_match) == STANDARD_IF_MATCH_WIRE_NAME
|
|
216
|
+
none_match_is_std = get_wire_name_lower(property_if_none_match) == STANDARD_IF_NONE_MATCH_WIRE_NAME
|
|
249
217
|
if match_is_std and not none_match_is_std:
|
|
250
218
|
property_if_none_match = property_if_match.copy()
|
|
251
219
|
property_if_none_match["wireName"] = STANDARD_IF_NONE_MATCH_WIRE_NAME
|
|
@@ -293,15 +261,11 @@ def _process_operation_etag_headers(
|
|
|
293
261
|
elif role == "ifNoneMatch":
|
|
294
262
|
if_none_match_candidates.append(p)
|
|
295
263
|
|
|
296
|
-
property_if_match, property_if_none_match = _resolve_etag_pair(
|
|
297
|
-
if_match_candidates, if_none_match_candidates
|
|
298
|
-
)
|
|
264
|
+
property_if_match, property_if_none_match = _resolve_etag_pair(if_match_candidates, if_none_match_candidates)
|
|
299
265
|
|
|
300
266
|
if property_if_match and property_if_none_match:
|
|
301
267
|
etag_params = {id(property_if_match), id(property_if_none_match)}
|
|
302
|
-
operation["parameters"] = [
|
|
303
|
-
item for item in operation["parameters"] if id(item) not in etag_params
|
|
304
|
-
] + [
|
|
268
|
+
operation["parameters"] = [item for item in operation["parameters"] if id(item) not in etag_params] + [
|
|
305
269
|
property_if_match,
|
|
306
270
|
property_if_none_match,
|
|
307
271
|
]
|
|
@@ -320,9 +284,7 @@ def has_json_content_type(yaml_data: dict[str, Any]) -> bool:
|
|
|
320
284
|
|
|
321
285
|
|
|
322
286
|
def has_multi_part_content_type(yaml_data: dict[str, Any]) -> bool:
|
|
323
|
-
return any(
|
|
324
|
-
ct for ct in yaml_data.get("contentTypes", []) if ct == "multipart/form-data"
|
|
325
|
-
)
|
|
287
|
+
return any(ct for ct in yaml_data.get("contentTypes", []) if ct == "multipart/form-data")
|
|
326
288
|
|
|
327
289
|
|
|
328
290
|
class PreProcessPlugin(YamlUpdatePlugin):
|
|
@@ -344,6 +306,94 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
344
306
|
def is_tsp(self) -> bool:
|
|
345
307
|
return self.options.get("tsp_file", False)
|
|
346
308
|
|
|
309
|
+
@staticmethod
|
|
310
|
+
def _find_existing_typeddict(
|
|
311
|
+
code_model: dict[str, Any],
|
|
312
|
+
cross_lang_id: Optional[str],
|
|
313
|
+
name: Optional[str] = None,
|
|
314
|
+
) -> Optional[dict[str, Any]]:
|
|
315
|
+
"""Find an existing typeddict copy for the given model.
|
|
316
|
+
|
|
317
|
+
Matches on both ``crossLanguageDefinitionId`` and ``name``. The name is required because
|
|
318
|
+
template-instantiated models (e.g. ``ResourceUpdateModel<Foo, FooProperties>``) all share
|
|
319
|
+
the template's cross-language id, so matching on the id alone would reuse one model's copy
|
|
320
|
+
(e.g. ``CacheUpdate``) for a different model (e.g. ``VolumeUpdate``).
|
|
321
|
+
"""
|
|
322
|
+
if not cross_lang_id:
|
|
323
|
+
return None
|
|
324
|
+
return next(
|
|
325
|
+
(
|
|
326
|
+
t
|
|
327
|
+
for t in code_model["types"]
|
|
328
|
+
if t.get("type") == "model"
|
|
329
|
+
and t.get("base") == "typeddict"
|
|
330
|
+
and t.get("crossLanguageDefinitionId") == cross_lang_id
|
|
331
|
+
and (name is None or t.get("name") == name)
|
|
332
|
+
),
|
|
333
|
+
None,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
@staticmethod
|
|
337
|
+
def _find_spread_original(code_model: dict[str, Any], json_model: dict[str, Any]) -> Optional[dict[str, Any]]:
|
|
338
|
+
"""Recover the dpg model that a spread (json) body was cloned from.
|
|
339
|
+
|
|
340
|
+
When a spread body type is also used elsewhere, the emitter clones it, renames the clone to
|
|
341
|
+
``<Method>Request`` and sets ``base = "json"`` while keeping the original
|
|
342
|
+
``crossLanguageDefinitionId``. To reference the real model's TypedDict we look the clone up
|
|
343
|
+
by that id.
|
|
344
|
+
|
|
345
|
+
Distinct template-instantiated models share a single ``crossLanguageDefinitionId``, so an id
|
|
346
|
+
match alone is ambiguous. We only reuse an original when the choice is unambiguous:
|
|
347
|
+
|
|
348
|
+
* a dpg candidate whose ``name`` equals the json model's name (the body was not renamed), or
|
|
349
|
+
* exactly one dpg candidate carries the id.
|
|
350
|
+
|
|
351
|
+
Otherwise we return ``None`` so the caller falls back to the json model itself, avoiding a
|
|
352
|
+
reference to the wrong model's TypedDict.
|
|
353
|
+
"""
|
|
354
|
+
cross_lang_id = json_model.get("crossLanguageDefinitionId")
|
|
355
|
+
if not cross_lang_id:
|
|
356
|
+
return None
|
|
357
|
+
candidates = [
|
|
358
|
+
t
|
|
359
|
+
for t in code_model["types"]
|
|
360
|
+
if t.get("type") == "model"
|
|
361
|
+
and t.get("base") == "dpg"
|
|
362
|
+
and t.get("crossLanguageDefinitionId") == cross_lang_id
|
|
363
|
+
and t is not json_model
|
|
364
|
+
]
|
|
365
|
+
if not candidates:
|
|
366
|
+
return None
|
|
367
|
+
name = json_model.get("name")
|
|
368
|
+
for candidate in candidates:
|
|
369
|
+
if candidate.get("name") == name:
|
|
370
|
+
return candidate
|
|
371
|
+
if len(candidates) == 1:
|
|
372
|
+
return candidates[0]
|
|
373
|
+
return None
|
|
374
|
+
|
|
375
|
+
@staticmethod
|
|
376
|
+
def _insert_typeddict_overload(
|
|
377
|
+
code_model: dict[str, Any],
|
|
378
|
+
body_parameter: dict[str, Any],
|
|
379
|
+
source: dict[str, Any],
|
|
380
|
+
origin_type: str,
|
|
381
|
+
existing_td: Optional[dict[str, Any]],
|
|
382
|
+
) -> None:
|
|
383
|
+
"""Insert a typeddict type into the body parameter's combined types."""
|
|
384
|
+
if origin_type == "model":
|
|
385
|
+
td_type = existing_td or {**source, "base": "typeddict"}
|
|
386
|
+
body_parameter["type"]["types"].insert(1, td_type)
|
|
387
|
+
if not existing_td:
|
|
388
|
+
code_model["types"].append(td_type)
|
|
389
|
+
else:
|
|
390
|
+
td_list_or_dict = copy.deepcopy(body_parameter["type"]["types"][0])
|
|
391
|
+
td_elem = existing_td or {**source, "base": "typeddict"}
|
|
392
|
+
td_list_or_dict["elementType"] = td_elem
|
|
393
|
+
body_parameter["type"]["types"].insert(1, td_list_or_dict)
|
|
394
|
+
if not existing_td:
|
|
395
|
+
code_model["types"].append(td_elem)
|
|
396
|
+
|
|
347
397
|
def add_body_param_type(
|
|
348
398
|
self,
|
|
349
399
|
code_model: dict[str, Any],
|
|
@@ -353,54 +403,70 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
353
403
|
if ( # pylint: disable=too-many-boolean-expressions
|
|
354
404
|
body_parameter
|
|
355
405
|
and body_parameter["type"]["type"] in ("model", "dict", "list")
|
|
356
|
-
and (
|
|
357
|
-
has_json_content_type(body_parameter)
|
|
358
|
-
or (self.is_tsp and has_multi_part_content_type(body_parameter))
|
|
359
|
-
)
|
|
406
|
+
and (has_json_content_type(body_parameter) or (self.is_tsp and has_multi_part_content_type(body_parameter)))
|
|
360
407
|
and not body_parameter["type"].get("xmlMetadata")
|
|
361
408
|
and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
|
|
362
409
|
):
|
|
363
410
|
origin_type = body_parameter["type"]["type"]
|
|
364
411
|
model_type = (
|
|
365
|
-
body_parameter["type"]
|
|
366
|
-
if origin_type == "model"
|
|
367
|
-
else body_parameter["type"].get("elementType", {})
|
|
412
|
+
body_parameter["type"] if origin_type == "model" else body_parameter["type"].get("elementType", {})
|
|
368
413
|
)
|
|
369
414
|
is_dpg_model = model_type.get("base") == "dpg"
|
|
415
|
+
is_json_model = model_type.get("base") == "json"
|
|
416
|
+
is_typeddict_only = self.options["models-mode"] == "typeddict"
|
|
417
|
+
|
|
370
418
|
body_parameter["type"] = {
|
|
371
419
|
"type": "combined",
|
|
372
420
|
"types": [body_parameter["type"]],
|
|
373
421
|
}
|
|
374
|
-
# don't add binary overload for multipart content type
|
|
375
|
-
if not (self.is_tsp and has_multi_part_content_type(body_parameter)):
|
|
422
|
+
# don't add binary overload for multipart content type or typeddict-only mode
|
|
423
|
+
if not (self.is_tsp and has_multi_part_content_type(body_parameter)) and not is_typeddict_only:
|
|
376
424
|
body_parameter["type"]["types"].append(KNOWN_TYPES["binary"])
|
|
377
425
|
|
|
426
|
+
# Add typeddict overload for non-spread dpg models
|
|
378
427
|
if self.options["models-mode"] == "dpg" and is_dpg_model:
|
|
379
|
-
|
|
380
|
-
|
|
428
|
+
cross_lang_id = model_type.get("crossLanguageDefinitionId")
|
|
429
|
+
existing_td = self._find_existing_typeddict(code_model, cross_lang_id, model_type.get("name"))
|
|
430
|
+
self._insert_typeddict_overload(code_model, body_parameter, model_type, origin_type, existing_td)
|
|
431
|
+
|
|
432
|
+
# For spread bodies (json base), add a typeddict overload that references
|
|
433
|
+
# the original model. This replaces the JSON single-body overload.
|
|
434
|
+
if is_json_model:
|
|
435
|
+
cross_lang_id = model_type.get("crossLanguageDefinitionId")
|
|
436
|
+
original = self._find_spread_original(code_model, model_type)
|
|
437
|
+
|
|
438
|
+
if is_typeddict_only and original:
|
|
439
|
+
# In typeddict-only mode, the original dpg model already renders
|
|
440
|
+
# as a TypedDict — reference it directly, no copy needed.
|
|
441
|
+
if origin_type == "model":
|
|
442
|
+
body_parameter["type"]["types"].insert(1, original)
|
|
443
|
+
else:
|
|
444
|
+
td_list_or_dict = copy.deepcopy(body_parameter["type"]["types"][0])
|
|
445
|
+
td_list_or_dict["elementType"] = original
|
|
446
|
+
body_parameter["type"]["types"].insert(1, td_list_or_dict)
|
|
381
447
|
else:
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
448
|
+
source = original or model_type
|
|
449
|
+
existing_td = self._find_existing_typeddict(code_model, cross_lang_id, source.get("name"))
|
|
450
|
+
self._insert_typeddict_overload(code_model, body_parameter, source, origin_type, existing_td)
|
|
451
|
+
|
|
452
|
+
if len(body_parameter["type"]["types"]) == 1:
|
|
453
|
+
# Only one body variant remains (e.g. typeddict-only mode where the
|
|
454
|
+
# binary and JSON overloads are omitted). Collapse the combined
|
|
455
|
+
# wrapper back to the single type so we don't emit a lone
|
|
456
|
+
# ``@overload`` (mypy rejects a single overload definition).
|
|
457
|
+
body_parameter["type"] = body_parameter["type"]["types"][0]
|
|
458
|
+
return
|
|
459
|
+
|
|
389
460
|
code_model["types"].append(body_parameter["type"])
|
|
390
461
|
|
|
391
|
-
def pad_reserved_words(
|
|
392
|
-
self, name: str, pad_type: PadType, yaml_type: dict[str, Any]
|
|
393
|
-
) -> str:
|
|
462
|
+
def pad_reserved_words(self, name: str, pad_type: PadType, yaml_type: dict[str, Any]) -> str:
|
|
394
463
|
# we want to pad hidden variables as well
|
|
395
464
|
if not name:
|
|
396
465
|
# we'll pass in empty operation groups sometime etc.
|
|
397
466
|
return name
|
|
398
467
|
|
|
399
468
|
if self.is_tsp:
|
|
400
|
-
reserved_words = {
|
|
401
|
-
k: (v + TSP_RESERVED_WORDS.get(k, []))
|
|
402
|
-
for k, v in RESERVED_WORDS.items()
|
|
403
|
-
}
|
|
469
|
+
reserved_words = {k: (v + TSP_RESERVED_WORDS.get(k, [])) for k, v in RESERVED_WORDS.items()}
|
|
404
470
|
else:
|
|
405
471
|
reserved_words = RESERVED_WORDS
|
|
406
472
|
name = pad_special_chars(name)
|
|
@@ -417,24 +483,18 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
417
483
|
def update_types(self, yaml_data: list[dict[str, Any]]) -> None:
|
|
418
484
|
for type in yaml_data:
|
|
419
485
|
for property in type.get("properties", []):
|
|
420
|
-
property["description"] = update_description(
|
|
421
|
-
property.get("description", "")
|
|
422
|
-
)
|
|
486
|
+
property["description"] = update_description(property.get("description", ""))
|
|
423
487
|
if not property.get("isExactName", False):
|
|
424
488
|
property["clientName"] = self.pad_reserved_words(
|
|
425
489
|
property["clientName"].lower(), PadType.PROPERTY, property
|
|
426
490
|
)
|
|
427
491
|
add_redefined_builtin_info(property["clientName"], property)
|
|
428
492
|
if type.get("name"):
|
|
429
|
-
pad_type =
|
|
430
|
-
PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
|
|
431
|
-
)
|
|
493
|
+
pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
|
|
432
494
|
if type["type"] != "enumvalue":
|
|
433
495
|
name = self.pad_reserved_words(type["name"], pad_type, type)
|
|
434
496
|
type["name"] = name[0].upper() + name[1:]
|
|
435
|
-
type["description"] = update_description(
|
|
436
|
-
type.get("description", ""), type["name"]
|
|
437
|
-
)
|
|
497
|
+
type["description"] = update_description(type.get("description", ""), type["name"])
|
|
438
498
|
type["snakeCaseName"] = to_snake_case(type["name"])
|
|
439
499
|
if type.get("values"):
|
|
440
500
|
# we're enums - enum values are UPPER_CASE so no padding needed for reserved words
|
|
@@ -452,24 +512,16 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
452
512
|
yaml_data.append(CLOUD_SETTING["type"]) # type: ignore
|
|
453
513
|
|
|
454
514
|
def update_client(self, yaml_data: dict[str, Any]) -> None:
|
|
455
|
-
yaml_data["description"] = update_description(
|
|
456
|
-
yaml_data["description"], default_description=yaml_data["name"]
|
|
457
|
-
)
|
|
515
|
+
yaml_data["description"] = update_description(yaml_data["description"], default_description=yaml_data["name"])
|
|
458
516
|
yaml_data["legacyFilename"] = to_snake_case(yaml_data["name"].replace(" ", "_"))
|
|
459
517
|
parameters = yaml_data["parameters"]
|
|
460
518
|
for parameter in parameters:
|
|
461
519
|
self.update_parameter(parameter)
|
|
462
520
|
if parameter["clientName"] == "credential":
|
|
463
521
|
policy = parameter["type"].get("policy")
|
|
464
|
-
if
|
|
465
|
-
policy
|
|
466
|
-
and policy["type"] == "BearerTokenCredentialPolicy"
|
|
467
|
-
and self.azure_arm
|
|
468
|
-
):
|
|
522
|
+
if policy and policy["type"] == "BearerTokenCredentialPolicy" and self.azure_arm:
|
|
469
523
|
policy["type"] = "ARMChallengeAuthenticationPolicy"
|
|
470
|
-
policy["credentialScopes"] = [
|
|
471
|
-
"https://management.azure.com/.default"
|
|
472
|
-
]
|
|
524
|
+
policy["credentialScopes"] = ["https://management.azure.com/.default"]
|
|
473
525
|
if (
|
|
474
526
|
(not self.version_tolerant or self.azure_arm)
|
|
475
527
|
and parameters
|
|
@@ -489,9 +541,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
489
541
|
if self.azure_arm and yaml_data["parameters"]:
|
|
490
542
|
yaml_data["parameters"].append(CLOUD_SETTING)
|
|
491
543
|
|
|
492
|
-
def get_operation_updater(
|
|
493
|
-
self, yaml_data: dict[str, Any]
|
|
494
|
-
) -> Callable[[dict[str, Any], dict[str, Any]], None]:
|
|
544
|
+
def get_operation_updater(self, yaml_data: dict[str, Any]) -> Callable[[dict[str, Any], dict[str, Any]], None]:
|
|
495
545
|
if yaml_data["discriminator"] == "lropaging":
|
|
496
546
|
return self.update_lro_paging_operation
|
|
497
547
|
if yaml_data["discriminator"] == "lro":
|
|
@@ -503,8 +553,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
503
553
|
def update_parameter(self, yaml_data: dict[str, Any]) -> None:
|
|
504
554
|
yaml_data["description"] = update_description(yaml_data.get("description", ""))
|
|
505
555
|
if not yaml_data.get("isExactName", False) and not (
|
|
506
|
-
yaml_data["location"] == "header"
|
|
507
|
-
and yaml_data["clientName"] in ("content_type", "accept")
|
|
556
|
+
yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")
|
|
508
557
|
):
|
|
509
558
|
yaml_data["clientName"] = self.pad_reserved_words(
|
|
510
559
|
yaml_data["clientName"].lower(), PadType.PARAMETER, yaml_data
|
|
@@ -520,16 +569,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
520
569
|
prop: (
|
|
521
570
|
param_name
|
|
522
571
|
if prop in exact_name_props
|
|
523
|
-
else self.pad_reserved_words(
|
|
524
|
-
param_name, PadType.PARAMETER, yaml_data
|
|
525
|
-
).lower()
|
|
572
|
+
else self.pad_reserved_words(param_name, PadType.PARAMETER, yaml_data).lower()
|
|
526
573
|
)
|
|
527
574
|
for prop, param_name in yaml_data["propertyToParameterName"].items()
|
|
528
575
|
}
|
|
529
576
|
wire_name_lower = (yaml_data.get("wireName") or "").lower()
|
|
530
577
|
if yaml_data["location"] == "header" and (
|
|
531
|
-
wire_name_lower in HEADERS_HIDE_IN_METHOD
|
|
532
|
-
or yaml_data.get("clientDefaultValue") == "multipart/form-data"
|
|
578
|
+
wire_name_lower in HEADERS_HIDE_IN_METHOD or yaml_data.get("clientDefaultValue") == "multipart/form-data"
|
|
533
579
|
):
|
|
534
580
|
yaml_data["hideInMethod"] = True
|
|
535
581
|
if self.version_tolerant and yaml_data["location"] == "header":
|
|
@@ -538,10 +584,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
538
584
|
headers_convert(yaml_data, ETAG_MATCH_DATA)
|
|
539
585
|
elif role == "ifNoneMatch":
|
|
540
586
|
headers_convert(yaml_data, ETAG_NONE_MATCH_DATA)
|
|
541
|
-
if
|
|
542
|
-
wire_name_lower in ["$host", "content-type", "accept"]
|
|
543
|
-
and yaml_data["type"]["type"] == "constant"
|
|
544
|
-
):
|
|
587
|
+
if wire_name_lower in ["$host", "content-type", "accept"] and yaml_data["type"]["type"] == "constant":
|
|
545
588
|
yaml_data["clientDefaultValue"] = yaml_data["type"]["value"]
|
|
546
589
|
|
|
547
590
|
def update_operation(
|
|
@@ -551,17 +594,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
551
594
|
*,
|
|
552
595
|
is_overload: bool = False,
|
|
553
596
|
) -> None:
|
|
554
|
-
yaml_data["groupName"] = self.pad_reserved_words(
|
|
555
|
-
yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data
|
|
556
|
-
)
|
|
597
|
+
yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data)
|
|
557
598
|
yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
|
|
558
599
|
if yaml_data.get("isExactName", False):
|
|
559
600
|
# exact() client name: keep the operation name as-is without lowercasing,
|
|
560
601
|
# snake-casing, or padding reserved words.
|
|
561
602
|
if yaml_data.get("isLroInitialOperation") is True:
|
|
562
|
-
yaml_data["name"] = (
|
|
563
|
-
"_" + extract_original_name(yaml_data["name"]) + "_initial"
|
|
564
|
-
)
|
|
603
|
+
yaml_data["name"] = "_" + extract_original_name(yaml_data["name"]) + "_initial"
|
|
565
604
|
else:
|
|
566
605
|
yaml_data["name"] = yaml_data["name"].lower()
|
|
567
606
|
if yaml_data.get("isLroInitialOperation") is True:
|
|
@@ -575,12 +614,8 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
575
614
|
+ "_initial"
|
|
576
615
|
)
|
|
577
616
|
else:
|
|
578
|
-
yaml_data["name"] = self.pad_reserved_words(
|
|
579
|
-
|
|
580
|
-
)
|
|
581
|
-
yaml_data["description"] = update_description(
|
|
582
|
-
yaml_data["description"], yaml_data["name"]
|
|
583
|
-
)
|
|
617
|
+
yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD, yaml_data)
|
|
618
|
+
yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
|
|
584
619
|
yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
|
|
585
620
|
body_parameter = yaml_data.get("bodyParameter")
|
|
586
621
|
for parameter in yaml_data["parameters"]:
|
|
@@ -601,12 +636,8 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
601
636
|
def _update_lro_operation_helper(self, yaml_data: dict[str, Any]) -> None:
|
|
602
637
|
for response in yaml_data.get("responses", []):
|
|
603
638
|
response["discriminator"] = "lro"
|
|
604
|
-
response["pollerSync"] = (
|
|
605
|
-
|
|
606
|
-
)
|
|
607
|
-
response["pollerAsync"] = (
|
|
608
|
-
response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
|
|
609
|
-
)
|
|
639
|
+
response["pollerSync"] = response.get("pollerSync") or "azure.core.polling.LROPoller"
|
|
640
|
+
response["pollerAsync"] = response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
|
|
610
641
|
if not response.get("pollingMethodSync"):
|
|
611
642
|
response["pollingMethodSync"] = (
|
|
612
643
|
"azure.mgmt.core.polling.arm_polling.ARMPolling"
|
|
@@ -628,9 +659,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
628
659
|
item_type: Optional[dict[str, Any]] = None,
|
|
629
660
|
) -> None:
|
|
630
661
|
self.update_lro_operation(code_model, yaml_data, is_overload=is_overload)
|
|
631
|
-
self.update_paging_operation(
|
|
632
|
-
code_model, yaml_data, is_overload=is_overload, item_type=item_type
|
|
633
|
-
)
|
|
662
|
+
self.update_paging_operation(code_model, yaml_data, is_overload=is_overload, item_type=item_type)
|
|
634
663
|
yaml_data["discriminator"] = "lropaging"
|
|
635
664
|
for response in yaml_data.get("responses", []):
|
|
636
665
|
response["discriminator"] = "lropaging"
|
|
@@ -653,16 +682,12 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
653
682
|
response["type"] = KNOWN_TYPES["binary"]
|
|
654
683
|
|
|
655
684
|
self.update_operation(code_model, yaml_data, is_overload=is_overload)
|
|
656
|
-
self.update_operation(
|
|
657
|
-
code_model, yaml_data["initialOperation"], is_overload=is_overload
|
|
658
|
-
)
|
|
685
|
+
self.update_operation(code_model, yaml_data["initialOperation"], is_overload=is_overload)
|
|
659
686
|
convert_initial_operation_response_type(yaml_data["initialOperation"])
|
|
660
687
|
self._update_lro_operation_helper(yaml_data)
|
|
661
688
|
for overload in yaml_data.get("overloads", []):
|
|
662
689
|
self._update_lro_operation_helper(overload)
|
|
663
|
-
self.update_operation(
|
|
664
|
-
code_model, overload["initialOperation"], is_overload=True
|
|
665
|
-
)
|
|
690
|
+
self.update_operation(code_model, overload["initialOperation"], is_overload=True)
|
|
666
691
|
convert_initial_operation_response_type(overload["initialOperation"])
|
|
667
692
|
|
|
668
693
|
def update_paging_operation(
|
|
@@ -680,9 +705,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
680
705
|
PadType.OPERATION_GROUP,
|
|
681
706
|
yaml_data["nextOperation"],
|
|
682
707
|
)
|
|
683
|
-
yaml_data["nextOperation"]["groupName"] = to_snake_case(
|
|
684
|
-
yaml_data["nextOperation"]["groupName"]
|
|
685
|
-
)
|
|
708
|
+
yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
|
|
686
709
|
for response in yaml_data["nextOperation"].get("responses", []):
|
|
687
710
|
update_paging_response(response)
|
|
688
711
|
response["itemType"] = item_type
|
|
@@ -690,13 +713,9 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
690
713
|
update_paging_response(response)
|
|
691
714
|
response["itemType"] = item_type
|
|
692
715
|
for overload in yaml_data.get("overloads", []):
|
|
693
|
-
self.update_paging_operation(
|
|
694
|
-
code_model, overload, is_overload=True, item_type=item_type
|
|
695
|
-
)
|
|
716
|
+
self.update_paging_operation(code_model, overload, is_overload=True, item_type=item_type)
|
|
696
717
|
|
|
697
|
-
def update_operation_groups(
|
|
698
|
-
self, code_model: dict[str, Any], client: dict[str, Any]
|
|
699
|
-
) -> None:
|
|
718
|
+
def update_operation_groups(self, code_model: dict[str, Any], client: dict[str, Any]) -> None:
|
|
700
719
|
operation_groups_yaml_data = client.get("operationGroups", [])
|
|
701
720
|
for operation_group in operation_groups_yaml_data:
|
|
702
721
|
operation_group["identifyName"] = self.pad_reserved_words(
|
|
@@ -704,17 +723,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
704
723
|
PadType.OPERATION_GROUP,
|
|
705
724
|
operation_group,
|
|
706
725
|
)
|
|
707
|
-
operation_group["identifyName"] = to_snake_case(
|
|
708
|
-
operation_group["identifyName"]
|
|
709
|
-
)
|
|
726
|
+
operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
|
|
710
727
|
operation_group["propertyName"] = self.pad_reserved_words(
|
|
711
728
|
operation_group["propertyName"],
|
|
712
729
|
PadType.OPERATION_GROUP,
|
|
713
730
|
operation_group,
|
|
714
731
|
)
|
|
715
|
-
operation_group["propertyName"] = to_snake_case(
|
|
716
|
-
operation_group["propertyName"]
|
|
717
|
-
)
|
|
732
|
+
operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
|
|
718
733
|
operation_group["className"] = update_operation_group_class_name(
|
|
719
734
|
client["name"], operation_group["className"]
|
|
720
735
|
)
|
|
@@ -738,6 +753,4 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
738
753
|
if __name__ == "__main__":
|
|
739
754
|
# TSP pipeline will call this
|
|
740
755
|
args, unknown_args = parse_args()
|
|
741
|
-
PreProcessPlugin(
|
|
742
|
-
output_folder=args.output_folder, tsp_file=args.tsp_file, **unknown_args
|
|
743
|
-
).process()
|
|
756
|
+
PreProcessPlugin(output_folder=args.output_folder, tsp_file=args.tsp_file, **unknown_args).process()
|
|
@@ -22,20 +22,13 @@ def description_ends_with_code_block(description: str) -> bool:
|
|
|
22
22
|
block runs to the end of the description.
|
|
23
23
|
"""
|
|
24
24
|
lines = description.rstrip().splitlines()
|
|
25
|
-
directives = [
|
|
26
|
-
i for i, line in enumerate(lines) if line.lstrip().startswith(CODE_BLOCK_MARKER)
|
|
27
|
-
]
|
|
25
|
+
directives = [i for i, line in enumerate(lines) if line.lstrip().startswith(CODE_BLOCK_MARKER)]
|
|
28
26
|
if not directives:
|
|
29
27
|
return False
|
|
30
|
-
return all(
|
|
31
|
-
not line.strip() or line.startswith((" ", "\t"))
|
|
32
|
-
for line in lines[directives[-1] + 1 :]
|
|
33
|
-
)
|
|
28
|
+
return all(not line.strip() or line.startswith((" ", "\t")) for line in lines[directives[-1] + 1 :])
|
|
34
29
|
|
|
35
30
|
|
|
36
|
-
def update_enum_value(
|
|
37
|
-
name: str, value: Any, description: str, enum_type: dict[str, Any]
|
|
38
|
-
) -> dict[str, Any]:
|
|
31
|
+
def update_enum_value(name: str, value: Any, description: str, enum_type: dict[str, Any]) -> dict[str, Any]:
|
|
39
32
|
return {
|
|
40
33
|
"name": name,
|
|
41
34
|
"type": "enumvalue",
|
|
@@ -62,12 +55,7 @@ def to_snake_case(name: str) -> str:
|
|
|
62
55
|
and len(name) - next_non_upper_case_char_location > 1
|
|
63
56
|
and name[next_non_upper_case_char_location].isalpha()
|
|
64
57
|
):
|
|
65
|
-
return (
|
|
66
|
-
prefix
|
|
67
|
-
+ match_str[: len(match_str) - 1]
|
|
68
|
-
+ "_"
|
|
69
|
-
+ match_str[len(match_str) - 1]
|
|
70
|
-
)
|
|
58
|
+
return prefix + match_str[: len(match_str) - 1] + "_" + match_str[len(match_str) - 1]
|
|
71
59
|
|
|
72
60
|
return prefix + match_str
|
|
73
61
|
|
|
@@ -114,9 +102,7 @@ def parse_args(
|
|
|
114
102
|
return value
|
|
115
103
|
|
|
116
104
|
unknown_args_ret = {
|
|
117
|
-
ua.strip("--").split("=", maxsplit=1)[0]: _get_value(
|
|
118
|
-
ua.strip("--").split("=", maxsplit=1)[1]
|
|
119
|
-
)
|
|
105
|
+
ua.strip("--").split("=", maxsplit=1)[0]: _get_value(ua.strip("--").split("=", maxsplit=1)[1])
|
|
120
106
|
for ua in unknown_args
|
|
121
107
|
}
|
|
122
108
|
return args, unknown_args_ret
|
|
@@ -158,11 +144,7 @@ def build_policies(
|
|
|
158
144
|
"self._config.user_agent_policy",
|
|
159
145
|
"self._config.proxy_policy",
|
|
160
146
|
"policies.ContentDecodePolicy(**kwargs)",
|
|
161
|
-
(
|
|
162
|
-
f"{async_prefix}ARMAutoResourceProviderRegistrationPolicy()"
|
|
163
|
-
if is_arm
|
|
164
|
-
else None
|
|
165
|
-
),
|
|
147
|
+
(f"{async_prefix}ARMAutoResourceProviderRegistrationPolicy()" if is_arm else None),
|
|
166
148
|
"self._config.redirect_policy",
|
|
167
149
|
"self._config.retry_policy",
|
|
168
150
|
"self._config.authentication_policy",
|