-
Notifications
You must be signed in to change notification settings - Fork 62
add customizations doc #1123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
add customizations doc #1123
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
556574a
add customizations doc
iscai-msft c33a8fa
fix typo
iscai-msft ddd3628
Update customizations.md
iscai-msft 8b76242
Update customizations.md
iscai-msft ddf3d91
Merge branch 'autorestv3' into add_quickstart
msyyc 7f1d132
modules -> subfolders
iscai-msft f54d90b
Merge branch 'autorestv3' into add_quickstart
iscai-msft 027a170
Update customizations.md
iscai-msft 6d1e116
remove patch_sdk doc
iscai-msft 6840fc1
fix descriptions of patch file in folder hierarchy
iscai-msft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,233 @@ | ||
| # Grow Up Story for Generated SDKs | ||
|
|
||
| This quickstart will introduce how to grow up your generated code with customizations. | ||
|
|
||
| **Note: This quickstart does not focus on renaming or removing generated objects**. If you'd like to rename or remove generated objects, the changes should really be made in the | ||
| OpenAPI definition of your service. If you absolutely can not modify the OpenAPI definition, you can use directives, specifically [these pre-defined directives](https://github.com/Azure/autorest/blob/main/docs/generate/built-in-directives.md) in your README configuration | ||
|
|
||
| ## Key Concept: _patch.py | ||
|
|
||
| The `_patch.py` files at each level of the subfolders will be the entry point to customize the generated code. | ||
|
|
||
| For example, if you want to override a model, you will use the `_patch.py` file at the `models` level of your | ||
| generated code to override. | ||
|
|
||
| The main flow of the `_patch.py` file will be: | ||
|
|
||
| 1. Import the generated object you wish to override. | ||
| 2. Inherit from the generated object, and override its behavior with your desired code functionality | ||
| 3. Include the name of your customized object in the `__all__` of the `_patch.py` file | ||
|
|
||
| To test that your behavior has been properly customized, please add tests for your customized code. | ||
| If you find that your customizations for an object are not being called, please make sure that | ||
| your customized object is included in the `__all__` of your `_patch.py` file. | ||
|
|
||
| The `_patch.py` file will never be removed during regeneration, so no worries about your customizations being | ||
| lost! | ||
|
|
||
| ## Examples | ||
|
|
||
| - [Change Model Behavior](#change-model-behavior) | ||
| - [Change Operation Behavior](#change-operation-behavior) | ||
| - [Overload an Operation](#overload-an-operation) | ||
| - [Change Client Behavior](#change-client-behavior) | ||
| - [Add a Client Method](#add-a-client-method) | ||
|
|
||
| ### Change Model Behavior | ||
|
|
||
| To override model behavior, you will work with the `_patch.py` file in the `models` folder of your generated code. | ||
|
|
||
| In the following example, we override the generated `Model`'s `input` parameter to accept both `str` and `datetime`, | ||
| instead of just `str`. | ||
|
|
||
| In this `_patch.py` file: | ||
|
|
||
| ``` | ||
| azure-sdk | ||
| │ README.md | ||
| │ | ||
| └───azure | ||
| └───sdk | ||
| └───models | ||
| │ _models.py # where the generated models are | ||
| | _patch.py # where we customize the models code | ||
| ``` | ||
|
|
||
| ```python | ||
| import datetime | ||
| from typing import Union | ||
| from ._models import Model as ModelGenerated | ||
|
|
||
| class Model(ModelGenerated): | ||
|
|
||
| def __init__(self, input: Union[str, datetime.datetime]): | ||
| super().__init__( | ||
| input=input.strftime("%d-%b-%Y") if isinstance(input, datetime.datetime) else input | ||
| ) | ||
|
|
||
| __all__ = ["Model"] | ||
| ``` | ||
|
|
||
| ### Change Operation Behavior | ||
|
|
||
| To change an operation, you will import the generated operation group the operation is on. Then you can inherit | ||
| from the generated operation group and modify the behavior of the operation. | ||
|
|
||
| In the following example, the generated operation takes in a datetime input, and returns a datetime response. | ||
| We want to also allow users to input strings, and return a string response if users inputted a string. | ||
|
|
||
| In this `_patch.py` file: | ||
|
|
||
| ``` | ||
| azure-sdk | ||
| │ README.md | ||
| │ | ||
| └───azure | ||
| └───sdk | ||
| └───operations | ||
| │ _operations.py # where the generated operations are | ||
| | _patch.py # where we customize the operations code | ||
| ``` | ||
|
|
||
| ```python | ||
| from typing import Union | ||
| import datetime | ||
| from ._operations import OperationGroup as OperationGroupGenerated | ||
|
|
||
| class OperationGroup(OperationGroupGenerated): | ||
|
|
||
| def operation(self, input: Union[str, datetime.datetime]): | ||
| response: datetime.datetime = super().operation( | ||
| datetime.datetime.strptime(input, '%b %d %Y') if isinstance(input, str) else input | ||
| ) | ||
| return response.strftime("%d-%b-%Y") if isinstance(input, str) else response | ||
|
|
||
|
|
||
| __all__ = ["OperationGroup"] | ||
| ``` | ||
|
|
||
| ### Overload an Operation | ||
|
|
||
| You can also easily overload generated operations. For example, if you want users to be able to pass in the body parameter | ||
| as a positional-only single dictionary, or as splatted keyword arguments, you can inherit and override the operation on the operation group | ||
| in the `_patch.py` file in the `operations` subfolders. | ||
|
|
||
| In this `_patch.py` file: | ||
|
|
||
| ``` | ||
| azure-sdk | ||
| │ README.md | ||
| │ | ||
| └───azure | ||
| └───sdk | ||
| └───operations | ||
| │ _operations.py # where the generated operations are | ||
| | _patch.py # where we customize the operations code | ||
| ``` | ||
|
|
||
| ```python | ||
| from typing import overload, Dict, Any | ||
| from ._operations import OperationGroup as OperationGroupGenerated | ||
|
|
||
| class OperationGroup(OperationGroupGenerated): | ||
|
|
||
| @overload | ||
| def operation(self, body: Dict[str, Any], /, **kwargs: Any): | ||
| """Pass in the body as a positional only parameter.""" | ||
|
|
||
| @overload | ||
| def operation(self, *, foo: str, bar: str, **kwargs: Any): | ||
| """Pass in the body as splatted keyword only arguments.""" | ||
|
|
||
| def operation(self, *args, **kwargs): | ||
| """Base operation for the two overloads""" | ||
| if not args: | ||
| args.append({"foo": kwargs.pop("foo"), "bar": kwargs.pop("bar")}) | ||
| return super().operation(*args, **kwargs) | ||
|
|
||
| __all__ = ["OperationGroup"] | ||
| ``` | ||
|
|
||
| ### Change Client Behavior | ||
|
|
||
| In this example, we change the default authentication policy for a client. | ||
|
|
||
| In this `_patch.py` file: | ||
|
|
||
| ``` | ||
| azure-sdk | ||
| │ README.md | ||
| │ | ||
| └───azure | ||
| └───sdk | ||
| │ _service_client.py # where the generated service client is | ||
| | _patch.py # where we customize the client code | ||
| └───operations | ||
| └───models | ||
| ``` | ||
|
|
||
| ```python | ||
| from ._service_client import ServiceClient as ServiceClientGenerated | ||
| from azure.core.pipeline import PipelineRequest | ||
| from azure.core.pipeline.policies import SansIOHTTPPolicy | ||
|
|
||
| class MyAuthenticationPolicy(SansIOHTTPPolicy): | ||
|
|
||
| def __init__(self, key: str): | ||
| self.key = key | ||
|
|
||
| def on_request(self, request: PipelineRequest): | ||
| request.http_request.headers["Authorization"] = f"My key is {self.key}" | ||
| return super().on_request(request) | ||
|
|
||
| class ServiceClient(ServiceClientGenerated): | ||
|
|
||
| def __init__(self, endpoint: str, credential: str, **kwargs): | ||
| super().__init__( | ||
| endpoint=endpoint, | ||
| credential=credential, | ||
| authentication_policy=kwargs.pop("authentication_policy", MyAuthenticationPolicy(credential)), | ||
| **kwargs | ||
| ) | ||
|
|
||
| __all__ = ["ServiceClient"] | ||
| ``` | ||
|
|
||
| ### Add a Client Method | ||
|
|
||
| Similar to models and operations, you can override client behavior in a `_patch.py` file, this time | ||
| at the root of the sdk. | ||
|
|
||
| Here, we will be adding an alternate form of authentication on the client, class method `from_connection_string`. | ||
|
|
||
| In this `_patch.py` file: | ||
|
|
||
| ``` | ||
| azure-sdk | ||
| │ README.md | ||
| │ | ||
| └───azure | ||
| └───sdk | ||
| │ _service_client.py # where the generated service client is | ||
| | _patch.py # where we customize the client code | ||
| └───operations | ||
| └───models | ||
| ``` | ||
|
|
||
| ```python | ||
| from typing import Any | ||
| from azure.core.credentials import AzureKeyCredential | ||
| from ._service_client import ServiceClient as ServiceClientGenerated | ||
|
|
||
| class ServiceClient(ServiceClientGenerated): | ||
|
|
||
| @classmethod | ||
| def from_connection_string(cls, connection_string: str, **kwargs: Any): | ||
| parsed_connection_string = _parse_connection_string(connection_string) # parsing function you've defined | ||
| return cls( | ||
| credential=AzureKeyCredential(parsed_connection_string.pop("accesskey")), | ||
| endpoint=parsed_connection_string.pop("endpoint") | ||
| ) | ||
|
|
||
| __all__ = ["ServiceClient"] | ||
| ``` | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add more doc reference at the end of doc for users ^^: https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: decided to not include in this doc, at least for now. The
how-to-patch-sdk-code.mddoc was really good when we didn't have our customizations design, but nowpatch_sdkis something that we really want users to treat as "everything else has failed, so let me last ditch usepatch_sdk. I've included some comment aboutpatch_sdkin the generated code, so I think it's best to leave the customizations doc with just common customization use cases for now