Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/http-client-python"
---

Keep original client name for backcompat reasons when the name is only padded for tsp generations
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,11 @@ def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path:
@property
def has_operation_named_list(self) -> bool:
return any(o.name.lower() == "list" for c in self.clients for og in c.operation_groups for o in og.operations)

@property
def has_padded_model_property(self) -> bool:
for model_type in self.model_types:
for prop in model_type.properties:
if prop.original_tsp_name:
return True
return False
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def __init__(
self.flattened_names: list[str] = yaml_data.get("flattenedNames", [])
self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")

def pylint_disable(self) -> str:
retval: str = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ def declare_property(self, prop: Property) -> str:
if prop.xml_metadata:
args.append(f"xml={prop.xml_metadata}")

if prop.original_tsp_name:
args.append(f'original_tsp_name="{prop.original_tsp_name}"')

field = "rest_discriminator" if prop.is_discriminator else "rest_field"
type_ignore = (
" # type: ignore"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,12 @@ class Model(_MyMutableMapping):
if not rf._rest_name_input:
rf._rest_name_input = attr
cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items())
{% if code_model.has_padded_model_property %}
cls._backcompat_attr_to_rest_field: dict[str, _RestField] = {
Model._get_backcompat_attribute_name(cls._attr_to_rest_field, attr): rf for attr, rf in cls
._attr_to_rest_field.items()
}
{% endif %}
cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")

return super().__new__(cls)
Expand All @@ -645,6 +651,18 @@ class Model(_MyMutableMapping):
if hasattr(base, "__mapping__"):
base.__mapping__[discriminator or cls.__name__] = cls # type: ignore

{% if code_model.has_padded_model_property %}
@classmethod
def _get_backcompat_attribute_name(cls, attr_to_rest_field: dict[str, "_RestField"], attr_name: str) -> str:
rest_field_obj = attr_to_rest_field.get(attr_name) # pylint: disable=protected-access
if rest_field_obj is None:
return attr_name
original_tsp_name = getattr(rest_field_obj, "_original_tsp_name", None) # pylint: disable=protected-access
if original_tsp_name:
return original_tsp_name
return attr_name
{% endif %}

@classmethod
def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]:
for v in cls.__dict__.values():
Expand Down Expand Up @@ -971,6 +989,9 @@ def _failsafe_deserialize_xml(
return None


{% if code_model.has_padded_model_property %}
# pylint: disable=too-many-instance-attributes
{% endif %}
class _RestField:
def __init__(
self,
Expand All @@ -983,6 +1004,9 @@ class _RestField:
format: typing.Optional[str] = None,
is_multipart_file_input: bool = False,
xml: typing.Optional[dict[str, typing.Any]] = None,
{% if code_model.has_padded_model_property %}
original_tsp_name: typing.Optional[str] = None,
{% endif %}
):
self._type = type
self._rest_name_input = name
Expand All @@ -994,6 +1018,9 @@ class _RestField:
self._format = format
self._is_multipart_file_input = is_multipart_file_input
self._xml = xml if xml is not None else {}
{% if code_model.has_padded_model_property %}
self._original_tsp_name = original_tsp_name
{% endif %}

@property
def _class_type(self) -> typing.Any:
Expand Down Expand Up @@ -1045,6 +1072,9 @@ def rest_field(
format: typing.Optional[str] = None,
is_multipart_file_input: bool = False,
xml: typing.Optional[dict[str, typing.Any]] = None,
{% if code_model.has_padded_model_property %}
original_tsp_name: typing.Optional[str] = None,
{% endif %}
) -> typing.Any:
return _RestField(
name=name,
Expand All @@ -1054,6 +1084,9 @@ def rest_field(
format=format,
is_multipart_file_input=is_multipart_file_input,
xml=xml,
{% if code_model.has_padded_model_property %}
original_tsp_name=original_tsp_name,
{% endif %}
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def add_body_param_type(
body_parameter["type"]["types"].insert(1, any_obj_list_or_dict)
code_model["types"].append(body_parameter["type"])

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

def update_types(self, yaml_data: list[dict[str, Any]]) -> None:
for type in yaml_data:
for property in type.get("properties", []):
property["description"] = update_description(property.get("description", ""))
property["clientName"] = self.pad_reserved_words(property["clientName"].lower(), PadType.PROPERTY)
property["clientName"] = self.pad_reserved_words(
property["clientName"].lower(), PadType.PROPERTY, property
)
add_redefined_builtin_info(property["clientName"], property)
if type.get("name"):
pad_type = PadType.MODEL if type["type"] == "model" else PadType.ENUM_CLASS
name = self.pad_reserved_words(type["name"], pad_type)
name = self.pad_reserved_words(type["name"], pad_type, type)
type["name"] = name[0].upper() + name[1:]
type["description"] = update_description(type.get("description", ""), type["name"])
type["snakeCaseName"] = to_snake_case(type["name"])
if type.get("values"):
# we're enums
values_to_add = []
for value in type["values"]:
padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE).upper()
padded_name = self.pad_reserved_words(value["name"].lower(), PadType.ENUM_VALUE, value).upper()
if self.version_tolerant:
if padded_name[0] in "0123456789":
padded_name = "ENUM_" + padded_name
Expand Down Expand Up @@ -364,12 +370,14 @@ def get_operation_updater(self, yaml_data: dict[str, Any]) -> Callable[[dict[str
def update_parameter(self, yaml_data: dict[str, Any]) -> None:
yaml_data["description"] = update_description(yaml_data.get("description", ""))
if not (yaml_data["location"] == "header" and yaml_data["clientName"] in ("content_type", "accept")):
yaml_data["clientName"] = self.pad_reserved_words(yaml_data["clientName"].lower(), PadType.PARAMETER)
yaml_data["clientName"] = self.pad_reserved_words(
yaml_data["clientName"].lower(), PadType.PARAMETER, yaml_data
)
if yaml_data.get("propertyToParameterName"):
# need to create a new one with padded keys and values
yaml_data["propertyToParameterName"] = {
self.pad_reserved_words(prop, PadType.PROPERTY): self.pad_reserved_words(
param_name, PadType.PARAMETER
self.pad_reserved_words(prop, PadType.PROPERTY, yaml_data): self.pad_reserved_words(
param_name, PadType.PARAMETER, yaml_data
).lower()
for prop, param_name in yaml_data["propertyToParameterName"].items()
}
Expand All @@ -390,15 +398,17 @@ def update_operation(
*,
is_overload: bool = False,
) -> None:
yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP)
yaml_data["groupName"] = self.pad_reserved_words(yaml_data["groupName"], PadType.OPERATION_GROUP, yaml_data)
yaml_data["groupName"] = to_snake_case(yaml_data["groupName"])
yaml_data["name"] = yaml_data["name"].lower()
if yaml_data.get("isLroInitialOperation") is True:
yaml_data["name"] = (
"_" + self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD) + "_initial"
"_"
+ self.pad_reserved_words(extract_original_name(yaml_data["name"]), PadType.METHOD, yaml_data)
+ "_initial"
)
else:
yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD)
yaml_data["name"] = self.pad_reserved_words(yaml_data["name"], PadType.METHOD, yaml_data)
yaml_data["description"] = update_description(yaml_data["description"], yaml_data["name"])
yaml_data["summary"] = update_description(yaml_data.get("summary", ""))
body_parameter = yaml_data.get("bodyParameter")
Expand Down Expand Up @@ -485,7 +495,7 @@ def update_paging_operation(
item_type = item_type or yaml_data["itemType"]["elementType"]
if yaml_data.get("nextOperation"):
yaml_data["nextOperation"]["groupName"] = self.pad_reserved_words(
yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP
yaml_data["nextOperation"]["groupName"], PadType.OPERATION_GROUP, yaml_data["nextOperation"]
)
yaml_data["nextOperation"]["groupName"] = to_snake_case(yaml_data["nextOperation"]["groupName"])
for response in yaml_data["nextOperation"].get("responses", []):
Expand All @@ -503,10 +513,11 @@ def update_operation_groups(self, code_model: dict[str, Any], client: dict[str,
operation_group["identifyName"] = self.pad_reserved_words(
operation_group.get("name", operation_group["propertyName"]),
PadType.OPERATION_GROUP,
operation_group,
)
operation_group["identifyName"] = to_snake_case(operation_group["identifyName"])
operation_group["propertyName"] = self.pad_reserved_words(
operation_group["propertyName"], PadType.OPERATION_GROUP
operation_group["propertyName"], PadType.OPERATION_GROUP, operation_group
)
operation_group["propertyName"] = to_snake_case(operation_group["propertyName"])
operation_group["className"] = update_operation_group_class_name(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


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


def test_escaped_reserved_words():
Expand Down
Loading