@typespec/http-client-python 0.33.0 → 0.34.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 (61) hide show
  1. package/dist/emitter/types.d.ts.map +1 -1
  2. package/dist/emitter/types.js +2 -0
  3. package/dist/emitter/types.js.map +1 -1
  4. package/emitter/src/types.ts +2 -0
  5. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  6. package/eng/scripts/ci/regenerate-common.ts +40 -15
  7. package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
  8. package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
  9. package/generator/build/lib/pygen/__init__.py +3 -3
  10. package/generator/build/lib/pygen/codegen/models/__init__.py +4 -2
  11. package/generator/build/lib/pygen/codegen/models/code_model.py +27 -5
  12. package/generator/build/lib/pygen/codegen/models/combined_type.py +3 -3
  13. package/generator/build/lib/pygen/codegen/models/enum_type.py +75 -25
  14. package/generator/build/lib/pygen/codegen/models/list_type.py +10 -4
  15. package/generator/build/lib/pygen/codegen/models/model_type.py +145 -4
  16. package/generator/build/lib/pygen/codegen/models/operation.py +1 -0
  17. package/generator/build/lib/pygen/codegen/models/paging_operation.py +5 -1
  18. package/generator/build/lib/pygen/codegen/models/parameter.py +2 -2
  19. package/generator/build/lib/pygen/codegen/models/primitive_types.py +30 -6
  20. package/generator/build/lib/pygen/codegen/models/property.py +21 -8
  21. package/generator/build/lib/pygen/codegen/models/response.py +16 -3
  22. package/generator/build/lib/pygen/codegen/models/utils.py +3 -6
  23. package/generator/build/lib/pygen/codegen/serializers/__init__.py +46 -11
  24. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +48 -15
  25. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +1 -1
  26. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +127 -3
  27. package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +263 -12
  28. package/generator/build/lib/pygen/codegen/serializers/unions_serializer.py +44 -0
  29. package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +1 -1
  30. package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +51 -2
  31. package/generator/build/lib/pygen/codegen/templates/unions.py.jinja2 +9 -0
  32. package/generator/build/lib/pygen/preprocess/__init__.py +137 -162
  33. package/generator/build/lib/pygen/utils.py +6 -24
  34. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  35. package/generator/pygen/__init__.py +3 -3
  36. package/generator/pygen/codegen/models/__init__.py +4 -2
  37. package/generator/pygen/codegen/models/code_model.py +27 -5
  38. package/generator/pygen/codegen/models/combined_type.py +3 -3
  39. package/generator/pygen/codegen/models/enum_type.py +75 -25
  40. package/generator/pygen/codegen/models/list_type.py +10 -4
  41. package/generator/pygen/codegen/models/model_type.py +145 -4
  42. package/generator/pygen/codegen/models/operation.py +1 -0
  43. package/generator/pygen/codegen/models/paging_operation.py +5 -1
  44. package/generator/pygen/codegen/models/parameter.py +2 -2
  45. package/generator/pygen/codegen/models/primitive_types.py +30 -6
  46. package/generator/pygen/codegen/models/property.py +21 -8
  47. package/generator/pygen/codegen/models/response.py +16 -3
  48. package/generator/pygen/codegen/models/utils.py +3 -6
  49. package/generator/pygen/codegen/serializers/__init__.py +46 -11
  50. package/generator/pygen/codegen/serializers/builder_serializer.py +48 -15
  51. package/generator/pygen/codegen/serializers/model_init_serializer.py +1 -1
  52. package/generator/pygen/codegen/serializers/model_serializer.py +127 -3
  53. package/generator/pygen/codegen/serializers/types_serializer.py +263 -12
  54. package/generator/pygen/codegen/serializers/unions_serializer.py +44 -0
  55. package/generator/pygen/codegen/templates/model_container.py.jinja2 +1 -1
  56. package/generator/pygen/codegen/templates/types.py.jinja2 +51 -2
  57. package/generator/pygen/codegen/templates/unions.py.jinja2 +9 -0
  58. package/generator/pygen/preprocess/__init__.py +137 -162
  59. package/generator/pygen/utils.py +6 -24
  60. package/generator/pygen.egg-info/SOURCES.txt +2 -0
  61. 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
