Skip to content

Commit

Permalink
[multipart] Support bytes[]/content type (#2380)
Browse files Browse the repository at this point in the history
  • Loading branch information
msyyc authored Feb 2, 2024
1 parent c656d3e commit 59f42d1
Show file tree
Hide file tree
Showing 329 changed files with 3,784 additions and 1,347 deletions.
17 changes: 17 additions & 0 deletions packages/autorest.python/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Release

## 2024-02-01 - 6.12.4

| Library | Min Version |
| ----------------------------------------------------------------------- | ----------- |
| `@autorest/core` | `3.9.2` |
| `@autorest/modelerfour` | `4.24.3` |
| `azure-core` dep of generated code | `1.30.0` |
| `isodate` dep of generated code | `0.6.1` |
| `msrest` dep of generated code (If generating legacy code) | `0.7.1` |
| `azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.3.2` |
| `typing-extensions` dep of generated code (If generating with constants)| `4.0.1` |

**Other Changes**

- Add support for multipart generation from typespec #2380
- Bump minimum dependency of `azure-core` to 1.30.0

## 2023-01-19 - 6.12.3

| Library | Min Version |
Expand Down
2 changes: 0 additions & 2 deletions packages/autorest.python/autorest/codegen/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
ParameterLocation,
BodyParameter,
ParameterDelimeter,
MultipartBodyParameter,
ClientParameter,
ConfigParameter,
)
Expand Down Expand Up @@ -115,7 +114,6 @@
"BodyParameter",
"RequestBuilderBodyParameter",
"ParameterDelimeter",
"MultipartBodyParameter",
"CredentialType",
"ClientParameter",
"ConfigParameter",
Expand Down
4 changes: 4 additions & 0 deletions packages/autorest.python/autorest/codegen/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,7 @@ def serialization_constraints(self) -> List[str]:
@property
def type_description(self) -> str:
return self.type_annotation()

@property
def is_form_data(self) -> bool:
return False
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ def type_definition(self, **kwargs: Any) -> str:
pattern = re.compile(r"Union\[.*\]")
return f'Union[{", ".join(map(lambda x: x[6: -1] if pattern.match(x) else x, inside_types))}]'

@property
def is_form_data(self) -> bool:
return any(t.is_form_data for t in self.types)

def get_json_template_representation(
self,
*,
Expand Down
13 changes: 13 additions & 0 deletions packages/autorest.python/autorest/codegen/models/model_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def __init__(
self.snake_case_name: str = self.yaml_data["snakeCaseName"]
self.page_result_model: bool = self.yaml_data.get("pageResultModel", False)

@property
def is_form_data(self) -> bool:
return any(p.is_multipart_file_input for p in self.properties)

@property
def is_xml(self) -> bool:
return self.yaml_data.get("isXml", False)
Expand Down Expand Up @@ -314,6 +318,15 @@ def imports(self, **kwargs: Any) -> FileImport:
if kwargs.get("model_typing")
else TypingSection.REGULAR,
)
if self.is_form_data:
file_import.add_submodule_import(
relative_path,
"_model_base",
ImportType.LOCAL,
typing_section=TypingSection.TYPING
if kwargs.get("model_typing")
else TypingSection.REGULAR,
)
return file_import


Expand Down
24 changes: 1 addition & 23 deletions packages/autorest.python/autorest/codegen/models/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
)
from .parameter import (
BodyParameter,
MultipartBodyParameter,
Parameter,
ParameterLocation,
)
Expand Down Expand Up @@ -290,7 +289,6 @@ def has_kwargs_to_pop_with_default(
Parameter,
RequestBuilderParameter,
BodyParameter,
MultipartBodyParameter,
]
],
location: ParameterLocation,
Expand Down Expand Up @@ -575,33 +573,13 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
relative_path = "..." if async_mode else ".."
if self.code_model.options["models_mode"] == "dpg":
if self.parameters.has_body:
if not self.parameters.body_parameter.is_form_data:
if not self.has_form_data_body:
file_import.add_submodule_import(
f"{relative_path}_model_base",
"SdkJSONEncoder",
ImportType.LOCAL,
)
file_import.add_import("json", ImportType.STDLIB)
else:
file_import.add_submodule_import(
relative_path, "_model_base", ImportType.LOCAL
)
file_import.add_submodule_import("io", "IOBase", ImportType.STDLIB)
file_import.add_submodule_import(
f"{relative_path}_vendor",
"multipart_file",
ImportType.LOCAL,
)
file_import.add_submodule_import(
f"{relative_path}_vendor",
"multipart_data",
ImportType.LOCAL,
)
file_import.add_submodule_import(
f"{relative_path}_vendor",
"handle_multipart_form_data_model",
ImportType.LOCAL,
)
if self.default_error_deserialization or any(
r.type for r in self.responses
):
Expand Down
77 changes: 26 additions & 51 deletions packages/autorest.python/autorest/codegen/models/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
Optional,
TypeVar,
Union,
Generic,
)

