@typespec/http-client-python 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +85 -24
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/lib.d.ts +1 -0
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +1 -0
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/run-python3.d.ts +2 -0
- package/dist/emitter/run-python3.d.ts.map +1 -0
- package/dist/emitter/run-python3.js +19 -0
- package/dist/emitter/run-python3.js.map +1 -0
- package/dist/emitter/system-requirements.d.ts +17 -0
- package/dist/emitter/system-requirements.d.ts.map +1 -0
- package/dist/emitter/system-requirements.js +167 -0
- package/dist/emitter/system-requirements.js.map +1 -0
- package/emitter/src/emitter.ts +88 -23
- package/emitter/src/lib.ts +2 -0
- package/emitter/src/run-python3.ts +20 -0
- package/emitter/src/system-requirements.ts +261 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/ci/regenerate.ts +16 -4
- package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
- package/eng/scripts/setup/build.ts +16 -0
- package/eng/scripts/setup/build_pygen_wheel.py +40 -0
- package/eng/scripts/setup/install.py +9 -3
- package/eng/scripts/setup/install.ts +32 -0
- package/eng/scripts/setup/prepare.py +3 -1
- package/eng/scripts/setup/prepare.ts +11 -0
- package/eng/scripts/setup/run-python3.ts +1 -6
- package/generator/build/lib/pygen/__init__.py +107 -0
- package/generator/build/lib/pygen/_version.py +7 -0
- package/generator/build/lib/pygen/black.py +71 -0
- package/generator/build/lib/pygen/codegen/__init__.py +357 -0
- package/generator/build/lib/pygen/codegen/_utils.py +17 -0
- package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
- package/generator/build/lib/pygen/codegen/models/base.py +186 -0
- package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
- package/generator/build/lib/pygen/codegen/models/client.py +435 -0
- package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
- package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
- package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
- package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
- package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
- package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
- package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
- package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
- package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
- package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
- package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
- package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
- package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
- package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
- package/generator/build/lib/pygen/codegen/models/property.py +170 -0
- package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
- package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
- package/generator/build/lib/pygen/codegen/models/response.py +348 -0
- package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
- package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
- package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
- package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
- package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
- package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
- package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
- package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
- package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
- package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
- package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
- package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
- package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
- package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
- package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
- package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
- package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
- package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
- package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
- package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
- package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
- package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
- package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
- package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
- package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
- package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
- package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
- package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
- package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
- package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
- package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
- package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
- package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
- package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
- package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
- package/generator/build/lib/pygen/m2r.py +65 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
- package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
- package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
- package/generator/build/lib/pygen/utils.py +163 -0
- package/generator/component-detection-pip-report.json +134 -0
- package/generator/dev_requirements.txt +0 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/m2r.py +1 -1
- package/generator/pygen.egg-info/PKG-INFO +7 -4
- package/generator/pygen.egg-info/requires.txt +7 -4
- package/generator/requirements.txt +5 -10
- package/generator/setup.py +7 -4
- package/generator/test/azure/requirements.txt +2 -0
- package/generator/test/generic_mock_api_tests/unittests/test_m2r.py +10 -0
- package/generator/test/unbranded/requirements.txt +2 -0
- package/package.json +6 -5
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
from typing import Any, Dict, TYPE_CHECKING, TypeVar, Generic, Union, List, Optional
|
|
7
|
+
|
|
8
|
+
from .base import BaseModel
|
|
9
|
+
from .parameter_list import ClientGlobalParameterList, ConfigGlobalParameterList
|
|
10
|
+
from .imports import FileImport, ImportType, TypingSection, MsrestImportType
|
|
11
|
+
from .utils import add_to_pylint_disable
|
|
12
|
+
from .operation_group import OperationGroup
|
|
13
|
+
from .request_builder import (
|
|
14
|
+
RequestBuilder,
|
|
15
|
+
OverloadedRequestBuilder,
|
|
16
|
+
get_request_builder,
|
|
17
|
+
)
|
|
18
|
+
from .parameter import Parameter, ParameterMethodLocation
|
|
19
|
+
from .lro_operation import LROOperation
|
|
20
|
+
from .lro_paging_operation import LROPagingOperation
|
|
21
|
+
from ...utils import extract_original_name, NAME_LENGTH_LIMIT
|
|
22
|
+
|
|
23
|
+
ParameterListType = TypeVar(
|
|
24
|
+
"ParameterListType",
|
|
25
|
+
bound=Union[ClientGlobalParameterList, ConfigGlobalParameterList],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from .code_model import CodeModel
|
|
30
|
+
from . import OperationType
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _ClientConfigBase(Generic[ParameterListType], BaseModel):
|
|
34
|
+
"""The service client base. Shared across our Client and Config type"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
yaml_data: Dict[str, Any],
|
|
39
|
+
code_model: "CodeModel",
|
|
40
|
+
parameters: ParameterListType,
|
|
41
|
+
):
|
|
42
|
+
super().__init__(yaml_data, code_model)
|
|
43
|
+
self.parameters = parameters
|
|
44
|
+
self.url: str = self.yaml_data["url"] # the base endpoint of the client. Can be parameterized or not
|
|
45
|
+
self.legacy_filename: str = self.yaml_data.get("legacyFilename", "client")
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def description(self) -> str:
|
|
49
|
+
return self.yaml_data["description"]
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def name(self) -> str:
|
|
53
|
+
return self.yaml_data["name"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Client(_ClientConfigBase[ClientGlobalParameterList]):
|
|
57
|
+
"""Model representing our service client"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
yaml_data: Dict[str, Any],
|
|
62
|
+
code_model: "CodeModel",
|
|
63
|
+
parameters: ClientGlobalParameterList,
|
|
64
|
+
*,
|
|
65
|
+
is_subclient: bool = False,
|
|
66
|
+
):
|
|
67
|
+
super().__init__(yaml_data, code_model, parameters)
|
|
68
|
+
self.operation_groups: List[OperationGroup] = []
|
|
69
|
+
self.config = Config.from_yaml(yaml_data, self.code_model)
|
|
70
|
+
self.is_subclient = is_subclient
|
|
71
|
+
self.request_builders = self._build_request_builders()
|
|
72
|
+
if self.code_model.options["show_operations"]:
|
|
73
|
+
self.operation_groups = [
|
|
74
|
+
OperationGroup.from_yaml(op_group, code_model, self)
|
|
75
|
+
for op_group in self.yaml_data.get("operationGroups", [])
|
|
76
|
+
]
|
|
77
|
+
self.link_lro_initial_operations()
|
|
78
|
+
self.request_id_header_name = self.yaml_data.get("requestIdHeaderName", None)
|
|
79
|
+
self.has_etag: bool = yaml_data.get("hasEtag", False)
|
|
80
|
+
|
|
81
|
+
def _build_request_builders(
|
|
82
|
+
self,
|
|
83
|
+
) -> List[Union[RequestBuilder, OverloadedRequestBuilder]]:
|
|
84
|
+
request_builders: List[Union[RequestBuilder, OverloadedRequestBuilder]] = []
|
|
85
|
+
|
|
86
|
+
def add_og_request_builder(og: Dict[str, Any]):
|
|
87
|
+
for operation_yaml in og["operations"]:
|
|
88
|
+
request_builder = get_request_builder(
|
|
89
|
+
operation_yaml,
|
|
90
|
+
code_model=self.code_model,
|
|
91
|
+
client=self,
|
|
92
|
+
)
|
|
93
|
+
if operation_yaml.get("isLroInitialOperation"):
|
|
94
|
+
# we want to change the name
|
|
95
|
+
request_builder.name = request_builder.get_name(
|
|
96
|
+
extract_original_name(request_builder.yaml_data["name"]),
|
|
97
|
+
request_builder.yaml_data,
|
|
98
|
+
request_builder.code_model,
|
|
99
|
+
request_builder.client,
|
|
100
|
+
)
|
|
101
|
+
if request_builder.overloads:
|
|
102
|
+
request_builders.extend(request_builder.overloads)
|
|
103
|
+
request_builders.append(request_builder)
|
|
104
|
+
if operation_yaml.get("nextOperation"):
|
|
105
|
+
# i am a paging operation and i have a next operation.
|
|
106
|
+
# Make sure to include my next operation
|
|
107
|
+
request_builders.append(
|
|
108
|
+
get_request_builder(
|
|
109
|
+
operation_yaml["nextOperation"],
|
|
110
|
+
code_model=self.code_model,
|
|
111
|
+
client=self,
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
queue = self.yaml_data.get("operationGroups", []).copy()
|
|
116
|
+
while queue:
|
|
117
|
+
now = queue.pop(0)
|
|
118
|
+
add_og_request_builder(now)
|
|
119
|
+
if now.get("operationGroups"):
|
|
120
|
+
queue.extend(now["operationGroups"])
|
|
121
|
+
|
|
122
|
+
return request_builders
|
|
123
|
+
|
|
124
|
+
def pipeline_class(self, async_mode: bool) -> str:
|
|
125
|
+
if self.code_model.options["azure_arm"]:
|
|
126
|
+
if async_mode:
|
|
127
|
+
return "AsyncARMPipelineClient"
|
|
128
|
+
return "ARMPipelineClient"
|
|
129
|
+
if async_mode:
|
|
130
|
+
return "AsyncPipelineClient"
|
|
131
|
+
return "PipelineClient"
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def credential(self) -> Optional[Parameter]:
|
|
135
|
+
"""The credential param, if one exists"""
|
|
136
|
+
return self.parameters.credential
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def send_request_name(self) -> str:
|
|
140
|
+
"""Name of the send request function"""
|
|
141
|
+
return "send_request" if self.code_model.options["show_send_request"] else "_send_request"
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def has_parameterized_host(self) -> bool:
|
|
145
|
+
"""Whether the base url is parameterized or not"""
|
|
146
|
+
return not any(p for p in self.parameters if p.is_host)
|
|
147
|
+
|
|
148
|
+
def pylint_disable(self) -> str:
|
|
149
|
+
retval = ""
|
|
150
|
+
if not any(
|
|
151
|
+
p
|
|
152
|
+
for p in self.parameters.parameters
|
|
153
|
+
if p.is_api_version
|
|
154
|
+
and p.method_location in [ParameterMethodLocation.KEYWORD_ONLY, ParameterMethodLocation.KWARG]
|
|
155
|
+
):
|
|
156
|
+
retval = add_to_pylint_disable(retval, "client-accepts-api-version-keyword")
|
|
157
|
+
if len(self.operation_groups) > 6:
|
|
158
|
+
retval = add_to_pylint_disable(retval, "too-many-instance-attributes")
|
|
159
|
+
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
160
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
161
|
+
return retval
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def url_pylint_disable(self) -> str:
|
|
165
|
+
# if the url is too long
|
|
166
|
+
retval = ""
|
|
167
|
+
if len(self.url) > 85:
|
|
168
|
+
retval = add_to_pylint_disable(retval, "line-too-long")
|
|
169
|
+
return retval
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def filename(self) -> str:
|
|
173
|
+
"""Name of the file for the client"""
|
|
174
|
+
if self.code_model.options["version_tolerant"] or self.code_model.options["low_level_client"]:
|
|
175
|
+
return "_client"
|
|
176
|
+
return f"_{self.legacy_filename}"
|
|
177
|
+
|
|
178
|
+
def lookup_request_builder(self, request_builder_id: int) -> Union[RequestBuilder, OverloadedRequestBuilder]:
|
|
179
|
+
"""Find the request builder based off of id"""
|
|
180
|
+
try:
|
|
181
|
+
return next(rb for rb in self.request_builders if id(rb.yaml_data) == request_builder_id)
|
|
182
|
+
except StopIteration as exc:
|
|
183
|
+
raise KeyError(f"No request builder with id {request_builder_id} found.") from exc
|
|
184
|
+
|
|
185
|
+
def lookup_operation(self, operation_id: int) -> "OperationType":
|
|
186
|
+
try:
|
|
187
|
+
return next(o for og in self.operation_groups for o in og.operations if id(o.yaml_data) == operation_id)
|
|
188
|
+
except StopIteration as exc:
|
|
189
|
+
raise KeyError(f"No operation with id {operation_id} found.") from exc
|
|
190
|
+
|
|
191
|
+
def _imports_shared(self, async_mode: bool) -> FileImport:
|
|
192
|
+
file_import = FileImport(self.code_model)
|
|
193
|
+
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
194
|
+
if self.code_model.options["azure_arm"]:
|
|
195
|
+
file_import.add_submodule_import("azure.mgmt.core", self.pipeline_class(async_mode), ImportType.SDKCORE)
|
|
196
|
+
else:
|
|
197
|
+
file_import.add_submodule_import(
|
|
198
|
+
"" if self.code_model.is_azure_flavor else "runtime",
|
|
199
|
+
self.pipeline_class(async_mode),
|
|
200
|
+
ImportType.SDKCORE,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
for gp in self.parameters:
|
|
204
|
+
if gp.method_location == ParameterMethodLocation.KWARG:
|
|
205
|
+
continue
|
|
206
|
+
file_import.merge(
|
|
207
|
+
gp.imports(
|
|
208
|
+
async_mode,
|
|
209
|
+
relative_path=".." if async_mode else ".",
|
|
210
|
+
operation=True,
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
file_import.add_submodule_import(
|
|
214
|
+
"._configuration",
|
|
215
|
+
f"{self.name}Configuration",
|
|
216
|
+
ImportType.LOCAL,
|
|
217
|
+
)
|
|
218
|
+
file_import.add_msrest_import(
|
|
219
|
+
relative_path=".." if async_mode else ".",
|
|
220
|
+
msrest_import_type=MsrestImportType.SerializerDeserializer,
|
|
221
|
+
typing_section=TypingSection.REGULAR,
|
|
222
|
+
)
|
|
223
|
+
file_import.add_submodule_import(
|
|
224
|
+
"pipeline" if self.code_model.is_azure_flavor else "runtime",
|
|
225
|
+
"policies",
|
|
226
|
+
ImportType.SDKCORE,
|
|
227
|
+
)
|
|
228
|
+
if self.code_model.options["azure_arm"]:
|
|
229
|
+
async_prefix = "Async" if async_mode else ""
|
|
230
|
+
file_import.add_submodule_import(
|
|
231
|
+
"azure.mgmt.core.policies",
|
|
232
|
+
f"{async_prefix}ARMAutoResourceProviderRegistrationPolicy",
|
|
233
|
+
ImportType.SDKCORE,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# import for "Self"
|
|
237
|
+
file_import.add_submodule_import(
|
|
238
|
+
"typing_extensions",
|
|
239
|
+
"Self",
|
|
240
|
+
ImportType.STDLIB,
|
|
241
|
+
)
|
|
242
|
+
return file_import
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def has_mixin(self) -> bool:
|
|
246
|
+
"""Do we want a mixin ABC class for typing purposes?"""
|
|
247
|
+
return any(og for og in self.operation_groups if og.is_mixin)
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def lro_operations(self) -> List["OperationType"]:
|
|
251
|
+
"""all LRO operations in this SDK?"""
|
|
252
|
+
return [operation for operation_group in self.operation_groups for operation in operation_group.lro_operations]
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def has_public_lro_operations(self) -> bool:
|
|
256
|
+
"""Are there any public LRO operations in this SDK?"""
|
|
257
|
+
return any(not operation.internal for operation in self.lro_operations)
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def has_operations(self) -> bool:
|
|
261
|
+
return any(operation_group.has_operations for operation_group in self.operation_groups)
|
|
262
|
+
|
|
263
|
+
def link_lro_initial_operations(self) -> None:
|
|
264
|
+
"""Link each LRO operation to its initial operation"""
|
|
265
|
+
for operation_group in self.operation_groups:
|
|
266
|
+
for operation in operation_group.operations:
|
|
267
|
+
if isinstance(operation, (LROOperation, LROPagingOperation)):
|
|
268
|
+
operation.initial_operation = self.lookup_operation(id(operation.yaml_data["initialOperation"]))
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def has_abstract_operations(self) -> bool:
|
|
272
|
+
"""Whether there is abstract operation in any operation group."""
|
|
273
|
+
return any(og.has_abstract_operations for og in self.operation_groups)
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def has_non_abstract_operations(self) -> bool:
|
|
277
|
+
"""Whether there is non-abstract operation in any operation group."""
|
|
278
|
+
return any(og.has_non_abstract_operations for og in self.operation_groups)
|
|
279
|
+
|
|
280
|
+
def imports(self, async_mode: bool) -> FileImport:
|
|
281
|
+
file_import = self._imports_shared(async_mode)
|
|
282
|
+
if async_mode:
|
|
283
|
+
file_import.add_submodule_import("typing", "Awaitable", ImportType.STDLIB)
|
|
284
|
+
file_import.add_submodule_import(
|
|
285
|
+
"rest",
|
|
286
|
+
"AsyncHttpResponse",
|
|
287
|
+
ImportType.SDKCORE,
|
|
288
|
+
TypingSection.CONDITIONAL,
|
|
289
|
+
)
|
|
290
|
+
else:
|
|
291
|
+
file_import.add_submodule_import(
|
|
292
|
+
"rest",
|
|
293
|
+
"HttpResponse",
|
|
294
|
+
ImportType.SDKCORE,
|
|
295
|
+
TypingSection.CONDITIONAL,
|
|
296
|
+
)
|
|
297
|
+
file_import.add_submodule_import(
|
|
298
|
+
"rest",
|
|
299
|
+
"HttpRequest",
|
|
300
|
+
ImportType.SDKCORE,
|
|
301
|
+
TypingSection.CONDITIONAL,
|
|
302
|
+
)
|
|
303
|
+
for og in self.operation_groups:
|
|
304
|
+
file_import.add_submodule_import(
|
|
305
|
+
f".{self.code_model.operations_folder_name}",
|
|
306
|
+
og.class_name,
|
|
307
|
+
ImportType.LOCAL,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if self.code_model.model_types and self.code_model.options["models_mode"] == "msrest":
|
|
311
|
+
path_to_models = ".." if async_mode else "."
|
|
312
|
+
file_import.add_submodule_import(path_to_models, "models", ImportType.LOCAL, alias="_models")
|
|
313
|
+
elif self.code_model.options["models_mode"] == "msrest":
|
|
314
|
+
# in this case, we have client_models = {} in the service client, which needs a type annotation
|
|
315
|
+
# this import will always be commented, so will always add it to the typing section
|
|
316
|
+
file_import.add_submodule_import("typing", "Dict", ImportType.STDLIB)
|
|
317
|
+
file_import.add_submodule_import("copy", "deepcopy", ImportType.STDLIB)
|
|
318
|
+
return file_import
|
|
319
|
+
|
|
320
|
+
def imports_for_multiapi(self, async_mode: bool) -> FileImport:
|
|
321
|
+
file_import = self._imports_shared(async_mode)
|
|
322
|
+
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
323
|
+
try:
|
|
324
|
+
mixin_operation = next(og for og in self.operation_groups if og.is_mixin)
|
|
325
|
+
file_import.add_submodule_import("._operations_mixin", mixin_operation.class_name, ImportType.LOCAL)
|
|
326
|
+
except StopIteration:
|
|
327
|
+
pass
|
|
328
|
+
file_import.add_submodule_import("azure.profiles", "KnownProfiles", import_type=ImportType.SDKCORE)
|
|
329
|
+
file_import.add_submodule_import("azure.profiles", "ProfileDefinition", import_type=ImportType.SDKCORE)
|
|
330
|
+
file_import.add_submodule_import(
|
|
331
|
+
"azure.profiles.multiapiclient",
|
|
332
|
+
"MultiApiClientMixin",
|
|
333
|
+
import_type=ImportType.SDKCORE,
|
|
334
|
+
)
|
|
335
|
+
return file_import
|
|
336
|
+
|
|
337
|
+
@classmethod
|
|
338
|
+
def from_yaml(
|
|
339
|
+
cls,
|
|
340
|
+
yaml_data: Dict[str, Any],
|
|
341
|
+
code_model: "CodeModel",
|
|
342
|
+
*,
|
|
343
|
+
is_subclient: bool = False,
|
|
344
|
+
) -> "Client":
|
|
345
|
+
return cls(
|
|
346
|
+
yaml_data=yaml_data,
|
|
347
|
+
code_model=code_model,
|
|
348
|
+
parameters=ClientGlobalParameterList.from_yaml(yaml_data, code_model),
|
|
349
|
+
is_subclient=is_subclient,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class Config(_ClientConfigBase[ConfigGlobalParameterList]):
|
|
354
|
+
"""Model representing our Config type."""
|
|
355
|
+
|
|
356
|
+
def pylint_disable(self) -> str:
|
|
357
|
+
retval = add_to_pylint_disable("", "too-many-instance-attributes") if self.code_model.is_azure_flavor else ""
|
|
358
|
+
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
359
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
360
|
+
return retval
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def description(self) -> str:
|
|
364
|
+
return (
|
|
365
|
+
f"Configuration for {self.yaml_data['name']}.\n\n."
|
|
366
|
+
"Note that all parameters used to create this instance are saved as instance attributes."
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def sdk_moniker(self) -> str:
|
|
371
|
+
package_name = self.code_model.options["package_name"]
|
|
372
|
+
if package_name and package_name.startswith("azure-"):
|
|
373
|
+
package_name = package_name[len("azure-") :]
|
|
374
|
+
return package_name if package_name else self.yaml_data["name"].lower()
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def name(self) -> str:
|
|
378
|
+
return f"{super().name}Configuration"
|
|
379
|
+
|
|
380
|
+
def _imports_shared(self, async_mode: bool) -> FileImport:
|
|
381
|
+
file_import = FileImport(self.code_model)
|
|
382
|
+
file_import.add_submodule_import(
|
|
383
|
+
"pipeline" if self.code_model.is_azure_flavor else "runtime",
|
|
384
|
+
"policies",
|
|
385
|
+
ImportType.SDKCORE,
|
|
386
|
+
)
|
|
387
|
+
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
388
|
+
if self.code_model.options["package_version"]:
|
|
389
|
+
file_import.add_submodule_import(".._version" if async_mode else "._version", "VERSION", ImportType.LOCAL)
|
|
390
|
+
if self.code_model.options["azure_arm"]:
|
|
391
|
+
policy = "AsyncARMChallengeAuthenticationPolicy" if async_mode else "ARMChallengeAuthenticationPolicy"
|
|
392
|
+
file_import.add_submodule_import("azure.mgmt.core.policies", "ARMHttpLoggingPolicy", ImportType.SDKCORE)
|
|
393
|
+
file_import.add_submodule_import("azure.mgmt.core.policies", policy, ImportType.SDKCORE)
|
|
394
|
+
|
|
395
|
+
return file_import
|
|
396
|
+
|
|
397
|
+
def imports(self, async_mode: bool) -> FileImport:
|
|
398
|
+
file_import = self._imports_shared(async_mode)
|
|
399
|
+
for gp in self.parameters:
|
|
400
|
+
if gp.method_location == ParameterMethodLocation.KWARG and gp not in self.parameters.kwargs_to_pop:
|
|
401
|
+
continue
|
|
402
|
+
file_import.merge(
|
|
403
|
+
gp.imports(
|
|
404
|
+
async_mode=async_mode,
|
|
405
|
+
relative_path=".." if async_mode else ".",
|
|
406
|
+
operation=True,
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
return file_import
|
|
410
|
+
|
|
411
|
+
def imports_for_multiapi(self, async_mode: bool) -> FileImport:
|
|
412
|
+
file_import = self._imports_shared(async_mode)
|
|
413
|
+
for gp in self.parameters:
|
|
414
|
+
if (
|
|
415
|
+
gp.method_location == ParameterMethodLocation.KWARG
|
|
416
|
+
and gp not in self.parameters.kwargs_to_pop
|
|
417
|
+
and gp.client_name == "api_version"
|
|
418
|
+
):
|
|
419
|
+
continue
|
|
420
|
+
file_import.merge(
|
|
421
|
+
gp.imports_for_multiapi(
|
|
422
|
+
async_mode=async_mode,
|
|
423
|
+
relative_path=".." if async_mode else ".",
|
|
424
|
+
operation=True,
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
return file_import
|
|
428
|
+
|
|
429
|
+
@classmethod
|
|
430
|
+
def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel") -> "Config":
|
|
431
|
+
return cls(
|
|
432
|
+
yaml_data=yaml_data,
|
|
433
|
+
code_model=code_model,
|
|
434
|
+
parameters=ConfigGlobalParameterList.from_yaml(yaml_data, code_model),
|
|
435
|
+
)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
|
+
# license information.
|
|
5
|
+
# --------------------------------------------------------------------------
|
|
6
|
+
from typing import List, Dict, Any, Set, Union, Literal
|
|
7
|
+
|
|
8
|
+
from .base import BaseType
|
|
9
|
+
from .enum_type import EnumType
|
|
10
|
+
from .model_type import ModelType, UsageFlags
|
|
11
|
+
from .combined_type import CombinedType
|
|
12
|
+
from .client import Client
|
|
13
|
+
from .request_builder import RequestBuilder, OverloadedRequestBuilder
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _is_legacy(options) -> bool:
|
|
17
|
+
return not (options.get("version_tolerant") or options.get("low_level_client"))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-instance-attributes
|
|
21
|
+
"""Top level code model
|
|
22
|
+
|
|
23
|
+
:param options: Options of the code model. I.e., whether this is for management generation
|
|
24
|
+
:type options: dict[str, bool]
|
|
25
|
+
:param str module_name: The module name for the client. Is in snake case.
|
|
26
|
+
:param str class_name: The class name for the client. Is in pascal case.
|
|
27
|
+
:param str description: The description of the client
|
|
28
|
+
:param str namespace: The namespace of our module
|
|
29
|
+
:param schemas: The list of schemas we are going to serialize in the models files. Maps their yaml
|
|
30
|
+
id to our created ModelType.
|
|
31
|
+
:type schemas: dict[int, ~autorest.models.ModelType]
|
|
32
|
+
:param sorted_schemas: Our schemas in order by inheritance and alphabet
|
|
33
|
+
:type sorted_schemas: list[~autorest.models.ModelType]
|
|
34
|
+
:param enums: The enums, if any, we are going to serialize. Maps their yaml id to our created EnumType.
|
|
35
|
+
:type enums: Dict[int, ~autorest.models.EnumType]
|
|
36
|
+
:param primitives: List of schemas we've created that are not EnumSchemas or ObjectSchemas. Maps their
|
|
37
|
+
yaml id to our created schemas.
|
|
38
|
+
:type primitives: Dict[int, ~autorest.models.BaseType]
|
|
39
|
+
:param package_dependency: All the dependencies needed in setup.py
|
|
40
|
+
:type package_dependency: Dict[str, str]
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
yaml_data: Dict[str, Any],
|
|
46
|
+
options: Dict[str, Any],
|
|
47
|
+
*,
|
|
48
|
+
is_subnamespace: bool = False,
|
|
49
|
+
) -> None:
|
|
50
|
+
self.yaml_data = yaml_data
|
|
51
|
+
self.options = options
|
|
52
|
+
self.namespace = self.yaml_data["namespace"]
|
|
53
|
+
self.types_map: Dict[int, BaseType] = {} # map yaml id to schema
|
|
54
|
+
self._model_types: List[ModelType] = []
|
|
55
|
+
from . import build_type
|
|
56
|
+
|
|
57
|
+
for type_yaml in yaml_data.get("types", []):
|
|
58
|
+
build_type(yaml_data=type_yaml, code_model=self)
|
|
59
|
+
self.clients: List[Client] = [
|
|
60
|
+
Client.from_yaml(client_yaml_data, self) for client_yaml_data in yaml_data["clients"]
|
|
61
|
+
]
|
|
62
|
+
self.subnamespace_to_clients: Dict[str, List[Client]] = {
|
|
63
|
+
subnamespace: [Client.from_yaml(client_yaml, self, is_subclient=True) for client_yaml in client_yamls]
|
|
64
|
+
for subnamespace, client_yamls in yaml_data.get("subnamespaceToClients", {}).items()
|
|
65
|
+
}
|
|
66
|
+
if self.options["models_mode"] and self.model_types:
|
|
67
|
+
self.sort_model_types()
|
|
68
|
+
self.is_subnamespace = is_subnamespace
|
|
69
|
+
self.named_unions: List[CombinedType] = [
|
|
70
|
+
t for t in self.types_map.values() if isinstance(t, CombinedType) and t.name
|
|
71
|
+
]
|
|
72
|
+
self.cross_language_package_id = self.yaml_data.get("crossLanguagePackageId")
|
|
73
|
+
self.for_test: bool = False
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def has_form_data(self) -> bool:
|
|
77
|
+
return any(og.has_form_data_body for client in self.clients for og in client.operation_groups)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def has_etag(self) -> bool:
|
|
81
|
+
return any(client.has_etag for client in self.clients)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def has_operations(self) -> bool:
|
|
85
|
+
if any(c for c in self.clients if c.has_operations):
|
|
86
|
+
return True
|
|
87
|
+
return any(c for clients in self.subnamespace_to_clients.values() for c in clients if c.has_operations)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def has_non_abstract_operations(self) -> bool:
|
|
91
|
+
return any(c for c in self.clients if c.has_non_abstract_operations) or any(
|
|
92
|
+
c for cs in self.subnamespace_to_clients.values() for c in cs if c.has_non_abstract_operations
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def lookup_request_builder(self, request_builder_id: int) -> Union[RequestBuilder, OverloadedRequestBuilder]:
|
|
96
|
+
"""Find the request builder based off of id"""
|
|
97
|
+
for client in self.clients:
|
|
98
|
+
try:
|
|
99
|
+
return client.lookup_request_builder(request_builder_id)
|
|
100
|
+
except KeyError:
|
|
101
|
+
pass
|
|
102
|
+
raise KeyError(f"No request builder with id {request_builder_id} found.")
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def is_azure_flavor(self) -> bool:
|
|
106
|
+
return self.options["flavor"] == "azure"
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def rest_layer_name(self) -> str:
|
|
110
|
+
"""If we have a separate rest layer, what is its name?"""
|
|
111
|
+
return "rest" if self.options["builders_visibility"] == "public" else "_rest"
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def client_filename(self) -> str:
|
|
115
|
+
return self.clients[0].filename
|
|
116
|
+
|
|
117
|
+
def need_vendored_code(self, async_mode: bool) -> bool:
|
|
118
|
+
"""Whether we need to vendor code in the _vendor.py file for this SDK"""
|
|
119
|
+
if self.has_abstract_operations:
|
|
120
|
+
return True
|
|
121
|
+
if async_mode:
|
|
122
|
+
return self.need_mixin_abc
|
|
123
|
+
return self.need_mixin_abc or self.has_etag or self.has_form_data
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def need_mixin_abc(self) -> bool:
|
|
127
|
+
return any(c for c in self.clients if c.has_mixin)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def has_abstract_operations(self) -> bool:
|
|
131
|
+
return any(c for c in self.clients if c.has_abstract_operations)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def operations_folder_name(self) -> str:
|
|
135
|
+
"""Get the name of the operations folder that holds operations."""
|
|
136
|
+
name = "operations"
|
|
137
|
+
if self.options["version_tolerant"] and not any(
|
|
138
|
+
og for client in self.clients for og in client.operation_groups if not og.is_mixin
|
|
139
|
+
):
|
|
140
|
+
name = f"_{name}"
|
|
141
|
+
return name
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def description(self) -> str:
|
|
145
|
+
return self.clients[0].description
|
|
146
|
+
|
|
147
|
+
def lookup_type(self, schema_id: int) -> BaseType:
|
|
148
|
+
"""Looks to see if the schema has already been created.
|
|
149
|
+
|
|
150
|
+
:param int schema_id: The yaml id of the schema
|
|
151
|
+
:return: If created, we return the created schema, otherwise, we throw.
|
|
152
|
+
:rtype: ~autorest.models.BaseType
|
|
153
|
+
:raises: KeyError if schema is not found
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
return next(type for id, type in self.types_map.items() if id == schema_id)
|
|
157
|
+
except StopIteration as exc:
|
|
158
|
+
raise KeyError(f"Couldn't find schema with id {schema_id}") from exc
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def model_types(self) -> List[ModelType]:
|
|
162
|
+
"""All of the model types in this class"""
|
|
163
|
+
if not self._model_types:
|
|
164
|
+
self._model_types = [
|
|
165
|
+
t for t in self.types_map.values() if isinstance(t, ModelType) and t.usage != UsageFlags.Default.value
|
|
166
|
+
]
|
|
167
|
+
return self._model_types
|
|
168
|
+
|
|
169
|
+
@model_types.setter
|
|
170
|
+
def model_types(self, val: List[ModelType]) -> None:
|
|
171
|
+
self._model_types = val
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def public_model_types(self) -> List[ModelType]:
|
|
175
|
+
return [m for m in self.model_types if not m.internal and not m.base == "json"]
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def enums(self) -> List[EnumType]:
|
|
179
|
+
"""All of the enums"""
|
|
180
|
+
return [t for t in self.types_map.values() if isinstance(t, EnumType)]
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def core_library(self) -> Literal["azure.core", "corehttp"]:
|
|
184
|
+
return "azure.core" if self.is_azure_flavor else "corehttp"
|
|
185
|
+
|
|
186
|
+
def _sort_model_types_helper(
|
|
187
|
+
self,
|
|
188
|
+
current: ModelType,
|
|
189
|
+
seen_schema_names: Set[str],
|
|
190
|
+
seen_schema_yaml_ids: Set[int],
|
|
191
|
+
):
|
|
192
|
+
if current.id in seen_schema_yaml_ids:
|
|
193
|
+
return []
|
|
194
|
+
if current.name in seen_schema_names:
|
|
195
|
+
raise ValueError(f"We have already generated a schema with name {current.name}")
|
|
196
|
+
ancestors = [current]
|
|
197
|
+
if current.parents:
|
|
198
|
+
for parent in current.parents:
|
|
199
|
+
if parent.id in seen_schema_yaml_ids:
|
|
200
|
+
continue
|
|
201
|
+
seen_schema_names.add(current.name)
|
|
202
|
+
seen_schema_yaml_ids.add(current.id)
|
|
203
|
+
ancestors = self._sort_model_types_helper(parent, seen_schema_names, seen_schema_yaml_ids) + ancestors
|
|
204
|
+
seen_schema_names.add(current.name)
|
|
205
|
+
seen_schema_yaml_ids.add(current.id)
|
|
206
|
+
return ancestors
|
|
207
|
+
|
|
208
|
+
def sort_model_types(self) -> None:
|
|
209
|
+
"""Sorts the final object schemas by inheritance and by alphabetical order.
|
|
210
|
+
|
|
211
|
+
:return: None
|
|
212
|
+
:rtype: None
|
|
213
|
+
"""
|
|
214
|
+
seen_schema_names: Set[str] = set()
|
|
215
|
+
seen_schema_yaml_ids: Set[int] = set()
|
|
216
|
+
sorted_object_schemas: List[ModelType] = []
|
|
217
|
+
for schema in sorted(self.model_types, key=lambda x: x.name.lower()):
|
|
218
|
+
sorted_object_schemas.extend(self._sort_model_types_helper(schema, seen_schema_names, seen_schema_yaml_ids))
|
|
219
|
+
self.model_types = sorted_object_schemas
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def models_filename(self) -> str:
|
|
223
|
+
"""Get the names of the model file(s)"""
|
|
224
|
+
if self.is_legacy:
|
|
225
|
+
return "_models_py3"
|
|
226
|
+
return "_models"
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def enums_filename(self) -> str:
|
|
230
|
+
"""The name of the enums file"""
|
|
231
|
+
if self.is_legacy:
|
|
232
|
+
return f"_{self.clients[0].legacy_filename}_enums"
|
|
233
|
+
return "_enums"
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def is_legacy(self) -> bool:
|
|
237
|
+
return _is_legacy(self.options)
|