Skip to content

Commit 025eceb

Browse files
l0lawrenceCopilot
andcommitted
Restore pre-TypedDict JSON dict overload when generate-typeddict is false
When generate-typeddict: false, the opt-out now performs a true revert to pre-TypedDict behavior by restoring the standalone raw-JSON dict @overload (and JSON in the impl Union) for both the explicit/model body path and the spread path, instead of only dropping the TypedDict overload. - add_overloads_for_body_param: only skip the single-body JSON overload when a TypedDict overload was actually inserted; otherwise keep it. - add_body_param_type: insert the raw-JSON (any-object) overload via new _insert_json_overload helper when TypedDict generation is disabled. - Update test_typeddict_overloads.py opt-out tests to assert the restored [model, JSON, binary] / flattened+JSON+binary overload sets. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 753f3788-98a9-4e39-8655-9c6a3ce90536
1 parent be122e6 commit 025eceb

2 files changed

Lines changed: 46 additions & 15 deletions

File tree

packages/http-client-python/generator/pygen/preprocess/__init__.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,12 @@ def add_overloads_for_body_param(yaml_data: dict[str, Any]) -> None:
9494
continue
9595
if body_type.get("type") == "model" and body_type.get("base") == "json":
9696
yaml_data["overloads"].append(add_overload(yaml_data, body_type, for_flatten_params=True))
97-
# Use the flattened JSON overload and skip the single-body JSON overload.
98-
# When TypedDict generation is disabled, this JSON overload remains.
99-
continue
97+
# When a TypedDict overload was inserted, it replaces the single-body JSON
98+
# overload, so skip it. When TypedDict generation is disabled, no TypedDict
99+
# overload exists and we keep the single-body raw-JSON overload (pre-TypedDict
100+
# behavior) by falling through.
101+
if any(t.get("base") == "typeddict" for t in body_parameter["type"]["types"]):
102+
continue
100103
yaml_data["overloads"].append(add_overload(yaml_data, body_type))
101104
content_type_param = next(p for p in yaml_data["parameters"] if p["wireName"].lower() == "content-type")
102105
content_type_param["inOverload"] = False
@@ -399,6 +402,25 @@ def _insert_typeddict_overload(
399402
if not existing_td:
400403
code_model["types"].append(td_elem)
401404

405+
@staticmethod
406+
def _insert_json_overload(
407+
body_parameter: dict[str, Any],
408+
origin_type: str,
409+
) -> None:
410+
"""Insert a raw-JSON (any-object) type into the body parameter's combined types.
411+
412+
This restores the pre-TypedDict dict-body overload used when TypedDict
413+
generation is disabled.
414+
"""
415+
if origin_type == "model":
416+
body_parameter["type"]["types"].insert(1, KNOWN_TYPES["any-object"])
417+
else:
418+
# dict or list: copy the original container type and swap its element
419+
# type for the raw-JSON any-object.
420+
any_obj_list_or_dict = copy.deepcopy(body_parameter["type"]["types"][0])
421+
any_obj_list_or_dict["elementType"] = KNOWN_TYPES["any-object"]
422+
body_parameter["type"]["types"].insert(1, any_obj_list_or_dict)
423+
402424
def add_body_param_type(
403425
self,
404426
code_model: dict[str, Any],
@@ -447,11 +469,15 @@ def add_body_param_type(
447469
if not (self.is_tsp and has_multi_part_content_type(body_parameter)) and not is_typeddict_only:
448470
body_parameter["type"]["types"].append(KNOWN_TYPES["binary"])
449471

450-
# Add typeddict overload for non-spread dpg models
451-
if self.options["models-mode"] == "dpg" and self.generate_typeddict and is_dpg_model:
452-
cross_lang_id = model_type.get("crossLanguageDefinitionId")
453-
existing_td = self._find_existing_typeddict(code_model, cross_lang_id, model_type.get("name"))
454-
self._insert_typeddict_overload(code_model, body_parameter, model_type, origin_type, existing_td)
472+
# Add the dict-body overload for non-spread dpg models: a TypedDict when
473+
# enabled, otherwise the raw-JSON overload (pre-TypedDict behavior).
474+
if self.options["models-mode"] == "dpg" and is_dpg_model:
475+
if self.generate_typeddict:
476+
cross_lang_id = model_type.get("crossLanguageDefinitionId")
477+
existing_td = self._find_existing_typeddict(code_model, cross_lang_id, model_type.get("name"))
478+
self._insert_typeddict_overload(code_model, body_parameter, model_type, origin_type, existing_td)
479+
else:
480+
self._insert_json_overload(body_parameter, origin_type)
455481

456482
# For spread bodies (json base), add a typeddict overload that references
457483
# the original model. This replaces the JSON single-body overload.

packages/http-client-python/tests/unit/test_typeddict_overloads.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def test_dpg_mode_still_emits_multiple_overloads():
105105

106106

107107
def test_dpg_mode_can_disable_typeddict_autogeneration():
108-
"""Opting out keeps the dpg model + binary overloads, but skips typeddict generation."""
108+
"""Opting out reverts to the pre-TypedDict overloads: model + raw-JSON + binary."""
109109
plugin = _plugin("dpg", **{"generate-typeddict": False})
110110
code_model, yaml_data, model_type = _json_model_operation()
111111
body_parameter = yaml_data["bodyParameter"]
@@ -114,13 +114,16 @@ def test_dpg_mode_can_disable_typeddict_autogeneration():
114114
add_overloads_for_body_param(yaml_data)
115115

116116
assert body_parameter["type"]["type"] == "combined"
117-
assert body_parameter["type"]["types"] == [model_type, {"type": "binary"}]
118-
assert len(yaml_data["overloads"]) == 2
117+
# The dict-body overload is the raw-JSON ``any-object`` (not a TypedDict).
118+
assert body_parameter["type"]["types"] == [model_type, {"type": "any-object"}, {"type": "binary"}]
119+
assert len(yaml_data["overloads"]) == 3
120+
overload_types = [o["bodyParameter"]["type"]["type"] for o in yaml_data["overloads"]]
121+
assert overload_types == ["model", "any-object", "binary"]
119122
assert not any(t for t in code_model["types"] if t.get("base") == "typeddict")
120123

121124

122125
def test_spread_body_opt_out_keeps_json_overload():
123-
"""Spread bodies keep the flattened JSON overload when TypedDict generation is disabled."""
126+
"""Spread bodies revert to pre-TypedDict: flattened + single-body JSON + binary."""
124127
plugin = _plugin("dpg", **{"generate-typeddict": False})
125128
spread_body = _json_spread_body_parameter("CreateRequest", "Contoso.Widget")
126129
yaml_data = {
@@ -139,9 +142,11 @@ def test_spread_body_opt_out_keeps_json_overload():
139142
assert spread_body["type"]["type"] == "combined"
140143
assert spread_body["type"]["types"][0]["base"] == "json"
141144
assert spread_body["type"]["types"][1] == {"type": "binary"}
142-
assert len(yaml_data["overloads"]) == 2
143-
json_overload = next(o for o in yaml_data["overloads"] if o["bodyParameter"]["type"].get("base") == "json")
144-
assert json_overload["bodyParameter"]["flattened"] is True
145+
assert len(yaml_data["overloads"]) == 3
146+
json_overloads = [o for o in yaml_data["overloads"] if o["bodyParameter"]["type"].get("base") == "json"]
147+
# Both a flattened (keyword params) overload AND a single-body raw-JSON overload.
148+
assert any(o["bodyParameter"].get("flattened") for o in json_overloads)
149+
assert any(not o["bodyParameter"].get("flattened") for o in json_overloads)
145150
assert not any(t for t in code_model["types"] if t.get("base") == "typeddict")
146151

147152

0 commit comments

Comments
 (0)