Skip to content

Commit

Permalink
Xml support (#2806)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* finish first version

* changelog

* lint

* fix lint

* fix

* format

* black

* black

* fix

* fix mypy

* fix

* refine

* fix lint and pyright

* fix

* fix

* fix lint

* fix lint

* fix

* fix

* fix pyright

* regenerate

---------

Co-authored-by: Yuchao Yan <yuchaoyan@microsoft.com>
  • Loading branch information
tadelesh and msyyc authored Sep 9, 2024
1 parent 9600df1 commit 10d8e5b
Show file tree
Hide file tree
Showing 234 changed files with 55,958 additions and 3,040 deletions.
7 changes: 7 additions & 0 deletions .chronus/changes/xml_support-2024-8-5-15-33-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@azure-tools/typespec-python"
---

dpg model support xml
2 changes: 1 addition & 1 deletion eng/scripts/run_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _single_dir_pylint(mod):
)
return True
except CalledProcessError as e:
logging.error("{} exited with linting error {}".format(inner_class.stem, e.returncode))
logging.error("{} exited with linting error {}".format(str(inner_class.absolute()), e.returncode))
return False


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from .model_type import ModelType
from .base import BaseType
from .request_builder import OverloadedRequestBuilder, RequestBuilder
from ...utils import xml_serializable, json_serializable

if TYPE_CHECKING:
from .code_model import CodeModel
Expand Down Expand Up @@ -422,14 +423,24 @@ def imports( # pylint: disable=too-many-branches, disable=too-many-statements
if self.parameters.has_body:
if self.has_form_data_body:
file_import.add_submodule_import(relative_path, "_model_base", ImportType.LOCAL)
else:
elif xml_serializable(self.parameters.body_parameter.default_content_type):
file_import.add_submodule_import(
f"{relative_path}_model_base",
"_get_element",
ImportType.LOCAL,
)
elif json_serializable(self.parameters.body_parameter.default_content_type):
file_import.add_submodule_import(
f"{relative_path}_model_base",
"SdkJSONEncoder",
ImportType.LOCAL,
)
file_import.add_import("json", ImportType.STDLIB)
if (self.default_error_deserialization or any(r.type for r in self.responses)) or self.non_default_errors:
if any(xml_serializable(str(r.default_content_type)) for r in self.responses):
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize_xml", ImportType.LOCAL)
elif any(r.type for r in self.responses):
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
if self.default_error_deserialization or self.non_default_errors:
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
return file_import

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict, Optional, TYPE_CHECKING, List, cast
from typing import Any, Dict, Optional, TYPE_CHECKING, List, cast, Union

from .base import BaseModel
from .constant_type import ConstantType
Expand Down Expand Up @@ -96,6 +96,10 @@ def is_base_discriminator(self) -> bool:
return self.is_polymorphic and self.client_default_value is None
return self.is_discriminator and self.is_polymorphic and cast(ConstantType, self.type).value is None

@property
def xml_metadata(self) -> Optional[Dict[str, Union[str, bool]]]:
return self.yaml_data.get("xmlMetadata")

def type_annotation(self, *, is_operation_file: bool = False) -> str:
if self.is_base_discriminator:
return "str"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from .parameter_serializer import ParameterSerializer, PopKwargType
from ..models.parameter_list import ParameterType
from . import utils
from ...utils import JSON_REGEXP
from ...utils import xml_serializable, json_serializable

T = TypeVar("T")
OrderedSet = Dict[T, None]
Expand All @@ -61,10 +61,6 @@ def _all_same(data: List[List[str]]) -> bool:
return len(data) > 1 and all(sorted(data[0]) == sorted(data[i]) for i in range(1, len(data)))


def _json_serializable(content_type: str) -> bool:
return bool(JSON_REGEXP.match(content_type.split(";")[0].strip().lower()))


def _need_type_ignore(builder: OperationType) -> bool:
for excep in builder.non_default_errors:
for status_code in excep.status_codes:
Expand Down Expand Up @@ -413,7 +409,9 @@ def _json_response_template_name(self) -> str:
return "response.json()"

@staticmethod
def declare_non_inputtable_headers_queries(builder: RequestBuilderType) -> List[str]:
def declare_non_inputtable_headers_queries(
builder: RequestBuilderType,
) -> List[str]:
def _get_value(param):
declaration = param.get_declaration() if param.constant else None
if param.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]:
Expand Down Expand Up @@ -688,7 +686,7 @@ def _serialize_body_parameter(self, builder: OperationType) -> List[str]:
f"'{body_param.type.serialization_type}'{is_xml_cmd}{serialization_ctxt_cmd})"
)
elif self.code_model.options["models_mode"] == "dpg":
if _json_serializable(body_param.default_content_type):
if json_serializable(body_param.default_content_type):
if hasattr(body_param.type, "encode") and body_param.type.encode: # type: ignore
create_body_call = (
f"_{body_kwarg_name} = json.dumps({body_param.client_name}, "
Expand All @@ -700,6 +698,8 @@ def _serialize_body_parameter(self, builder: OperationType) -> List[str]:
f"_{body_kwarg_name} = json.dumps({body_param.client_name}, "
"cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore"
)
elif xml_serializable(body_param.default_content_type):
create_body_call = f"_{body_kwarg_name} = _get_element({body_param.client_name})"
else:
create_body_call = f"_{body_kwarg_name} = {body_param.client_name}"
else:
Expand Down Expand Up @@ -961,8 +961,11 @@ def response_deserialization(
and response.default_content_type == "application/json"
else ""
)
response_attr = "json" if _json_serializable(str(response.default_content_type)) else "text"
deserialize_code.append("deserialized = _deserialize(")
response_attr = "json" if json_serializable(str(response.default_content_type)) else "text"
deserialize_func = "_deserialize"
if xml_serializable(str(response.default_content_type)):
deserialize_func = "_deserialize_xml"
deserialize_code.append(f"deserialized = {deserialize_func}(")
deserialize_code.append(
f" {response.type.type_annotation(is_operation_file=True)},{pylint_disable}"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ def declare_property(prop: Property) -> str:
elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
args.append(f'format="{prop.type.encode}"') # type: ignore

if prop.xml_metadata:
args.append(f"xml={prop.xml_metadata}")

field = "rest_discriminator" if prop.is_discriminator else "rest_field"
type_ignore = prop.is_discriminator and isinstance(prop.type, (ConstantType, EnumValue)) and prop.type.value
return (
Expand Down
Loading

0 comments on commit 10d8e5b

Please sign in to comment.