from .imports import FileImport, ImportType, TypingSection
Expand Down Expand Up @@ -243,9 +242,18 @@ def method_signature(self, async_mode: bool) -> str:
class BodyParameter(_ParameterBase):
"""Body parameter."""

@property
def entries(self) -> List["BodyParameter"]:
return [
BodyParameter.from_yaml(e, self.code_model)
for e in self.yaml_data.get("entries", [])
]

@property
def is_form_data(self) -> bool:
return self.default_content_type == "multipart/form-data"
# hacky, but rn in legacy, there is no formdata model type, it's just a dict
# with all of the entries splatted out
return self.type.is_form_data or bool(self.entries)

@property
def is_partial_body(self) -> bool:
Expand All @@ -262,6 +270,10 @@ def method_location(self) -> ParameterMethodLocation:

@property
def in_method_signature(self) -> bool:
if self.yaml_data.get("entries"):
# Right now, only legacy generates with multipart bodies and entries
# and legacy generates with the multipart body arguments splatted out
return False
return not (self.flattened or self.grouped_by)

@property
Expand All @@ -278,6 +290,18 @@ def has_json_model_type(self) -> bool:
return self.type.target_model_subtype((JSONModelType,)) is not None
return isinstance(self.type, JSONModelType)

def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
file_import = super().imports(async_mode, **kwargs)
if self.is_form_data:
relative_path = "..." if async_mode else ".."
file_import.add_submodule_import(
f"{relative_path}_vendor",
"prepare_multipart_form_data",
ImportType.LOCAL,
)
file_import.add_submodule_import("typing", "List", ImportType.STDLIB)
return file_import

