Skip to content

Commit 8705df1

Browse files
iscai-msftiscai-msftmsyyc
authored
[python] backcompat tsp padding (#9017)
fix #9013 Related PR: - [ ] Azure/azure-sdk-for-python#44084 - [x] #9019 --------- Co-authored-by: iscai-msft <isabellavcai@gmail.com> Co-authored-by: Yuchao Yan <yuchaoyan@microsoft.com>
1 parent 7b47584 commit 8705df1

File tree

7 files changed

+76
-13
lines changed

7 files changed

+76
-13
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/http-client-python"
5+
---
6+
7+
Keep original client name for backcompat reasons when the name is only padded for tsp generations

packages/http-client-python/generator/pygen/codegen/models/code_model.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,14 @@ def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path:
491491
def has_operation_named_list(self) -> bool:
492492
return any(o.name.lower() == "list" for c in self.clients for og in c.operation_groups for o in og.operations)
493493

494+
@property
495+
def has_padded_model_property(self) -> bool:
496+
for model_type in self.model_types:
497+
for prop in model_type.properties:
498+
if prop.original_tsp_name:
499+
return True
500+
return False
501+
494502
@property
495503
def external_types(self) -> list[ExternalType]:
496504
"""All of the external types"""

packages/http-client-python/generator/pygen/codegen/models/property.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def __init__(
3939
self.flattened_names: list[str] = yaml_data.get("flattenedNames", [])
4040
self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
4141
self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
42+
self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")
4243

4344
def pylint_disable(self) -> str:
4445
retval: str = ""

packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,9 @@ def declare_property(self, prop: Property) -> str:
333333
if prop.xml_metadata:
334334
args.append(f"xml={prop.xml_metadata}")
335335

336+
if prop.original_tsp_name:
337+
args.append(f'original_tsp_name="{prop.original_tsp_name}"')
338+
336339
field = "rest_discriminator" if prop.is_discriminator else "rest_field"
337340
type_ignore = (
338341
" # type: ignore"

packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,12 @@ class Model(_MyMutableMapping):
662662
if not rf._rest_name_input:
663663
rf._rest_name_input = attr
664664
cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items())
665+
{% if code_model.has_padded_model_property %}
666+
cls._backcompat_attr_to_rest_field: dict[str, _RestField] = {
667+
Model._get_backcompat_attribute_name(cls._attr_to_rest_field, attr): rf for attr, rf in cls
668+
._attr_to_rest_field.items()
669+
}
670+
{% endif %}
665671
cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")
666672

667673
return super().__new__(cls)
@@ -671,6 +677,18 @@ class Model(_MyMutableMapping):
671677
if hasattr(base, "__mapping__"):
672678
base.__mapping__[discriminator or cls.__name__] = cls # type: ignore
673679

680+
{% if code_model.has_padded_model_property %}
681+
@classmethod
682+
def _get_backcompat_attribute_name(cls, attr_to_rest_field: dict[str, "_RestField"], attr_name: str) -> str:
683+
rest_field_obj = attr_to_rest_field.get(attr_name) # pylint: disable=protected-access
684+
if rest_field_obj is None:
685+
return attr_name
686+
original_tsp_name = getattr(rest_field_obj, "_original_tsp_name", None) # pylint: disable=protected-access
687+
if original_tsp_name:
688+
return original_tsp_name
689+
return attr_name
690+
{% endif %}
691+
674692
@classmethod
675693
def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]:
676694
for v in cls.__dict__.values():
@@ -997,6 +1015,9 @@ def _failsafe_deserialize_xml(
9971015
return None
9981016

9991017

1018+
{% if code_model.has_padded_model_property %}
1019+
# pylint: disable=too-many-instance-attributes
1020+
{% endif %}
10001021
class _RestField:
10011022
def __init__(
10021023
self,
@@ -1009,6 +1030,9 @@ class _RestField:
10091030
format: typing.Optional[str] = None,
10101031
is_multipart_file_input: bool = False,
10111032
xml: typing.Optional[dict[str, typing.Any]] = None,
1033+
{% if code_model.has_padded_model_property %}
1034+
original_tsp_name: typing.Optional[str] = None,
1035+
{% endif %}
10121036
):
10131037
self._type = type
10141038
self._rest_name_input = name
@@ -1020,6 +1044,9 @@ class _RestField:
10201044
self._format = format
10211045
self._is_multipart_file_input = is_multipart_file_input
10221046
self._xml = xml if xml is not None else {}
1047+
{% if code_model.has_padded_model_property %}
1048+
self._original_tsp_name = original_tsp_name
1049+
{% endif %}
10231050

10241051
@property
10251052
def _class_type(self) -> typing.Any:
@@ -1075,6 +1102,9 @@ def rest_field(
10751102
format: typing.Optional[str] = None,
10761103
is_multipart_file_input: bool = False,
10771104
xml: typing.Optional[dict[str, typing.Any]] = None,
1105+
{% if code_model.has_padded_model_property %}
1106+
original_tsp_name: typing.Optional[str] = None,
1107+
{% endif %}
10781108
) -> typing.Any:
10791109
return _RestField(
10801110
name=name,
@@ -1084,6 +1114,9 @@ def rest_field(
10841114
format=format,
10851115
is_multipart_file_input=is_multipart_file_input,
10861116
xml=xml,
1117+
{% if code_model.has_padded_model_property %}
1118+
original_tsp_name=original_tsp_name,
1119+
{% endif %}
10871120
)
10881121

10891122

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def add_body_param_type(
236236
body_parameter["type"]["types"].insert(1, any_obj_list_or_dict)
237237
code_model["types"].append(body_parameter["type"])
238238

239-
def pad_reserved_words(self, name: str, pad_type: PadType):
239+
def pad_reserved_words(self, name: str, pad_type: PadType, yaml_type: dict[str, Any]) -> str:
240240
# we want to pad hidden variables as well
241241
if not name:
242242
# we'll pass in empty operation groups sometime etc.
@@ -250,26 +250,32 @@ def pad_reserved_words(self, name: str, pad_type: PadType):
250250
name_prefix = "_" if name[0] == "_" else ""
251251
name = name[1:] if name[0] == "_" else name
252252
if name.lower() in reserved_words[pad_type]:
253+
if self.is_tsp and name.lower() in TSP_RESERVED_WORDS.get(pad_type, []):
254+
# to maintain backcompat for cases where we pad in tsp but not in autorest,
255+
# if we have a tsp reserved word, we also want to keep track of the original name for backcompat
256+
yaml_type["originalTspName"] = name_prefix + name
253257
return name_prefix + name + pad_type
254258
return name_prefix + name
255259

256260
def update_types(self, yaml_data: list[dict[str, Any]]) -> None:
257261
for type in yaml_data:
258262
for property in type.get("properties", []):
259263
property["description"] = update_description(property.get("description", ""))
260-
property["clientName"] = self.pad_reserved_words(property["clientName"].lower(), PadType.PROPERTY)
264+
property["clientName"] = self.pad_reserved_words(
265+
property["clientName"].lower(), PadType.PROPERTY, property
266+
)
261267
add_redefined_builtin_info(property["clientName"], property)
262268
if type.get("name"):
263269
pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
264-
name = self.pad_reserved_words(type["name"], pad_type)
270+
name = self.pad_reserved_words(type["name"], pad_type, type)
265271
type["name"] = name[0].upper() + name[1:]
266272
type["description"] = update_description(type.get("description", ""), type["name"])
267273
type["snakeCaseName"] = to_snake_case(type["name"])
268274
if type.get("values"):
269275
# we're enums
270276
values_to_add = []
271277
for value in type["values"]:
272-
padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE).upper()
278+
padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE, value).upper()
273279
if self.version_tolerant:
274280
if padded_name[0] in "0123456789":
275281
padded_name = "ENUM_" + padded_name
@@ -364,12 +370,14 @@ def get_operation_updater(self, yaml_data: dict[str, Any]) -> Callable[[dict[str
364370
def update_parameter(self, yaml_data: dict[str, Any]) -> None:
365371
yaml_data["description"] = update_description(yaml_data.get("description", ""))
366372
if not (yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")):
367-
yaml_data["clientName"] = self.pad_reserved_words(yaml_data["clientName"].lower(), PadType.PARAMETER)
373+
yaml_data["clientName"] = self.pad_reserved_words(
374+
yaml_data["clientName"].lower(), PadType.PARAMETER, yaml_data
375+
)
368376
if yaml_data.get("propertyToParameterName"):
369377
# need to create a new one with padded keys and values
370378
yaml_data["propertyToParameterName"] = {
371-
self.pad_reserved_words(prop, PadType.PROPERTY): self.pad_reserved_words(
372-
param_name, PadType.PARAMETER
379+
self.pad_reserved_words(prop, PadType.PROPERTY, yaml_data): self.pad_reserved_words(
380+
param_name, PadType.PARAMETER, yaml_data
373381
).lower()
374382
for prop, param_name in yaml_data["propertyToParameterName"].items()
375383
}
@@ -390,15 +398,17 @@ def update_operation(
390398
*,
391399
is_overload: bool = False,
392400
) -> None:
393-
yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP)
401+
yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data)
394402
yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
395403
yaml_data["name"] = yaml_data["name"].lower()
396404
if yaml_data.get("isLroInitialOperation") is True:
397405
yaml_data["name"] = (
398-
"_" + self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD) + "_initial"
406+
"_"
407+
+ self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD, yaml_data)
408+
+ "_initial"
399409
)
400410
else:
401-
yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
411+
yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD, yaml_data)
402412
yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
403413
yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
404414
body_parameter = yaml_data.get("bodyParameter")
@@ -485,7 +495,7 @@ def update_paging_operation(
485495
item_type = item_type or yaml_data["itemType"]["elementType"]
486496
if yaml_data.get("nextOperation"):
487497
yaml_data["nextOperation"]["groupName"] = self.pad_reserved_words(
488-
yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP
498+
yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP, yaml_data["nextOperation"]
489499
)
490500
yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
491501
for response in yaml_data["nextOperation"].get("responses", []):
@@ -503,10 +513,11 @@ def update_operation_groups(self, code_model: dict[str, Any], client: dict[str,
503513
operation_group["identifyName"] = self.pad_reserved_words(
504514
operation_group.get("name", operation_group["propertyName"]),
505515
PadType.OPERATION_GROUP,
516+
operation_group,
506517
)
507518
operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
508519
operation_group["propertyName"] = self.pad_reserved_words(
509-
operation_group["propertyName"], PadType.OPERATION_GROUP
520+
operation_group["propertyName"], PadType.OPERATION_GROUP, operation_group
510521
)
511522
operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
512523
operation_group["className"] = update_operation_group_class_name(

packages/http-client-python/generator/test/unittests/test_name_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def pad_reserved_words(name: str, pad_type: PadType) -> str:
11-
return PreProcessPlugin(output_folder="").pad_reserved_words(name, pad_type)
11+
return PreProcessPlugin(output_folder="").pad_reserved_words(name, pad_type, {})
1212

1313

1414
def test_escaped_reserved_words():

0 commit comments

Comments
 (0)