- add_overload(yaml_data, body_type, for_flatten_params=True)
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
- if_match_candidates, STANDARD_IF_MATCH_WIRE_NAME
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
- get_wire_name_lower(property_if_match) == STANDARD_IF_MATCH_WIRE_NAME
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,44 @@ 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(code_model: dict[str, Any], cross_lang_id: Optional[str]) -> Optional[dict[str, Any]]:
311
+ """Find an existing typeddict copy with the given crossLanguageDefinitionId."""
312
+ if not cross_lang_id:
313
+ return None
314
+ return next(
315
+ (
316
+ t
317
+ for t in code_model["types"]
318
+ if t.get("type") == "model"
319
+ and t.get("base") == "typeddict"
320
+ and t.get("crossLanguageDefinitionId") == cross_lang_id
321
+ ),
322
+ None,
323
+ )
324
+
325
+ @staticmethod
326
+ def _insert_typeddict_overload(
327
+ code_model: dict[str, Any],
328
+ body_parameter: dict[str, Any],
329
+ source: dict[str, Any],
330
+ origin_type: str,
331
+ existing_td: Optional[dict[str, Any]],
332
+ ) -> None:
333
+ """Insert a typeddict type into the body parameter's combined types."""
334
+ if origin_type == "model":
335
+ td_type = existing_td or {**source, "base": "typeddict"}
336
+ body_parameter["type"]["types"].insert(1, td_type)
337
+ if not existing_td:
338
+ code_model["types"].append(td_type)
339
+ else:
340
+ td_list_or_dict = copy.deepcopy(body_parameter["type"]["types"][0])
341
+ td_elem = existing_td or {**source, "base": "typeddict"}
342
+ td_list_or_dict["elementType"] = td_elem
343
+ body_parameter["type"]["types"].insert(1, td_list_or_dict)
344
+ if not existing_td:
345
+ code_model["types"].append(td_elem)
346
+
347
347
  def add_body_param_type(
348
348
  self,
349
349
  code_model: dict[str, Any],
@@ -353,54 +353,82 @@ class PreProcessPlugin(YamlUpdatePlugin):
353
353
  if ( # pylint: disable=too-many-boolean-expressions
354
354
  body_parameter
355
355
  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
- )
356
+ and (has_json_content_type(body_parameter) or (self.is_tsp and has_multi_part_content_type(body_parameter)))
360
357
  and not body_parameter["type"].get("xmlMetadata")
361
358
  and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
362
359
  ):
363
360
  origin_type = body_parameter["type"]["type"]
364
361
  model_type = (
365
- body_parameter["type"]
366
- if origin_type == "model"
367
- else body_parameter["type"].get("elementType", {})
362
+ body_parameter["type"] if origin_type == "model" else body_parameter["type"].get("elementType", {})
368
363
  )
369
364
  is_dpg_model = model_type.get("base") == "dpg"
365
+ is_json_model = model_type.get("base") == "json"
366
+ is_typeddict_only = self.options["models-mode"] == "typeddict"
367
+
370
368
  body_parameter["type"] = {
371
369
  "type": "combined",
372
370
  "types": [body_parameter["type"]],
373
371
  }
374
- # don't add binary overload for multipart content type
375
- if not (self.is_tsp and has_multi_part_content_type(body_parameter)):
372
+ # don't add binary overload for multipart content type or typeddict-only mode
373
+ if not (self.is_tsp and has_multi_part_content_type(body_parameter)) and not is_typeddict_only:
376
374
  body_parameter["type"]["types"].append(KNOWN_TYPES["binary"])
377
375
 
376
+ # Add typeddict overload for non-spread dpg models
378
377
  if self.options["models-mode"] == "dpg" and is_dpg_model:
