Skip to content

Instantly share code, notes, and snippets.

@msyyc
Last active December 13, 2023 01:58
Show Gist options
  • Select an option

  • Save msyyc/5d2e9359330337b682ae1e921921c587 to your computer and use it in GitHub Desktop.

Select an option

Save msyyc/5d2e9359330337b682ae1e921921c587 to your computer and use it in GitHub Desktop.
"multipart/form-data" design for Python SDK (Only for DPG model)

Typespec

For the following Typespec definition about "multipart/form-data":

...
model MultiPartRequest {
  id: string;
  profileImage: bytes;
}

op basic(@header contentType: "multipart/form-data", @body body: MultiPartRequest): NoContentResponse;

Expected Python SDK API

Compared with content-type, the only difference is that we don't generate overload for IO since core can't handle IO which may contain IO, too.

    @overload
    def basic(self, body: JSON, **kwargs: Any) -> None:
      ...

    @overload
    def basic(self, body: _models.MultiPartRequest, **kwargs: Any) -> None:
      ...
  
    @distributed_trace
    def basic(self, body: Union[_models.MultiPartRequest, JSON], **kwargs: Any) -> None:
      ...

Users could use SDK with the following ways:

...
with open("image.png", "rb") as file:
    clent.basic({"id": "123", "profileImage": file})
    # or
    # clent.basic({"id": "123", "profileImage": file.read()})

Implementation

  
    def multipart_form_data_file(file: Union[IOBase, bytes]) -> Union[IOBase, Tuple[str, bytes, str]]:
        if isinstance(file, IOBase):
            return file
        return (str(time.time()), file, "application/octet-stream")
    
    def basic(self, body: Union[_models.MultiPartRequest, JSON], **kwargs: Any) -> None:
        ...
        _files = {k: multipart_form_data_file(v) for k, v in body.items() if isinstance(v, (IOBase, bytes))}
        _data = {k: v for k, v in body.items() if not isinstance(v, (IOBase, bytes))}
        _request = build_multi_part_basic_request(
            data=_data,
            files=_files,
            headers=_headers,
            params=_params,
        )
        ...

nit: I make a test locally and it passes with up logic and we don't need change for current azure-core version.

@johanste
Copy link

Why use time.time() as the (file) name? There is a very high probability of name conflicts.

@msyyc
Copy link
Author

msyyc commented Dec 13, 2023

@johanste The doc is mainly to show API and rough structure of implement, so I don't make too much thought on details (e.g time.time() as file name). If you think the API and implement flow are expected, I will begin the work in code generator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment