Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1af685a
initial reordering to pass metadata to code model
iscai-msft Aug 6, 2020
fb6cfca
introduce CodeModel model
iscai-msft Aug 6, 2020
95d1ac4
change some values to properties of the MultiAPI class
iscai-msft Aug 7, 2020
1de62dc
add client model
iscai-msft Aug 12, 2020
2407898
add config model
iscai-msft Aug 12, 2020
a567662
add operation group model
iscai-msft Aug 13, 2020
17ba92d
add operation mixin group
iscai-msft Aug 13, 2020
354fea5
add mixin operation
iscai-msft Aug 13, 2020
b9575b9
sort operations and operaiton groups by name
iscai-msft Aug 18, 2020
2d65d35
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python…
iscai-msft Aug 18, 2020
3c78add
move _get_default_api_version_from_list to utils
iscai-msft Aug 18, 2020
f73d119
move last_rt_list to code model
iscai-msft Aug 18, 2020
4bcb3da
trim operations from main init
iscai-msft Aug 18, 2020
f452b09
add global parameters model
iscai-msft Aug 18, 2020
c081016
add final necessary properties from multiapi init to code model
iscai-msft Aug 19, 2020
6d43a9a
switch serializer to serializer init file, remove conf
iscai-msft Aug 19, 2020
082b953
change templates to accept code_model
iscai-msft Aug 19, 2020
32bbd38
fix mypy and pylint
iscai-msft Aug 19, 2020
25aa848
fix removal of _extract_version from utils to client model file
iscai-msft Aug 24, 2020
bac6b3e
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python…
iscai-msft Sep 15, 2020
9c283c9
differentiate operation group class name by api version
iscai-msft Sep 17, 2020
5d59d67
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python…
iscai-msft Sep 17, 2020
aaa830f
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python…
iscai-msft Sep 24, 2020
8b7608e
get rid of unnecessary class inheritance from object
iscai-msft Sep 24, 2020
83b84f1
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python…
iscai-msft Sep 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions autorest/codegen/templates/metadata.json.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@
"client_side_validation": {{ code_model.options["client_side_validation"] | tojson }}
},
"global_parameters": {
"sync_method": {
"sync": {
{% for gp in sync_global_parameters.method %}
{{ gp.serialized_name | tojson }}: {
"method_signature": {{ gp.sync_method_signature | tojson }},
"signature": {{ gp.sync_method_signature | tojson }},
"description": {{ gp.description | tojson }},
"docstring_type": {{ gp.docstring_type | tojson }},
"required": {{ gp.required | tojson }}
}{{ "," if not loop.last else "" }}
{% endfor %}
},
"async_method": {
"async": {
{% for gp in async_global_parameters.method %}
{{ gp.serialized_name | tojson }}: {
"method_signature": {{ gp.sync_method_signature | tojson }},
"signature": {{ gp.sync_method_signature | tojson }},
"description": {{ gp.description | tojson }},
"docstring_type": {{ gp.docstring_type | tojson }},
"required": {{ gp.required | tojson }}
Expand Down
458 changes: 78 additions & 380 deletions autorest/multiapi/__init__.py

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions autorest/multiapi/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

from .code_model import CodeModel
from .imports import ImportType, FileImport, TypingSection

__all__ = [
"ImportType",
"CodeModel",
"FileImport",
"TypingSection"
"ImportType",
"TypingSection",
]
55 changes: 55 additions & 0 deletions autorest/multiapi/models/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import sys
from typing import Any, Dict, List
from pathlib import Path

def _extract_version(metadata_json: Dict[str, Any], version_path: Path) -> str:
version = metadata_json['chosen_version']
total_api_version_list = metadata_json['total_api_version_list']
if not version:
if total_api_version_list:
sys.exit(
f"Unable to match {total_api_version_list} to label {version_path.stem}"
)
else:
sys.exit(
f"Unable to extract api version of {version_path.stem}"
)
return version

class Client:
def __init__(
self,
azure_arm: bool,
default_version_metadata: Dict[str, Any],
version_path_to_metadata: Dict[Path, Dict[str, Any]]
):
self.name = default_version_metadata["client"]["name"]
self.pipeline_client = "ARMPipelineClient" if azure_arm else "PipelineClient"
self.filename = default_version_metadata["client"]["filename"]
self.base_url = default_version_metadata["client"]["base_url"]
self.description = default_version_metadata["client"]["description"]
self.client_side_validation = default_version_metadata["client"]["client_side_validation"]
self.version_path_to_metadata = version_path_to_metadata

@property
def custom_base_url_to_api_version(self) -> Dict[str, List[str]]:
custom_base_url_to_api_version: Dict[str, List[str]] = {}
for version_path, metadata_json in self.version_path_to_metadata.items():
custom_base_url = metadata_json["client"]["custom_base_url"]
version = _extract_version(metadata_json, version_path)
custom_base_url_to_api_version.setdefault(custom_base_url, []).append(version)
return custom_base_url_to_api_version

@property
def has_lro_operations(self) -> bool:
has_lro_operations = False
for _, metadata_json in self.version_path_to_metadata.items():
current_client_has_lro_operations = metadata_json["client"]["has_lro_operations"]
if current_client_has_lro_operations:
has_lro_operations = True
return has_lro_operations
131 changes: 131 additions & 0 deletions autorest/multiapi/models/code_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

from typing import Any, Dict, List, Optional
from pathlib import Path
from .client import Client
from .config import Config
from .operation_group import OperationGroup
from .operation_mixin_group import OperationMixinGroup
from .global_parameters import GlobalParameters
from ..utils import _get_default_api_version_from_list

class CodeModel: # pylint: disable=too-many-instance-attributes
def __init__(
self,
module_name: str,
package_name: str,
default_api_version: str,
preview_mode: bool,
default_version_metadata: Dict[str, Any],
mod_to_api_version: Dict[str, str],
version_path_to_metadata: Dict[Path, Dict[str, Any]],
user_specified_default_api: Optional[str] = None
):
self.module_name = module_name
self.package_name = package_name
self.mod_to_api_version = mod_to_api_version
self.default_api_version = default_api_version
self.preview_mode = preview_mode
self.azure_arm = default_version_metadata["client"]["azure_arm"]
self.default_version_metadata = default_version_metadata
self.version_path_to_metadata = version_path_to_metadata
self.service_client = Client(self.azure_arm, default_version_metadata, version_path_to_metadata)
self.config = Config(default_version_metadata)
self.operation_mixin_group = OperationMixinGroup(version_path_to_metadata, default_api_version)
self.global_parameters = GlobalParameters(default_version_metadata["global_parameters"])
self.user_specified_default_api = user_specified_default_api

@property
def operation_groups(self) -> List[OperationGroup]:
operation_groups: List[OperationGroup] = []
for version_path, metadata_json in self.version_path_to_metadata.items():
if not metadata_json.get('operation_groups'):
continue
operation_groups_metadata = metadata_json['operation_groups']
for operation_group_name, operation_group_class_name in operation_groups_metadata.items():
try:
operation_group = [og for og in operation_groups if og.name == operation_group_name][0]
except IndexError:
operation_group = OperationGroup(operation_group_name)
operation_groups.append(operation_group)
operation_group.append_available_api(version_path.name)
operation_group.append_api_class_name_pair(version_path.name, operation_group_class_name)
operation_groups.sort(key=lambda x: x.name)
return operation_groups

@property
def last_rt_list(self) -> Dict[str, str]:
"""Build the a mapping RT => API version if RT doesn't exist in latest detected API version.

Example:
last_rt_list = {
'check_dns_name_availability': '2018-05-01'
}

There is one subtle scenario if PREVIEW mode is disabled:
- RT1 available on 2019-05-01 and 2019-06-01-preview
- RT2 available on 2019-06-01-preview
- RT3 available on 2019-07-01-preview

Then, if I put "RT2: 2019-06-01-preview" in the list, this means I have to make
"2019-06-01-preview" the default for models loading (otherwise "RT2: 2019-06-01-preview" won't work).
But this likely breaks RT1 default operations at "2019-05-01", with default models at "2019-06-01-preview"
since "models" are shared for the entire set of operations groups (I wished models would be split by
operation groups, but meh, that's not the case)

So, until we have a smarter Autorest to deal with that, only preview RTs which do not share models with
a stable RT can be added to this map. In this case, RT2 is out, RT3 is in.
"""

def there_is_a_rt_that_contains_api_version(rt_dict, api_version):
"Test in the given api_version is is one of those RT."
for rt_api_version in rt_dict.values():
if api_version in rt_api_version:
return True
return False

last_rt_list = {}

# First let's map operation groups to their available APIs
versioned_dict = {
operation_group.name: operation_group.available_apis
for operation_group in self.operation_groups
}

# Now let's also include mixins to their available APIs
versioned_dict.update(
{
mixin_operation.name: mixin_operation.available_apis
for mixin_operation in self.operation_mixin_group.mixin_operations
}
)
for operation, api_versions_list in versioned_dict.items():
local_default_api_version = _get_default_api_version_from_list(
self.mod_to_api_version,
api_versions_list,
self.preview_mode,
self.user_specified_default_api
)
if local_default_api_version == self.default_api_version:
continue
# If some others RT contains "local_default_api_version", and
# if it's greater than the future default, danger, don't profile it
if (
there_is_a_rt_that_contains_api_version(
versioned_dict, local_default_api_version
)
and local_default_api_version > self.default_api_version
):
continue
last_rt_list[operation] = local_default_api_version
return last_rt_list

@property
def default_models(self):
return sorted(
{self.default_api_version} | {versions for _, versions in self.last_rt_list.items()}
)
16 changes: 16 additions & 0 deletions autorest/multiapi/models/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict

class Config:
def __init__(self, default_version_metadata: Dict[str, Any]):
self.credential = default_version_metadata["config"]["credential"]
self.credential_scopes = default_version_metadata["config"]["credential_scopes"]
self.credential_default_policy_type = default_version_metadata["config"]["credential_default_policy_type"]
self.credential_default_policy_type_has_async_version = (
default_version_metadata["config"]["credential_default_policy_type_has_async_version"]
)
self.credential_key_header_name = default_version_metadata["config"]["credential_key_header_name"]
10 changes: 10 additions & 0 deletions autorest/multiapi/models/constant_global_parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

class ConstantGlobalParameter:
def __init__(self, name: str, value: str):
self.name = name
self.value = value
32 changes: 32 additions & 0 deletions autorest/multiapi/models/global_parameter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict

class GlobalParameter:
def __init__(
self,
name: str,
global_parameter_metadata_sync: Dict[str, Any],
global_parameter_metadata_async: Dict[str, Any]
):
self.name = name
self.global_parameter_metadata_sync = global_parameter_metadata_sync
self.global_parameter_metadata_async = global_parameter_metadata_async
self.required = global_parameter_metadata_sync["required"]

def _global_parameter_metadata(self, async_mode: bool) -> Dict[str, Any]:
if async_mode:
return self.global_parameter_metadata_async
return self.global_parameter_metadata_sync

def signature(self, async_mode: bool) -> str:
return self._global_parameter_metadata(async_mode)["signature"]

def description(self, async_mode: bool) -> str:
return self._global_parameter_metadata(async_mode)["description"]

def docstring_type(self, async_mode: bool) -> str:
return self._global_parameter_metadata(async_mode)["docstring_type"]
34 changes: 34 additions & 0 deletions autorest/multiapi/models/global_parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict, List
from .global_parameter import GlobalParameter
from .constant_global_parameter import ConstantGlobalParameter

class GlobalParameters:
def __init__(self, global_parameters_metadata: Dict[str, Any]):
self.call = global_parameters_metadata["call"]
self.global_parameters_metadata = global_parameters_metadata

@property
def parameters(self) -> List[GlobalParameter]:
global_parameters_metadata_sync = self.global_parameters_metadata["sync"]
global_parameters_metadata_async = self.global_parameters_metadata["async"]

return [
GlobalParameter(
name=parameter_name,
global_parameter_metadata_sync=gp_sync,
global_parameter_metadata_async=global_parameters_metadata_async[parameter_name]
)
for parameter_name, gp_sync in global_parameters_metadata_sync.items()
]

@property
def constant_parameters(self) -> List[ConstantGlobalParameter]:
return [
ConstantGlobalParameter(constant_name, constant_value)
for constant_name, constant_value in self.global_parameters_metadata["constant"].items()
]
38 changes: 38 additions & 0 deletions autorest/multiapi/models/mixin_operation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict, List, TypeVar
from ..utils import _sync_or_async

T = TypeVar('T')
OrderedSet = Dict[T, None]

class MixinOperation:
def __init__(self, name: str, mixin_operation_metadata: Dict[str, Any]):
self.name = name
self.mixin_operation_metadata = mixin_operation_metadata
self._available_apis: OrderedSet[str] = {}

def signature(self, async_mode: bool) -> str:
return self.mixin_operation_metadata[_sync_or_async(async_mode)]["signature"]

def description(self, async_mode: bool) -> str:
return self.mixin_operation_metadata[_sync_or_async(async_mode)]["doc"]

def coroutine(self, async_mode: bool) -> bool:
if not async_mode:
return False
return self.mixin_operation_metadata["async"]["coroutine"]

@property
def call(self) -> str:
return self.mixin_operation_metadata["call"]

@property
def available_apis(self) -> List[str]:
return list(self._available_apis.keys())

def append_available_api(self, val: str) -> None:
self._available_apis[val] = None
28 changes: 28 additions & 0 deletions autorest/multiapi/models/operation_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Dict, List, TypeVar

T = TypeVar('T')
OrderedSet = Dict[T, None]

class OperationGroup:
def __init__(self, name: str):
self.name = name
self._available_apis: OrderedSet[str] = {}
self._api_to_class_name: Dict[str, str] = {}

@property
def available_apis(self) -> List[str]:
return list(self._available_apis.keys())

def append_available_api(self, val: str) -> None:
self._available_apis[val] = None

def append_api_class_name_pair(self, api_version: str, class_name: str):
self._api_to_class_name[api_version] = class_name

def class_name(self, api_version: str):
return self._api_to_class_name[api_version]
Loading