379
- if origin_type == "model":
380
- body_parameter["type"]["types"].insert(1, KNOWN_TYPES["any-object"])
381
- else:
382
- # dict or list
383
- # copy the original dict / list type
384
- any_obj_list_or_dict = copy.deepcopy(
385
- body_parameter["type"]["types"][0]
378
+ cross_lang_id = model_type.get("crossLanguageDefinitionId")
379
+ existing_td = self._find_existing_typeddict(code_model, cross_lang_id)
380
+ self._insert_typeddict_overload(code_model, body_parameter, model_type, origin_type, existing_td)
381
+
382
+ # For spread bodies (json base), add a typeddict overload that references
383
+ # the original model. This replaces the JSON single-body overload.
384
+ if is_json_model:
385
+ cross_lang_id = model_type.get("crossLanguageDefinitionId")
386
+ original = None
387
+ if cross_lang_id:
388
+ original = next(
389
+ (
390
+ t
391
+ for t in code_model["types"]
392
+ if t.get("type") == "model"
393
+ and t.get("crossLanguageDefinitionId") == cross_lang_id
394
+ and t is not model_type
395
+ ),
396
+ None,
386
397
  )
387
- any_obj_list_or_dict["elementType"] = KNOWN_TYPES["any-object"]
388
- body_parameter["type"]["types"].insert(1, any_obj_list_or_dict)
398
+
399
+ if is_typeddict_only and original:
400
+ # In typeddict-only mode, the original dpg model already renders
401
+ # as a TypedDict — reference it directly, no copy needed.
402
+ if origin_type == "model":
403
+ body_parameter["type"]["types"].insert(1, original)
404
+ else:
405
+ td_list_or_dict = copy.deepcopy(body_parameter["type"]["types"][0])
406
+ td_list_or_dict["elementType"] = original
407
+ body_parameter["type"]["types"].insert(1, td_list_or_dict)
408
+ else:
409
+ source = original or model_type
410
+ existing_td = self._find_existing_typeddict(code_model, cross_lang_id)
411
+ self._insert_typeddict_overload(code_model, body_parameter, source, origin_type, existing_td)
412
+
413
+ if len(body_parameter["type"]["types"]) == 1:
414
+ # Only one body variant remains (e.g. typeddict-only mode where the
415
+ # binary and JSON overloads are omitted). Collapse the combined
416
+ # wrapper back to the single type so we don't emit a lone
417
+ # ``@overload`` (mypy rejects a single overload definition).
418
+ body_parameter["type"] = body_parameter["type"]["types"][0]
419
+ return
420
+
389
421
  code_model["types"].append(body_parameter["type"])
390
422
 
391
- def pad_reserved_words(
392
- self, name: str, pad_type: PadType, yaml_type: dict[str, Any]
393
- ) -> str:
423
+
424
+ def pad_reserved_words(self, name: str, pad_type: PadType, yaml_type: dict[str, Any]) -> str:
394
425
  # we want to pad hidden variables as well
395
426
  if not name:
396
427
  # we'll pass in empty operation groups sometime etc.
397
428
  return name
398
429
 
399
430
  if self.is_tsp:
400
- reserved_words = {
401
- k: (v + TSP_RESERVED_WORDS.get(k, []))
402
- for k, v in RESERVED_WORDS.items()
403
- }
431
+ reserved_words = {k: (v + TSP_RESERVED_WORDS.get(k, [])) for k, v in RESERVED_WORDS.items()}
404
432
  else:
405
433
  reserved_words = RESERVED_WORDS
406
434
  name = pad_special_chars(name)
@@ -417,24 +445,18 @@ class PreProcessPlugin(YamlUpdatePlugin):
417
445
  def update_types(self, yaml_data: list[dict[str, Any]]) -> None:
418
446
  for type in yaml_data:
419
447
  for property in type.get("properties", []):
420
- property["description"] = update_description(
421
- property.get("description", "")
422
- )
448
+ property["description"] = update_description(property.get("description", ""))
423
449
  if not property.get("isExactName", False):
424
450
  property["clientName"] = self.pad_reserved_words(
425
451
  property["clientName"].lower(), PadType.PROPERTY, property
426
452
  )
427
453
  add_redefined_builtin_info(property["clientName"], property)
428
454
  if type.get("name"):
429
- pad_type = (
430
- PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
431
- )
455
+ pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
432
456
  if type["type"] != "enumvalue":
433
457
  name = self.pad_reserved_words(type["name"], pad_type, type)
434
458
  type["name"] = name[0].upper() + name[1:]
435
- type["description"] = update_description(
436
- type.get("description", ""), type["name"]
437
- )
459
+ type["description"] = update_description(type.get("description", ""), type["name"])
438
460
  type["snakeCaseName"] = to_snake_case(type["name"])
439
461
  if type.get("values"):
440
462
  # we're enums - enum values are UPPER_CASE so no padding needed for reserved words
@@ -452,24 +474,16 @@ class PreProcessPlugin(YamlUpdatePlugin):
452
474
  yaml_data.append(CLOUD_SETTING["type"]) # type: ignore
453
475
 
454
476
  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
- )
477
+ yaml_data["description"] = update_description(yaml_data["description"], default_description=yaml_data["name"])
458
478
  yaml_data["legacyFilename"] = to_snake_case(yaml_data["name"].replace(" ", "_"))
