@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,238 @@
|
|
|
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, List, TYPE_CHECKING, Optional, cast
|
|
7
|
+
|
|
8
|
+
from .base import BaseType
|
|
9
|
+
from .imports import FileImport, ImportType, TypingSection
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .code_model import CodeModel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EnumValue(BaseType):
|
|
16
|
+
"""Model containing necessary information for a single value of an enum.
|
|
17
|
+
|
|
18
|
+
:param str name: The name of this enum value
|
|
19
|
+
:param str value: The value of this enum value
|
|
20
|
+
:param str description: Optional. The description for this enum value
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
yaml_data: Dict[str, Any],
|
|
26
|
+
code_model: "CodeModel",
|
|
27
|
+
enum_type: "EnumType",
|
|
28
|
+
value_type: BaseType,
|
|
29
|
+
) -> None:
|
|
30
|
+
super().__init__(yaml_data=yaml_data, code_model=code_model)
|
|
31
|
+
self.name: str = self.yaml_data["name"]
|
|
32
|
+
self.value: str = self.yaml_data["value"]
|
|
33
|
+
self.enum_type = enum_type
|
|
34
|
+
self.value_type = value_type
|
|
35
|
+
|
|
36
|
+
def description(self, *, is_operation_file: bool) -> str:
|
|
37
|
+
return self.yaml_data.get("description", "")
|
|
38
|
+
|
|
39
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
40
|
+
"""The python type used for type annotation"""
|
|
41
|
+
return f"Literal[{self.enum_type.name}.{self.name}]"
|
|
42
|
+
|
|
43
|
+
def get_declaration(self, value=None):
|
|
44
|
+
return self.enum_type.name + "." + self.name
|
|
45
|
+
|
|
46
|
+
def docstring_text(self, **kwargs: Any) -> str:
|
|
47
|
+
return self.enum_type.name + "." + self.name
|
|
48
|
+
|
|
49
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
50
|
+
"""The python type used for RST syntax input and type annotation."""
|
|
51
|
+
|
|
52
|
+
type_annotation = self.value_type.type_annotation(**kwargs)
|
|
53
|
+
enum_type_annotation = f"{self.code_model.namespace}.models.{self.name}"
|
|
54
|
+
return f"{type_annotation} or ~{enum_type_annotation}"
|
|
55
|
+
|
|
56
|
+
def get_json_template_representation(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
client_default_value_declaration: Optional[str] = None,
|
|
60
|
+
) -> Any:
|
|
61
|
+
# for better display effect, use the only value instead of var type
|
|
62
|
+
return self.value_type.get_json_template_representation(
|
|
63
|
+
client_default_value_declaration=client_default_value_declaration,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def serialization_type(self) -> str:
|
|
68
|
+
return self.value_type.serialization_type
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def instance_check_template(self) -> str:
|
|
72
|
+
return self.value_type.instance_check_template
|
|
73
|
+
|
|
74
|
+
def imports(self, **kwargs: Any) -> FileImport:
|
|
75
|
+
file_import = FileImport(self.code_model)
|
|
76
|
+
file_import.merge(self.value_type.imports(**kwargs))
|
|
77
|
+
file_import.add_submodule_import("typing", "Literal", ImportType.STDLIB, TypingSection.REGULAR)
|
|
78
|
+
file_import.add_submodule_import("._enums", self.enum_type.name, ImportType.LOCAL, TypingSection.REGULAR)
|
|
79
|
+
|
|
80
|
+
return file_import
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel") -> "EnumValue":
|
|
84
|
+
"""Constructs an EnumValue from yaml data.
|
|
85
|
+
|
|
86
|
+
:param yaml_data: the yaml data from which we will construct this object
|
|
87
|
+
:type yaml_data: dict[str, Any]
|
|
88
|
+
|
|
89
|
+
:return: A created EnumValue
|
|
90
|
+
:rtype: ~autorest.models.EnumValue
|
|
91
|
+
"""
|
|
92
|
+
from . import build_type
|
|
93
|
+
|
|
94
|
+
return cls(
|
|
95
|
+
yaml_data=yaml_data,
|
|
96
|
+
code_model=code_model,
|
|
97
|
+
enum_type=cast(EnumType, build_type(yaml_data["enumType"], code_model)),
|
|
98
|
+
value_type=build_type(yaml_data["valueType"], code_model),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class EnumType(BaseType):
|
|
103
|
+
"""Schema for enums that will be serialized.
|
|
104
|
+
|
|
105
|
+
:param yaml_data: the yaml data for this schema
|
|
106
|
+
:type yaml_data: dict[str, Any]
|
|
107
|
+
:param str description: The description of this enum
|
|
108
|
+
:param str name: The name of the enum.
|
|
109
|
+
:type element_type: ~autorest.models.PrimitiveType
|
|
110
|
+
:param values: List of the values for this enum
|
|
111
|
+
:type values: list[~autorest.models.EnumValue]
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
yaml_data: Dict[str, Any],
|
|
117
|
+
code_model: "CodeModel",
|
|
118
|
+
values: List["EnumValue"],
|
|
119
|
+
value_type: BaseType,
|
|
120
|
+
) -> None:
|
|
121
|
+
super().__init__(yaml_data=yaml_data, code_model=code_model)
|
|
122
|
+
self.name: str = yaml_data["name"][0].upper() + yaml_data["name"][1:]
|
|
123
|
+
self.values = values
|
|
124
|
+
self.value_type = value_type
|
|
125
|
+
self.internal: bool = self.yaml_data.get("internal", False)
|
|
126
|
+
self.cross_language_definition_id: Optional[str] = self.yaml_data.get("crossLanguageDefinitionId")
|
|
127
|
+
|
|
128
|
+
def __lt__(self, other):
|
|
129
|
+
return self.name.lower() < other.name.lower()
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def serialization_type(self) -> str:
|
|
133
|
+
"""Returns the serialization value for msrest.
|
|
134
|
+
|
|
135
|
+
:return: The serialization value for msrest
|
|
136
|
+
:rtype: str
|
|
137
|
+
"""
|
|
138
|
+
return self.value_type.serialization_type
|
|
139
|
+
|
|
140
|
+
def description(self, *, is_operation_file: bool) -> str:
|
|
141
|
+
possible_values = [self.get_declaration(v.value) for v in self.values]
|
|
142
|
+
if not possible_values:
|
|
143
|
+
return ""
|
|
144
|
+
if len(possible_values) == 1:
|
|
145
|
+
return possible_values[0]
|
|
146
|
+
if len(possible_values) == 2:
|
|
147
|
+
possible_values_str = " and ".join(possible_values)
|
|
148
|
+
else:
|
|
149
|
+
possible_values_str = (
|
|
150
|
+
", ".join(possible_values[: len(possible_values) - 1]) + f", and {possible_values[-1]}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
enum_description = f"Known values are: {possible_values_str}."
|
|
154
|
+
return enum_description
|
|
155
|
+
|
|
156
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
157
|
+
"""The python type used for type annotation
|
|
158
|
+
|
|
159
|
+
:return: The type annotation for this schema
|
|
160
|
+
:rtype: str
|
|
161
|
+
"""
|
|
162
|
+
if self.code_model.options["models_mode"]:
|
|
163
|
+
module_name = "_models." if kwargs.get("need_module_name", True) else ""
|
|
164
|
+
file_name = f"{self.code_model.enums_filename}." if self.internal else ""
|
|
165
|
+
model_name = module_name + file_name + self.name
|
|
166
|
+
# we don't need quoted annotation in operation files, and need it in model folder files.
|
|
167
|
+
if not kwargs.get("is_operation_file", False):
|
|
168
|
+
model_name = f'"{model_name}"'
|
|
169
|
+
|
|
170
|
+
return f"Union[{self.value_type.type_annotation(**kwargs)}, {model_name}]"
|
|
171
|
+
return self.value_type.type_annotation(**kwargs)
|
|
172
|
+
|
|
173
|
+
def get_declaration(self, value: Any) -> str:
|
|
174
|
+
return self.value_type.get_declaration(value)
|
|
175
|
+
|
|
176
|
+
def docstring_text(self, **kwargs: Any) -> str:
|
|
177
|
+
if self.code_model.options["models_mode"]:
|
|
178
|
+
return self.name
|
|
179
|
+
return self.value_type.type_annotation(**kwargs)
|
|
180
|
+
|
|
181
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
182
|
+
"""The python type used for RST syntax input and type annotation."""
|
|
183
|
+
if self.code_model.options["models_mode"]:
|
|
184
|
+
type_annotation = self.value_type.type_annotation(**kwargs)
|
|
185
|
+
enum_type_annotation = f"{self.code_model.namespace}.models.{self.name}"
|
|
186
|
+
return f"{type_annotation} or ~{enum_type_annotation}"
|
|
187
|
+
return self.value_type.type_annotation(**kwargs)
|
|
188
|
+
|
|
189
|
+
def get_json_template_representation(
|
|
190
|
+
self,
|
|
191
|
+
*,
|
|
192
|
+
client_default_value_declaration: Optional[str] = None,
|
|
193
|
+
) -> Any:
|
|
194
|
+
# for better display effect, use the only value instead of var type
|
|
195
|
+
return self.value_type.get_json_template_representation(
|
|
196
|
+
client_default_value_declaration=client_default_value_declaration,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def instance_check_template(self) -> str:
|
|
201
|
+
return self.value_type.instance_check_template
|
|
202
|
+
|
|
203
|
+
def fill_instance_from_yaml(self, yaml_data: Dict[str, Any], code_model: "CodeModel") -> None:
|
|
204
|
+
for value in yaml_data["values"]:
|
|
205
|
+
self.values.append(EnumValue.from_yaml(value, code_model))
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel") -> "EnumType":
|
|
209
|
+
raise ValueError(
|
|
210
|
+
"You shouldn't call from_yaml for EnumType to avoid recursion. "
|
|
211
|
+
"Please initial a blank EnumType, then call .fill_instance_from_yaml on the created type."
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def imports(self, **kwargs: Any) -> FileImport:
|
|
215
|
+
operation = kwargs.pop("operation", False)
|
|
216
|
+
file_import = FileImport(self.code_model)
|
|
217
|
+
if self.code_model.options["models_mode"]:
|
|
218
|
+
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
219
|
+
if not operation:
|
|
220
|
+
file_import.add_submodule_import(
|
|
221
|
+
"..",
|
|
222
|
+
"models",
|
|
223
|
+
ImportType.LOCAL,
|
|
224
|
+
TypingSection.TYPING,
|
|
225
|
+
alias="_models",
|
|
226
|
+
)
|
|
227
|
+
file_import.merge(self.value_type.imports(operation=operation, **kwargs))
|
|
228
|
+
relative_path = kwargs.pop("relative_path", None)
|
|
229
|
+
if self.code_model.options["models_mode"] and relative_path:
|
|
230
|
+
# add import for enums in operations file
|
|
231
|
+
file_import.add_submodule_import(
|
|
232
|
+
relative_path,
|
|
233
|
+
"models",
|
|
234
|
+
ImportType.LOCAL,
|
|
235
|
+
alias="_models",
|
|
236
|
+
typing_section=(TypingSection.TYPING if kwargs.get("model_typing") else TypingSection.REGULAR),
|
|
237
|
+
)
|
|
238
|
+
return file_import
|
|
@@ -0,0 +1,291 @@
|
|
|
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 enum import Enum, auto
|
|
7
|
+
from typing import Dict, List, Optional, Tuple, Union, Set, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .code_model import CodeModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ImportType(str, Enum):
|
|
14
|
+
"""
|
|
15
|
+
Ordering of these enum matters. We order import groupings in a file based off of this ordering.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
STDLIB = "stdlib"
|
|
19
|
+
THIRDPARTY = "thirdparty"
|
|
20
|
+
SDKCORE = "sdkcore"
|
|
21
|
+
LOCAL = "local"
|
|
22
|
+
BY_VERSION = "by_version"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TypingSection(str, Enum):
|
|
26
|
+
REGULAR = "regular" # this import is always a typing import
|
|
27
|
+
CONDITIONAL = "conditional" # is a typing import when we're dealing with files that py2 will use, else regular
|
|
28
|
+
TYPING = "typing" # never a typing import
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MsrestImportType(Enum):
|
|
32
|
+
Module = auto() # import _serialization.py or msrest.serialization as Module
|
|
33
|
+
Serializer = auto() # from _serialization.py or msrest.serialization import Serializer
|
|
34
|
+
SerializerDeserializer = auto() # from _serialization.py or msrest.serialization import Serializer and Deserializer
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ImportModel:
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
typing_section: TypingSection,
|
|
41
|
+
import_type: ImportType,
|
|
42
|
+
module_name: str,
|
|
43
|
+
*,
|
|
44
|
+
submodule_name: Optional[str] = None,
|
|
45
|
+
alias: Optional[str] = None,
|
|
46
|
+
version_modules: Optional[Tuple[Tuple[Tuple[int, int], str, Optional[str]]]] = None,
|
|
47
|
+
):
|
|
48
|
+
self.typing_section = typing_section
|
|
49
|
+
self.import_type = import_type
|
|
50
|
+
self.module_name = module_name
|
|
51
|
+
self.submodule_name = submodule_name
|
|
52
|
+
self.alias = alias
|
|
53
|
+
# version_modules: this field is for imports submodule from specified module by python version.
|
|
54
|
+
# It's a list of "python version, module_name, comments".
|
|
55
|
+
# The python version is in form of (major, minor), for instance (3, 9) stands for py3.9.
|
|
56
|
+
self.version_modules = version_modules
|
|
57
|
+
|
|
58
|
+
def __eq__(self, other):
|
|
59
|
+
try:
|
|
60
|
+
return (
|
|
61
|
+
self.typing_section == other.typing_section
|
|
62
|
+
and self.import_type == other.import_type
|
|
63
|
+
and self.module_name == other.module_name
|
|
64
|
+
and self.submodule_name == other.submodule_name
|
|
65
|
+
and self.alias == other.alias
|
|
66
|
+
)
|
|
67
|
+
except AttributeError:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
def __hash__(self) -> int:
|
|
71
|
+
retval: int = 0
|
|
72
|
+
for attr in dir(self):
|
|
73
|
+
if attr[0] != "_":
|
|
74
|
+
retval += hash(getattr(self, attr))
|
|
75
|
+
return retval
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TypeDefinition:
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
sync_definition: str,
|
|
82
|
+
async_definition: str,
|
|
83
|
+
):
|
|
84
|
+
self.sync_definition = sync_definition
|
|
85
|
+
self.async_definition = async_definition
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FileImport:
|
|
89
|
+
def __init__(self, code_model: "CodeModel") -> None:
|
|
90
|
+
self.imports: List[ImportModel] = []
|
|
91
|
+
self.code_model = code_model
|
|
92
|
+
# has sync and async type definitions
|
|
93
|
+
self.type_definitions: Dict[str, TypeDefinition] = {}
|
|
94
|
+
self.core_library = self.code_model.core_library
|
|
95
|
+
|
|
96
|
+
def _append_import(self, import_model: ImportModel) -> None:
|
|
97
|
+
if import_model.import_type == ImportType.SDKCORE:
|
|
98
|
+
mod_name = import_model.module_name
|
|
99
|
+
core_libraries = [
|
|
100
|
+
self.code_model.core_library,
|
|
101
|
+
"azure",
|
|
102
|
+
"msrest",
|
|
103
|
+
]
|
|
104
|
+
if all(l not in mod_name for l in core_libraries):
|
|
105
|
+
# this is to make sure we don't tack on core libraries when we don't need to
|
|
106
|
+
import_model.module_name = f"{self.code_model.core_library}{'.' if mod_name else ''}{mod_name}"
|
|
107
|
+
if not any(
|
|
108
|
+
i
|
|
109
|
+
for i in self.imports
|
|
110
|
+
if all(getattr(i, attr) == getattr(import_model, attr) for attr in dir(i) if attr[0] != "_")
|
|
111
|
+
):
|
|
112
|
+
self.imports.append(import_model)
|
|
113
|
+
|
|
114
|
+
def get_imports_from_section(self, typing_section: TypingSection) -> List[ImportModel]:
|
|
115
|
+
return [i for i in self.imports if i.typing_section == typing_section]
|
|
116
|
+
|
|
117
|
+
def add_submodule_import(
|
|
118
|
+
self,
|
|
119
|
+
module_name: str,
|
|
120
|
+
submodule_name: str,
|
|
121
|
+
import_type: ImportType,
|
|
122
|
+
typing_section: TypingSection = TypingSection.REGULAR,
|
|
123
|
+
alias: Optional[str] = None,
|
|
124
|
+
version_modules: Optional[Tuple[Tuple[Tuple[int, int], str, Optional[str]]]] = None,
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Add an import to this import block."""
|
|
127
|
+
self._append_import(
|
|
128
|
+
ImportModel(
|
|
129
|
+
typing_section=typing_section,
|
|
130
|
+
import_type=import_type,
|
|
131
|
+
module_name=module_name,
|
|
132
|
+
submodule_name=submodule_name,
|
|
133
|
+
alias=alias,
|
|
134
|
+
version_modules=version_modules,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def add_import(
|
|
139
|
+
self,
|
|
140
|
+
module_name: str,
|
|
141
|
+
import_type: ImportType,
|
|
142
|
+
typing_section: TypingSection = TypingSection.REGULAR,
|
|
143
|
+
alias: Optional[str] = None,
|
|
144
|
+
) -> None:
|
|
145
|
+
# Implementation detail: a regular import is just a "from" with no from
|
|
146
|
+
self._append_import(
|
|
147
|
+
ImportModel(
|
|
148
|
+
typing_section=typing_section,
|
|
149
|
+
import_type=import_type,
|
|
150
|
+
module_name=module_name,
|
|
151
|
+
alias=alias,
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def define_mypy_type(
|
|
156
|
+
self,
|
|
157
|
+
type_name: str,
|
|
158
|
+
type_value: str,
|
|
159
|
+
async_type_value: Optional[str] = None,
|
|
160
|
+
):
|
|
161
|
+
self.type_definitions[type_name] = TypeDefinition(type_value, async_type_value or type_value)
|
|
162
|
+
|
|
163
|
+
def merge(self, file_import: "FileImport") -> None:
|
|
164
|
+
"""Merge the given file import format."""
|
|
165
|
+
for i in file_import.imports:
|
|
166
|
+
self._append_import(i)
|
|
167
|
+
self.type_definitions.update(file_import.type_definitions)
|
|
168
|
+
|
|
169
|
+
def add_mutable_mapping_import(self) -> None:
|
|
170
|
+
self.add_import("sys", ImportType.STDLIB)
|
|
171
|
+
self.add_submodule_import(
|
|
172
|
+
"typing",
|
|
173
|
+
"MutableMapping",
|
|
174
|
+
ImportType.BY_VERSION,
|
|
175
|
+
TypingSection.REGULAR,
|
|
176
|
+
None,
|
|
177
|
+
(((3, 9), "collections.abc", None),),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def define_mutable_mapping_type(self) -> None:
|
|
181
|
+
"""Helper function for defining the mutable mapping type"""
|
|
182
|
+
self.add_mutable_mapping_import()
|
|
183
|
+
self.define_mypy_type(
|
|
184
|
+
"JSON",
|
|
185
|
+
"MutableMapping[str, Any] # pylint: disable=unsubscriptable-object",
|
|
186
|
+
)
|
|
187
|
+
self.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
188
|
+
|
|
189
|
+
def to_dict(
|
|
190
|
+
self,
|
|
191
|
+
) -> Dict[
|
|
192
|
+
TypingSection,
|
|
193
|
+
Dict[
|
|
194
|
+
ImportType,
|
|
195
|
+
Dict[
|
|
196
|
+
str,
|
|
197
|
+
Set[
|
|
198
|
+
Optional[
|
|
199
|
+
Union[
|
|
200
|
+
str,
|
|
201
|
+
Tuple[str, str],
|
|
202
|
+
Tuple[
|
|
203
|
+
str,
|
|
204
|
+
Optional[str],
|
|
205
|
+
Tuple[Tuple[Tuple[int, int], str, Optional[str]]],
|
|
206
|
+
],
|
|
207
|
+
]
|
|
208
|
+
]
|
|
209
|
+
],
|
|
210
|
+
],
|
|
211
|
+
],
|
|
212
|
+
]:
|
|
213
|
+
retval: Dict[
|
|
214
|
+
TypingSection,
|
|
215
|
+
Dict[
|
|
216
|
+
ImportType,
|
|
217
|
+
Dict[
|
|
218
|
+
str,
|
|
219
|
+
Set[
|
|
220
|
+
Optional[
|
|
221
|
+
Union[
|
|
222
|
+
str,
|
|
223
|
+
Tuple[str, str],
|
|
224
|
+
Tuple[
|
|
225
|
+
str,
|
|
226
|
+
Optional[str],
|
|
227
|
+
Tuple[Tuple[Tuple[int, int], str, Optional[str]]],
|
|
228
|
+
],
|
|
229
|
+
]
|
|
230
|
+
]
|
|
231
|
+
],
|
|
232
|
+
],
|
|
233
|
+
],
|
|
234
|
+
] = {}
|
|
235
|
+
for i in self.imports:
|
|
236
|
+
name_import: Optional[
|
|
237
|
+
Union[
|
|
238
|
+
str,
|
|
239
|
+
Tuple[str, str],
|
|
240
|
+
Tuple[
|
|
241
|
+
str,
|
|
242
|
+
Optional[str],
|
|
243
|
+
Tuple[Tuple[Tuple[int, int], str, Optional[str]]],
|
|
244
|
+
],
|
|
245
|
+
]
|
|
246
|
+
] = None
|
|
247
|
+
if i.submodule_name:
|
|
248
|
+
if i.version_modules:
|
|
249
|
+
name_import = (i.submodule_name, i.alias, i.version_modules)
|
|
250
|
+
elif i.alias:
|
|
251
|
+
name_import = (i.submodule_name, i.alias)
|
|
252
|
+
else:
|
|
253
|
+
name_import = i.submodule_name
|
|
254
|
+
retval.setdefault(i.typing_section, {}).setdefault(i.import_type, {}).setdefault(i.module_name, set()).add(
|
|
255
|
+
name_import
|
|
256
|
+
)
|
|
257
|
+
return retval
|
|
258
|
+
|
|
259
|
+
def add_msrest_import(
|
|
260
|
+
self,
|
|
261
|
+
*,
|
|
262
|
+
relative_path: str,
|
|
263
|
+
msrest_import_type: MsrestImportType,
|
|
264
|
+
typing_section: TypingSection,
|
|
265
|
+
):
|
|
266
|
+
if self.code_model.options["client_side_validation"]:
|
|
267
|
+
if msrest_import_type == MsrestImportType.Module:
|
|
268
|
+
self.add_import("msrest.serialization", ImportType.SDKCORE, typing_section)
|
|
269
|
+
else:
|
|
270
|
+
self.add_submodule_import("msrest", "Serializer", ImportType.THIRDPARTY, typing_section)
|
|
271
|
+
if msrest_import_type == MsrestImportType.SerializerDeserializer:
|
|
272
|
+
self.add_submodule_import("msrest", "Deserializer", ImportType.THIRDPARTY, typing_section)
|
|
273
|
+
else:
|
|
274
|
+
if self.code_model.options["multiapi"]:
|
|
275
|
+
relative_path += "."
|
|
276
|
+
if msrest_import_type == MsrestImportType.Module:
|
|
277
|
+
self.add_submodule_import(relative_path, "_serialization", ImportType.LOCAL, typing_section)
|
|
278
|
+
else:
|
|
279
|
+
self.add_submodule_import(
|
|
280
|
+
f"{relative_path}_serialization",
|
|
281
|
+
"Serializer",
|
|
282
|
+
ImportType.LOCAL,
|
|
283
|
+
typing_section,
|
|
284
|
+
)
|
|
285
|
+
if msrest_import_type == MsrestImportType.SerializerDeserializer:
|
|
286
|
+
self.add_submodule_import(
|
|
287
|
+
f"{relative_path}_serialization",
|
|
288
|
+
"Deserializer",
|
|
289
|
+
ImportType.LOCAL,
|
|
290
|
+
typing_section,
|
|
291
|
+
)
|
|
@@ -0,0 +1,143 @@
|
|
|
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, Optional, Union, TYPE_CHECKING, List
|
|
7
|
+
from .base import BaseType
|
|
8
|
+
from .imports import FileImport, ImportType, TypingSection
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .code_model import CodeModel
|
|
12
|
+
from .model_type import ModelType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ListType(BaseType):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
yaml_data: Dict[str, Any],
|
|
19
|
+
code_model: "CodeModel",
|
|
20
|
+
element_type: BaseType,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(yaml_data=yaml_data, code_model=code_model)
|
|
23
|
+
self.element_type = element_type
|
|
24
|
+
self.max_items: Optional[int] = yaml_data.get("maxItems")
|
|
25
|
+
self.min_items: Optional[int] = yaml_data.get("minItems")
|
|
26
|
+
self.unique_items: bool = yaml_data.get("uniqueItems", False)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def encode(self) -> Optional[str]:
|
|
30
|
+
return self.element_type.encode if hasattr(self.element_type, "encode") else None # type: ignore
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def serialization_type(self) -> str:
|
|
34
|
+
return f"[{self.element_type.serialization_type}]"
|
|
35
|
+
|
|
36
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
37
|
+
if (
|
|
38
|
+
self.code_model.options["version_tolerant"]
|
|
39
|
+
and self.element_type.is_xml
|
|
40
|
+
and not self.code_model.options["models_mode"]
|
|
41
|
+
):
|
|
42
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
43
|
+
return self.element_type.type_annotation(**kwargs)
|
|
44
|
+
return f"List[{self.element_type.type_annotation(**kwargs)}]"
|
|
45
|
+
|
|
46
|
+
def description(self, *, is_operation_file: bool) -> str:
|
|
47
|
+
return "" if is_operation_file else self.yaml_data.get("description", "")
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def xml_serialization_ctxt(self) -> Optional[str]:
|
|
51
|
+
attrs_list = []
|
|
52
|
+
base_xml_map = super().xml_serialization_ctxt
|
|
53
|
+
if base_xml_map:
|
|
54
|
+
attrs_list.append(base_xml_map)
|
|
55
|
+
|
|
56
|
+
# Attribute at the list level
|
|
57
|
+
if self.xml_metadata.get("wrapped", False):
|
|
58
|
+
attrs_list.append("'wrapped': True")
|
|
59
|
+
|
|
60
|
+
# Attributes of the items
|
|
61
|
+
item_xml_metadata = self.element_type.xml_metadata
|
|
62
|
+
if item_xml_metadata.get("name"):
|
|
63
|
+
attrs_list.append(f"'itemsName': '{item_xml_metadata['name']}'")
|
|
64
|
+
if item_xml_metadata.get("prefix", False):
|
|
65
|
+
attrs_list.append(f"'itemsPrefix': '{item_xml_metadata['prefix']}'")
|
|
66
|
+
if item_xml_metadata.get("namespace", False):
|
|
67
|
+
attrs_list.append(f"'itemsNs': '{item_xml_metadata['namespace']}'")
|
|
68
|
+
|
|
69
|
+
return ", ".join(attrs_list)
|
|
70
|
+
|
|
71
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
72
|
+
if self.code_model.options["version_tolerant"] and self.element_type.xml_metadata:
|
|
73
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
74
|
+
return self.element_type.docstring_type(**kwargs)
|
|
75
|
+
return f"list[{self.element_type.docstring_type(**kwargs)}]"
|
|
76
|
+
|
|
77
|
+
def docstring_text(self, **kwargs: Any) -> str:
|
|
78
|
+
if self.code_model.options["version_tolerant"] and self.element_type.xml_metadata:
|
|
79
|
+
# this means we're version tolerant XML, we just return the XML element
|
|
80
|
+
return self.element_type.docstring_text(**kwargs)
|
|
81
|
+
return f"list of {self.element_type.docstring_text(**kwargs)}"
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def validation(self) -> Optional[Dict[str, Union[bool, int, str]]]:
|
|
85
|
+
validation: Dict[str, Union[bool, int, str]] = {}
|
|
86
|
+
if self.max_items:
|
|
87
|
+
validation["max_items"] = self.max_items
|
|
88
|
+
validation["min_items"] = self.min_items or 0
|
|
89
|
+
if self.min_items:
|
|
90
|
+
validation["min_items"] = self.min_items
|
|
91
|
+
if self.unique_items:
|
|
92
|
+
validation["unique"] = True
|
|
93
|
+
return validation or None
|
|
94
|
+
|
|
95
|
+
def get_json_template_representation(
|
|
96
|
+
self,
|
|
97
|
+
*,
|
|
98
|
+
client_default_value_declaration: Optional[str] = None,
|
|
99
|
+
) -> Any:
|
|
100
|
+
return [
|
|
101
|
+
self.element_type.get_json_template_representation(
|
|
102
|
+
client_default_value_declaration=client_default_value_declaration,
|
|
103
|
+
)
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
|
|
107
|
+
from .model_type import ModelType
|
|
108
|
+
|
|
109
|
+
if isinstance(self.element_type, ModelType):
|
|
110
|
+
is_polymorphic_subtype = (
|
|
111
|
+
self.element_type.discriminator_value and not self.element_type.discriminated_subtypes
|
|
112
|
+
)
|
|
113
|
+
if self.element_type.name not in (m.name for m in polymorphic_subtypes) and is_polymorphic_subtype:
|
|
114
|
+
polymorphic_subtypes.append(self.element_type)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def instance_check_template(self) -> str:
|
|
118
|
+
return "isinstance({}, list)"
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def from_yaml(cls, yaml_data: Dict[str, Any], code_model: "CodeModel") -> "ListType":
|
|
122
|
+
from . import build_type
|
|
123
|
+
|
|
124
|
+
return cls(
|
|
125
|
+
yaml_data=yaml_data,
|
|
126
|
+
code_model=code_model,
|
|
127
|
+
element_type=build_type(yaml_data=yaml_data["elementType"], code_model=code_model),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def imports(self, **kwargs: Any) -> FileImport:
|
|
131
|
+
file_import = FileImport(self.code_model)
|
|
132
|
+
if not (
|
|
133
|
+
self.code_model.options["version_tolerant"]
|
|
134
|
+
and self.element_type.is_xml
|
|
135
|
+
and not self.code_model.options["models_mode"]
|
|
136
|
+
):
|
|
137
|
+
file_import.add_submodule_import("typing", "List", ImportType.STDLIB, TypingSection.CONDITIONAL)
|
|
138
|
+
file_import.merge(self.element_type.imports(**kwargs))
|
|
139
|
+
return file_import
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def type_description(self) -> str:
|
|
143
|
+
return f"[{self.element_type.type_description}]"
|