@classmethod
def from_yaml(
cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
Expand All @@ -294,46 +318,6 @@ def from_yaml(
)


class _MultipartBodyParameter(Generic[EntryBodyParameterType], BodyParameter):
"""Base class for MultipartBodyParameter and RequestBuilderMultipartBodyParameter"""

def __init__(
self,
yaml_data: Dict[str, Any],
code_model: "CodeModel",
type: BaseType,
entries: List[EntryBodyParameterType],
) -> None:
super().__init__(yaml_data, code_model, type)
self.entries = entries

@property
def in_method_signature(self) -> bool:
# Right now, only legacy generates with multipart bodies
# and legacy generates with the multipart body arguments splatted out
return False


class MultipartBodyParameter(
_MultipartBodyParameter[BodyParameter] # pylint: disable=unsubscriptable-object
):
"""Multipart body parameter for Operation. Used for files and data input."""

@classmethod
def from_yaml(
cls, yaml_data: Dict[str, Any], code_model: "CodeModel"
) -> "MultipartBodyParameter":
return cls(
yaml_data=yaml_data,
code_model=code_model,
type=code_model.lookup_type(id(yaml_data["type"])),
entries=[
BodyParameter.from_yaml(entry, code_model)
for entry in yaml_data["entries"]
],
)


class Parameter(_ParameterBase):
"""Basic Parameter class"""

Expand Down Expand Up @@ -455,12 +439,3 @@ def method_location(self) -> ParameterMethodLocation:
if self.constant:
return ParameterMethodLocation.KWARG
return ParameterMethodLocation.POSITIONAL


def get_body_parameter(
yaml_data: Dict[str, Any], code_model: "CodeModel"
) -> Union[BodyParameter, MultipartBodyParameter]:
"""Creates a regular body parameter or Multipart body parameter"""
if yaml_data.get("entries"):
return MultipartBodyParameter.from_yaml(yaml_data, code_model)
return BodyParameter.from_yaml(yaml_data, code_model)
28 changes: 9 additions & 19 deletions packages/autorest.python/autorest/codegen/models/parameter_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,15 @@

from .request_builder_parameter import (
RequestBuilderBodyParameter,
RequestBuilderMultipartBodyParameter,
RequestBuilderParameter,
get_request_body_parameter,
)
from .parameter import (
MultipartBodyParameter,
ParameterLocation,
BodyParameter,
Parameter,
ParameterMethodLocation,
ClientParameter,
ConfigParameter,
get_body_parameter,
)

ParameterType = TypeVar(
Expand All @@ -43,10 +39,6 @@
BodyParameterType = TypeVar(
"BodyParameterType", bound=Union[BodyParameter, RequestBuilderBodyParameter]
)
RequestBuilderBodyParameterType = Union[
RequestBuilderBodyParameter, RequestBuilderMultipartBodyParameter
]


if TYPE_CHECKING:
from .code_model import CodeModel
Expand Down Expand Up @@ -314,7 +306,7 @@ def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel"):

class _ParameterList(
_ParameterListBase[ # pylint: disable=unsubscriptable-object
Parameter, Union[MultipartBodyParameter, BodyParameter]
Parameter, BodyParameter
]
):
"""Base Parameter class for the two operation ParameterLists"""
Expand All @@ -325,11 +317,9 @@ def parameter_creator() -> Callable[[Dict[str, Any], "CodeModel"], Parameter]:

@staticmethod
def body_parameter_creator() -> (
Callable[
[Dict[str, Any], "CodeModel"], Union[MultipartBodyParameter, BodyParameter]
]
Callable[[Dict[str, Any], "CodeModel"], BodyParameter]
):
return get_body_parameter
return BodyParameter.from_yaml

@property
def implementation(self) -> str:
Expand All @@ -348,7 +338,7 @@ class ParameterList(_ParameterList):

class _RequestBuilderParameterList(
_ParameterListBase[ # pylint: disable=unsubscriptable-object
RequestBuilderParameter, RequestBuilderBodyParameterType
RequestBuilderParameter, RequestBuilderBodyParameter
]
):
"""_RequestBuilderParameterList is base parameter list for RequestBuilder classes"""
Expand All @@ -361,9 +351,9 @@ def parameter_creator() -> (

@staticmethod
def body_parameter_creator() -> (
Callable[[Dict[str, Any], "CodeModel"], RequestBuilderBodyParameterType]
Callable[[Dict[str, Any], "CodeModel"], RequestBuilderBodyParameter]
):
return get_request_body_parameter
return RequestBuilderBodyParameter.from_yaml

@property
def implementation(self) -> str:
Expand All @@ -372,14 +362,14 @@ def implementation(self) -> str:
@property
def unsorted_method_params(
self,
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameterType]]:
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameter]]:
# don't have access to client params in request builder
retval = [
p
for p in super().unsorted_method_params
if not (
p.location == ParameterLocation.BODY
and cast(RequestBuilderBodyParameterType, p).is_partial_body
and cast(RequestBuilderBodyParameter, p).is_partial_body
)
]
retval.extend(
Expand All @@ -400,7 +390,7 @@ def path(self) -> List[RequestBuilderParameter]:
@property
def constant(
self,
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameterType]]:
) -> List[Union[RequestBuilderParameter, RequestBuilderBodyParameter]]:
"""All constant parameters"""
return [
p for p in super().constant if p.location != ParameterLocation.ENDPOINT_PATH
Expand Down
Loading

0 comments on commit 59f42d1

Please sign in to comment.