459
479
  parameters = yaml_data["parameters"]
460
480
  for parameter in parameters:
461
481
  self.update_parameter(parameter)
462
482
  if parameter["clientName"] == "credential":
463
483
  policy = parameter["type"].get("policy")
464
- if (
465
- policy
466
- and policy["type"] == "BearerTokenCredentialPolicy"
467
- and self.azure_arm
468
- ):
484
+ if policy and policy["type"] == "BearerTokenCredentialPolicy" and self.azure_arm:
469
485
  policy["type"] = "ARMChallengeAuthenticationPolicy"
470
- policy["credentialScopes"] = [
471
- "https://management.azure.com/.default"
472
- ]
486
+ policy["credentialScopes"] = ["https://management.azure.com/.default"]
473
487
  if (
474
488
  (not self.version_tolerant or self.azure_arm)
475
489
  and parameters
@@ -489,9 +503,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
489
503
  if self.azure_arm and yaml_data["parameters"]:
490
504
  yaml_data["parameters"].append(CLOUD_SETTING)
491
505
 
492
- def get_operation_updater(
493
- self, yaml_data: dict[str, Any]
494
- ) -> Callable[[dict[str, Any], dict[str, Any]], None]:
506
+ def get_operation_updater(self, yaml_data: dict[str, Any]) -> Callable[[dict[str, Any], dict[str, Any]], None]:
495
507
  if yaml_data["discriminator"] == "lropaging":
496
508
  return self.update_lro_paging_operation
497
509
  if yaml_data["discriminator"] == "lro":
@@ -503,8 +515,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
503
515
  def update_parameter(self, yaml_data: dict[str, Any]) -> None:
504
516
  yaml_data["description"] = update_description(yaml_data.get("description", ""))
505
517
  if not yaml_data.get("isExactName", False) and not (
506
- yaml_data["location"] == "header"
507
- and yaml_data["clientName"] in ("content_type", "accept")
518
+ yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")
508
519
  ):
509
520
  yaml_data["clientName"] = self.pad_reserved_words(
510
521
  yaml_data["clientName"].lower(), PadType.PARAMETER, yaml_data
@@ -520,16 +531,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
520
531
  prop: (
521
532
  param_name
522
533
  if prop in exact_name_props
523
- else self.pad_reserved_words(
524
- param_name, PadType.PARAMETER, yaml_data
525
- ).lower()
534
+ else self.pad_reserved_words(param_name, PadType.PARAMETER, yaml_data).lower()
526
535
  )
527
536
  for prop, param_name in yaml_data["propertyToParameterName"].items()
528
537
  }
529
538
  wire_name_lower = (yaml_data.get("wireName") or "").lower()
530
539
  if yaml_data["location"] == "header" and (
531
- wire_name_lower in HEADERS_HIDE_IN_METHOD
532
- or yaml_data.get("clientDefaultValue") == "multipart/form-data"
540
+ wire_name_lower in HEADERS_HIDE_IN_METHOD or yaml_data.get("clientDefaultValue") == "multipart/form-data"
533
541
  ):
534
542
  yaml_data["hideInMethod"] = True
535
543
  if self.version_tolerant and yaml_data["location"] == "header":
@@ -538,10 +546,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
538
546
  headers_convert(yaml_data, ETAG_MATCH_DATA)
539
547
  elif role == "ifNoneMatch":
540
548
  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
- ):
549
+ if wire_name_lower in ["$host", "content-type", "accept"] and yaml_data["type"]["type"] == "constant":
545
550
  yaml_data["clientDefaultValue"] = yaml_data["type"]["value"]
546
551
 
547
552
  def update_operation(
@@ -551,17 +556,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
551
556
  *,
552
557
  is_overload: bool = False,
553
558
  ) -> None:
554
- yaml_data["groupName"] = self.pad_reserved_words(
555
- yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data
556
- )
559
+ yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data)
557
560
  yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
558
561
  if yaml_data.get("isExactName", False):
559
562
  # exact() client name: keep the operation name as-is without lowercasing,
560
563
  # snake-casing, or padding reserved words.
561
564
  if yaml_data.get("isLroInitialOperation") is True:
562
- yaml_data["name"] = (
563
- "_" + extract_original_name(yaml_data["name"]) + "_initial"
564
- )
565
+ yaml_data["name"] = "_" + extract_original_name(yaml_data["name"]) + "_initial"
565
566
  else:
566
567
  yaml_data["name"] = yaml_data["name"].lower()
567
568
  if yaml_data.get("isLroInitialOperation") is True:
@@ -575,12 +576,8 @@ class PreProcessPlugin(YamlUpdatePlugin):
575
576
  + "_initial"
576
577
  )
577
578
  else:
578
- yaml_data["name"] = self.pad_reserved_words(
579
- yaml_data["name"], PadType.METHOD, yaml_data
580
- )
581
- yaml_data["description"] = update_description(
582
- yaml_data["description"], yaml_data["name"]
583
- )
579
+ yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD, yaml_data)
580
+ yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
584
581
  yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
585
582
  body_parameter = yaml_data.get("bodyParameter")
586
583
  for parameter in yaml_data["parameters"]:
@@ -601,12 +598,8 @@ class PreProcessPlugin(YamlUpdatePlugin):
601
598
  def _update_lro_operation_helper(self, yaml_data: dict[str, Any]) -> None:
602
599
  for response in yaml_data.get("responses", []):
603
600
  response["discriminator"] = "lro"
604
- response["pollerSync"] = (
605
- response.get("pollerSync") or "azure.core.polling.LROPoller"
606
- )
607
- response["pollerAsync"] = (
608
- response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
609
- )
601
+ response["pollerSync"] = response.get("pollerSync") or "azure.core.polling.LROPoller"
602
+ response["pollerAsync"] = response.get("pollerAsync") or "azure.core.polling.AsyncLROPoller"
610
603
  if not response.get("pollingMethodSync"):
611
604
  response["pollingMethodSync"] = (
612
605
  "azure.mgmt.core.polling.arm_polling.ARMPolling"
@@ -628,9 +621,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
628
621
  item_type: Optional[dict[str, Any]] = None,
629
622
  ) -> None:
630
623
  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
- )
624
+ self.update_paging_operation(code_model, yaml_data, is_overload=is_overload, item_type=item_type)
634
625
  yaml_data["discriminator"] = "lropaging"
635
626
  for response in yaml_data.get("responses", []):
636
627
  response["discriminator"] = "lropaging"
@@ -653,16 +644,12 @@ class PreProcessPlugin(YamlUpdatePlugin):
653
644
  response["type"] = KNOWN_TYPES["binary"]
654
645
 
655
646
  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
- )
647
+ self.update_operation(code_model, yaml_data["initialOperation"], is_overload=is_overload)
659
648
  convert_initial_operation_response_type(yaml_data["initialOperation"])
660
649
  self._update_lro_operation_helper(yaml_data)
661
650
  for overload in yaml_data.get("overloads", []):
662
651
  self._update_lro_operation_helper(overload)
663
- self.update_operation(
664
- code_model, overload["initialOperation"], is_overload=True
665
- )
652
+ self.update_operation(code_model, overload["initialOperation"], is_overload=True)
666
653
  convert_initial_operation_response_type(overload["initialOperation"])
667
654
 
668
655
  def update_paging_operation(
@@ -680,9 +667,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
680
667
  PadType.OPERATION_GROUP,
681
668
  yaml_data["nextOperation"],
682
669
  )
683
- yaml_data["nextOperation"]["groupName"] = to_snake_case(
684
- yaml_data["nextOperation"]["groupName"]
685
- )
670
+ yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
686
671
  for response in yaml_data["nextOperation"].get("responses", []):
687
672
  update_paging_response(response)
688
673
  response["itemType"] = item_type
@@ -690,13 +675,9 @@ class PreProcessPlugin(YamlUpdatePlugin):
690
675
  update_paging_response(response)
691
676
  response["itemType"] = item_type
692
677
  for overload in yaml_data.get("overloads", []):
693
- self.update_paging_operation(
694
- code_model, overload, is_overload=True, item_type=item_type
695
- )
678
+ self.update_paging_operation(code_model, overload, is_overload=True, item_type=item_type)
696
679
 
697
- def update_operation_groups(
698
- self, code_model: dict[str, Any], client: dict[str, Any]
699
- ) -> None:
680
+ def update_operation_groups(self, code_model: dict[str, Any], client: dict[str, Any]) -> None:
700
681
  operation_groups_yaml_data = client.get("operationGroups", [])
701
682
  for operation_group in operation_groups_yaml_data:
702
683
  operation_group["identifyName"] = self.pad_reserved_words(
@@ -704,17 +685,13 @@ class PreProcessPlugin(YamlUpdatePlugin):
704
685
  PadType.OPERATION_GROUP,
705
686
  operation_group,
706
687
  )
707
- operation_group["identifyName"] = to_snake_case(
708
- operation_group["identifyName"]
709
- )
688
+ operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
710
689
  operation_group["propertyName"] = self.pad_reserved_words(
711
690
  operation_group["propertyName"],
712
691
  PadType.OPERATION_GROUP,
713
692
  operation_group,
714
693
  )
715
- operation_group["propertyName"] = to_snake_case(
716
- operation_group["propertyName"]
717
- )
694
+ operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
718
695
  operation_group["className"] = update_operation_group_class_name(
719
696
  client["name"], operation_group["className"]
720
697
  )
@@ -738,6 +715,4 @@ class PreProcessPlugin(YamlUpdatePlugin):
738
715
  if __name__ == "__main__":
739
716
  # TSP pipeline will call this
740
717
  args, unknown_args = parse_args()
741
- PreProcessPlugin(
742
- output_folder=args.output_folder, tsp_file=args.tsp_file, **unknown_args
743
- ).process()
718
+ 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",
@@ -56,6 +56,7 @@ pygen/codegen/serializers/request_builders_serializer.py
56
56
  pygen/codegen/serializers/sample_serializer.py
57
57
  pygen/codegen/serializers/test_serializer.py
58
58
  pygen/codegen/serializers/types_serializer.py
59
+ pygen/codegen/serializers/unions_serializer.py
59
60
  pygen/codegen/serializers/utils.py
60
61
  pygen/codegen/templates/client.py.jinja2
61
62
  pygen/codegen/templates/client_container.py.jinja2
@@ -90,6 +91,7 @@ pygen/codegen/templates/serialization.py.jinja2
90
91
  pygen/codegen/templates/test.py.jinja2
91
92
  pygen/codegen/templates/testpreparer.py.jinja2
92
93
  pygen/codegen/templates/types.py.jinja2
94
+ pygen/codegen/templates/unions.py.jinja2
93
95
  pygen/codegen/templates/utils.py.jinja2
94
96
  pygen/codegen/templates/validation.py.jinja2
95
97
  pygen/codegen/templates/version.py.jinja2