@autorest/python 6.1.5 → 6.1.7
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/autorest/__init__.py +10 -3
- package/autorest/black/__init__.py +2 -8
- package/autorest/codegen/__init__.py +4 -2
- package/autorest/codegen/models/__init__.py +1 -1
- package/autorest/codegen/models/base_builder.py +4 -1
- package/autorest/codegen/models/client.py +5 -3
- package/autorest/codegen/models/combined_type.py +6 -4
- package/autorest/codegen/models/enum_type.py +6 -4
- package/autorest/codegen/models/model_type.py +30 -9
- package/autorest/codegen/models/operation.py +32 -0
- package/autorest/codegen/models/operation_group.py +11 -7
- package/autorest/codegen/models/parameter.py +44 -0
- package/autorest/codegen/models/property.py +8 -15
- package/autorest/codegen/models/request_builder_parameter.py +4 -2
- package/autorest/codegen/serializers/__init__.py +18 -2
- package/autorest/codegen/serializers/builder_serializer.py +64 -22
- package/autorest/codegen/serializers/client_serializer.py +3 -4
- package/autorest/codegen/serializers/general_serializer.py +8 -0
- package/autorest/codegen/serializers/model_serializer.py +159 -61
- package/autorest/codegen/templates/model_base.py.jinja2 +636 -0
- package/autorest/codegen/templates/model_container.py.jinja2 +6 -2
- package/autorest/codegen/templates/model_dpg.py.jinja2 +62 -0
- package/autorest/codegen/templates/model_init.py.jinja2 +0 -5
- package/autorest/codegen/templates/{model.py.jinja2 → model_msrest.py.jinja2} +1 -1
- package/autorest/codegen/templates/operation_group.py.jinja2 +1 -1
- package/autorest/codegen/templates/operation_groups_container.py.jinja2 +1 -1
- package/autorest/codegen/templates/validation.py.jinja2 +38 -0
- package/package.json +9 -10
- package/ChangeLog.md +0 -1284
package/autorest/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import json
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
10
|
-
from typing import Any, Dict, Union
|
|
10
|
+
from typing import Any, Dict, Union, List
|
|
11
11
|
|
|
12
12
|
import yaml
|
|
13
13
|
|
|
@@ -22,6 +22,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
22
22
|
class ReaderAndWriter:
|
|
23
23
|
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
|
|
24
24
|
self.output_folder = Path(output_folder)
|
|
25
|
+
self._list_file: List[str] = []
|
|
25
26
|
try:
|
|
26
27
|
with open(
|
|
27
28
|
Path(self.output_folder) / Path("..") / Path("python.json"), "r"
|
|
@@ -37,7 +38,7 @@ class ReaderAndWriter:
|
|
|
37
38
|
self.options.update(python_json)
|
|
38
39
|
|
|
39
40
|
def read_file(self, path: Union[str, Path]) -> str:
|
|
40
|
-
"""
|
|
41
|
+
"""Directly reading from disk"""
|
|
41
42
|
# make path relative to output folder
|
|
42
43
|
try:
|
|
43
44
|
with open(self.output_folder / Path(path), "r") as fd:
|
|
@@ -46,13 +47,16 @@ class ReaderAndWriter:
|
|
|
46
47
|
return ""
|
|
47
48
|
|
|
48
49
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
49
|
-
"""
|
|
50
|
+
"""Directly writing to disk"""
|
|
50
51
|
file_folder = Path(filename).parent
|
|
51
52
|
if not Path.is_dir(self.output_folder / file_folder):
|
|
52
53
|
Path.mkdir(self.output_folder / file_folder, parents=True)
|
|
53
54
|
with open(self.output_folder / Path(filename), "w") as fd:
|
|
54
55
|
fd.write(file_content)
|
|
55
56
|
|
|
57
|
+
def list_file(self) -> List[str]:
|
|
58
|
+
return [str(f) for f in self.output_folder.glob("**/*") if f.is_file()]
|
|
59
|
+
|
|
56
60
|
|
|
57
61
|
class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
58
62
|
def __init__(
|
|
@@ -67,6 +71,9 @@ class ReaderAndWriterAutorest(ReaderAndWriter):
|
|
|
67
71
|
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
68
72
|
return self._autorestapi.write_file(filename, file_content)
|
|
69
73
|
|
|
74
|
+
def list_file(self) -> List[str]:
|
|
75
|
+
return self._autorestapi.list_inputs()
|
|
76
|
+
|
|
70
77
|
|
|
71
78
|
class Plugin(ReaderAndWriter, ABC):
|
|
72
79
|
"""A base class for autorest plugin.
|
|
@@ -30,16 +30,10 @@ class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
|
|
|
30
30
|
|
|
31
31
|
def process(self) -> bool:
|
|
32
32
|
# apply format_file on every file in the output folder
|
|
33
|
-
list(
|
|
34
|
-
map(
|
|
35
|
-
self.format_file,
|
|
36
|
-
[f for f in self.output_folder.glob("**/*") if f.is_file()],
|
|
37
|
-
)
|
|
38
|
-
)
|
|
33
|
+
list(map(self.format_file, [Path(f) for f in self.list_file()]))
|
|
39
34
|
return True
|
|
40
35
|
|
|
41
|
-
def format_file(self,
|
|
42
|
-
file = full_path.relative_to(self.output_folder)
|
|
36
|
+
def format_file(self, file: Path) -> None:
|
|
43
37
|
file_content = self.read_file(file)
|
|
44
38
|
if not file.suffix == ".py":
|
|
45
39
|
self.write_file(file, file_content)
|
|
@@ -44,9 +44,9 @@ def _validate_code_model_options(options: Dict[str, Any]) -> None:
|
|
|
44
44
|
"or 'embedded'"
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
-
if options["models_mode"] not in ["msrest", "none"]:
|
|
47
|
+
if options["models_mode"] not in ["msrest", "dpg", "none"]:
|
|
48
48
|
raise ValueError(
|
|
49
|
-
"--models-mode can only be 'msrest' or 'none'. "
|
|
49
|
+
"--models-mode can only be 'msrest', 'dpg' or 'none'. "
|
|
50
50
|
"Pass in 'msrest' if you want msrest models, or "
|
|
51
51
|
"'none' if you don't want any."
|
|
52
52
|
)
|
|
@@ -179,6 +179,8 @@ class CodeGenerator(Plugin):
|
|
|
179
179
|
models_mode_default = (
|
|
180
180
|
"none" if low_level_client or version_tolerant else "msrest"
|
|
181
181
|
)
|
|
182
|
+
if self.options.get("cadl_file") is not None:
|
|
183
|
+
models_mode_default = "dpg"
|
|
182
184
|
|
|
183
185
|
options: Dict[str, Any] = {
|
|
184
186
|
"azure_arm": azure_arm,
|
|
@@ -158,7 +158,7 @@ def build_type(yaml_data: Dict[str, Any], code_model: CodeModel) -> BaseType:
|
|
|
158
158
|
code_model.types_map[yaml_id] = response
|
|
159
159
|
response.fill_instance_from_yaml(yaml_data, code_model)
|
|
160
160
|
else:
|
|
161
|
-
object_type = yaml_data.get("type"
|
|
161
|
+
object_type = yaml_data.get("type")
|
|
162
162
|
if object_type is None:
|
|
163
163
|
_LOGGER.warning(
|
|
164
164
|
'Unrecognized definition type "%s" is found, falling back it as "string"! ',
|
|
@@ -31,7 +31,9 @@ if TYPE_CHECKING:
|
|
|
31
31
|
_LOGGER = logging.getLogger(__name__)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
class BaseBuilder(
|
|
34
|
+
class BaseBuilder(
|
|
35
|
+
Generic[ParameterListType], BaseModel
|
|
36
|
+
): # pylint: disable=too-many-instance-attributes
|
|
35
37
|
"""Base class for Operations and Request Builders"""
|
|
36
38
|
|
|
37
39
|
def __init__(
|
|
@@ -56,6 +58,7 @@ class BaseBuilder(Generic[ParameterListType], BaseModel):
|
|
|
56
58
|
self.group_name: str = yaml_data["groupName"]
|
|
57
59
|
self.is_overload: bool = yaml_data["isOverload"]
|
|
58
60
|
self.api_versions: List[str] = yaml_data["apiVersions"]
|
|
61
|
+
self.added_on: Optional[str] = yaml_data.get("addedOn")
|
|
59
62
|
|
|
60
63
|
if code_model.options["version_tolerant"] and yaml_data.get("abstract"):
|
|
61
64
|
_LOGGER.warning(
|
|
@@ -108,7 +108,6 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
|
|
|
108
108
|
f"{self.code_model.client.name}Configuration",
|
|
109
109
|
ImportType.LOCAL,
|
|
110
110
|
)
|
|
111
|
-
|
|
112
111
|
file_import.add_msrest_import(
|
|
113
112
|
self.code_model,
|
|
114
113
|
".." if async_mode else ".",
|
|
@@ -148,7 +147,10 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
|
|
|
148
147
|
ImportType.LOCAL,
|
|
149
148
|
)
|
|
150
149
|
|
|
151
|
-
if
|
|
150
|
+
if (
|
|
151
|
+
self.code_model.model_types
|
|
152
|
+
and self.code_model.options["models_mode"] == "msrest"
|
|
153
|
+
):
|
|
152
154
|
path_to_models = ".." if async_mode else "."
|
|
153
155
|
if len(self.code_model.model_types) != len(
|
|
154
156
|
self.code_model.public_model_types
|
|
@@ -167,7 +169,7 @@ class Client(_ClientConfigBase[ClientGlobalParameterList]):
|
|
|
167
169
|
file_import.add_submodule_import(
|
|
168
170
|
path_to_models, "models", ImportType.LOCAL
|
|
169
171
|
)
|
|
170
|
-
|
|
172
|
+
elif self.code_model.options["models_mode"] == "msrest":
|
|
171
173
|
# in this case, we have client_models = {} in the service client, which needs a type annotation
|
|
172
174
|
# this import will always be commented, so will always add it to the typing section
|
|
173
175
|
file_import.add_submodule_import(
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
7
|
-
|
|
7
|
+
import re
|
|
8
8
|
from autorest.codegen.models.imports import FileImport, ImportType
|
|
9
9
|
from .base_type import BaseType
|
|
10
10
|
|
|
@@ -67,9 +67,11 @@ class CombinedType(BaseType):
|
|
|
67
67
|
Special case for enum, for instance: Union[str, "EnumName"]
|
|
68
68
|
"""
|
|
69
69
|
kwargs["is_operation_file"] = True
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
inside_types = [type.type_annotation(**kwargs) for type in self.types]
|
|
71
|
+
|
|
72
|
+
# If the inside types has been a Union, peel first and then re-union
|
|
73
|
+
pattern = re.compile(r"Union\[.*\]")
|
|
74
|
+
return f'Union[{", ".join(map(lambda x: x[6: -1] if pattern.match(x) else x, inside_types))}]'
|
|
73
75
|
|
|
74
76
|
def get_json_template_representation(
|
|
75
77
|
self,
|
|
@@ -107,10 +107,12 @@ class EnumType(BaseType):
|
|
|
107
107
|
:rtype: str
|
|
108
108
|
"""
|
|
109
109
|
if self.code_model.options["models_mode"]:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
model_name = f"_models.{self.name}"
|
|
111
|
+
# we don't need quoted annotation in operation files, and need it in model folder files.
|
|
112
|
+
if not kwargs.get("is_operation_file", False):
|
|
113
|
+
model_name = f'"{model_name}"'
|
|
114
|
+
|
|
115
|
+
return f"Union[{self.value_type.type_annotation(**kwargs)}, {model_name}]"
|
|
114
116
|
return self.value_type.type_annotation(**kwargs)
|
|
115
117
|
|
|
116
118
|
def get_declaration(self, value: Any) -> str:
|
|
@@ -8,6 +8,7 @@ from typing import Any, Dict, List, Optional, TYPE_CHECKING, cast
|
|
|
8
8
|
|
|
9
9
|
from autorest.codegen.models.utils import add_to_pylint_disable
|
|
10
10
|
from .base_type import BaseType
|
|
11
|
+
from .constant_type import ConstantType
|
|
11
12
|
from .property import Property
|
|
12
13
|
from .imports import FileImport, ImportType, TypingSection
|
|
13
14
|
|
|
@@ -32,7 +33,9 @@ def _get_properties(type: "ModelType", properties: List[Property]) -> List[Prope
|
|
|
32
33
|
return properties
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
class ModelType(
|
|
36
|
+
class ModelType(
|
|
37
|
+
BaseType
|
|
38
|
+
): # pylint: disable=too-many-instance-attributes, too-many-public-methods
|
|
36
39
|
"""Represents a class ready to be serialized in Python.
|
|
37
40
|
|
|
38
41
|
:param str name: The name of the class.
|
|
@@ -71,14 +74,16 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
71
74
|
|
|
72
75
|
@property
|
|
73
76
|
def serialization_type(self) -> str:
|
|
74
|
-
if self.code_model.options["models_mode"]:
|
|
75
|
-
return (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
else f"{self.code_model.models_filename}.{self.name}"
|
|
79
|
-
)
|
|
77
|
+
if self.code_model.options["models_mode"] == "msrest":
|
|
78
|
+
return f"{'' if self.is_public else (self.code_model.models_filename + '.')}{self.name}"
|
|
79
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
80
|
+
return f"{'' if self.is_public else '_models.'}_models.{self.name}"
|
|
80
81
|
return "object"
|
|
81
82
|
|
|
83
|
+
@property
|
|
84
|
+
def is_polymorphic(self) -> bool:
|
|
85
|
+
return any(p.is_polymorphic for p in self.properties)
|
|
86
|
+
|
|
82
87
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
83
88
|
if self.code_model.options["models_mode"]:
|
|
84
89
|
is_operation_file = kwargs.pop("is_operation_file", False)
|
|
@@ -229,10 +234,26 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
229
234
|
except StopIteration:
|
|
230
235
|
return None
|
|
231
236
|
|
|
237
|
+
@property
|
|
238
|
+
def discriminator_property(self) -> Optional[Property]:
|
|
239
|
+
try:
|
|
240
|
+
return next(
|
|
241
|
+
p
|
|
242
|
+
for p in self.properties
|
|
243
|
+
if p.is_discriminator
|
|
244
|
+
and isinstance(p.type, ConstantType)
|
|
245
|
+
and p.type.value == self.discriminator_value
|
|
246
|
+
)
|
|
247
|
+
except StopIteration:
|
|
248
|
+
return None
|
|
249
|
+
|
|
232
250
|
@property
|
|
233
251
|
def instance_check_template(self) -> str:
|
|
234
|
-
|
|
252
|
+
models_mode = self.code_model.options["models_mode"]
|
|
253
|
+
if models_mode == "msrest":
|
|
235
254
|
return "isinstance({}, msrest.Model)"
|
|
255
|
+
if models_mode == "dpg":
|
|
256
|
+
return "isinstance({}, _model_base.Model)"
|
|
236
257
|
return "isinstance({}, MutableMapping)"
|
|
237
258
|
|
|
238
259
|
@property
|
|
@@ -257,7 +278,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes
|
|
|
257
278
|
file_import.add_submodule_import(
|
|
258
279
|
relative_path, "models", ImportType.LOCAL, alias="_models"
|
|
259
280
|
)
|
|
260
|
-
if self.code_model.options["models_mode"]:
|
|
281
|
+
if self.code_model.options["models_mode"] == "msrest":
|
|
261
282
|
return file_import
|
|
262
283
|
file_import.add_submodule_import(
|
|
263
284
|
"typing", "Any", ImportType.STDLIB, TypingSection.CONDITIONAL
|
|
@@ -154,6 +154,14 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
154
154
|
retval = self._response_docstring_helper("docstring_text", **kwargs)
|
|
155
155
|
if not self.code_model.options["version_tolerant"]:
|
|
156
156
|
retval += " or the result of cls(response)"
|
|
157
|
+
if self.code_model.options["models_mode"] == "dpg" and any(
|
|
158
|
+
isinstance(r.type, ModelType) for r in self.responses
|
|
159
|
+
):
|
|
160
|
+
r = next(r for r in self.responses if isinstance(r.type, ModelType))
|
|
161
|
+
type_name = getattr(r, "item_type", getattr(r, "type")).docstring_text(
|
|
162
|
+
**kwargs
|
|
163
|
+
)
|
|
164
|
+
retval += f". The {type_name} is compatible with MutableMapping"
|
|
157
165
|
return retval
|
|
158
166
|
|
|
159
167
|
def response_docstring_type(self, **kwargs) -> str:
|
|
@@ -209,6 +217,12 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
209
217
|
file_import.add_submodule_import(
|
|
210
218
|
"typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL
|
|
211
219
|
)
|
|
220
|
+
if self.added_on:
|
|
221
|
+
file_import.add_submodule_import(
|
|
222
|
+
f"{'.' if async_mode else ''}.._validation",
|
|
223
|
+
"api_version_validation",
|
|
224
|
+
ImportType.LOCAL,
|
|
225
|
+
)
|
|
212
226
|
return file_import
|
|
213
227
|
|
|
214
228
|
def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
|
|
@@ -244,6 +258,11 @@ class OperationBase( # pylint: disable=too-many-public-methods
|
|
|
244
258
|
for kwarg in kwargs_to_pop
|
|
245
259
|
)
|
|
246
260
|
|
|
261
|
+
@property
|
|
262
|
+
def need_validation(self) -> bool:
|
|
263
|
+
"""Whether we need parameter / operation validation. For API version."""
|
|
264
|
+
return bool(self.added_on) or any(p for p in self.parameters if p.added_on)
|
|
265
|
+
|
|
247
266
|
def get_request_builder_import(
|
|
248
267
|
self,
|
|
249
268
|
request_builder: Union[RequestBuilder, OverloadedRequestBuilder],
|
|
@@ -450,6 +469,19 @@ class Operation(OperationBase[Response]):
|
|
|
450
469
|
and not self.code_model.options["models_mode"]
|
|
451
470
|
):
|
|
452
471
|
file_import.add_submodule_import("typing", "cast", ImportType.STDLIB)
|
|
472
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
473
|
+
relative_path = "..." if async_mode else ".."
|
|
474
|
+
if self.parameters.has_body:
|
|
475
|
+
file_import.add_submodule_import(
|
|
476
|
+
f"{relative_path}_model_base", "AzureJSONEncoder", ImportType.LOCAL
|
|
477
|
+
)
|
|
478
|
+
file_import.add_import("json", ImportType.STDLIB)
|
|
479
|
+
if self.default_error_deserialization or any(
|
|
480
|
+
[r.type for r in self.responses]
|
|
481
|
+
):
|
|
482
|
+
file_import.add_submodule_import(
|
|
483
|
+
f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL
|
|
484
|
+
)
|
|
453
485
|
|
|
454
486
|
return file_import
|
|
455
487
|
|
|
@@ -66,6 +66,11 @@ class OperationGroup(BaseModel):
|
|
|
66
66
|
return " # type: ignore"
|
|
67
67
|
return ""
|
|
68
68
|
|
|
69
|
+
@property
|
|
70
|
+
def need_validation(self) -> bool:
|
|
71
|
+
"""Whether any of its operations need validation"""
|
|
72
|
+
return any(o for o in self.operations if o.need_validation)
|
|
73
|
+
|
|
69
74
|
def imports(self, async_mode: bool) -> FileImport:
|
|
70
75
|
file_import = FileImport()
|
|
71
76
|
|
|
@@ -75,13 +80,12 @@ class OperationGroup(BaseModel):
|
|
|
75
80
|
operation.imports(async_mode, relative_path=relative_path)
|
|
76
81
|
)
|
|
77
82
|
# for multiapi
|
|
78
|
-
if
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
83
|
+
if (
|
|
84
|
+
self.code_model.model_types or self.code_model.enums
|
|
85
|
+
) and self.code_model.options["models_mode"] == "msrest":
|
|
86
|
+
file_import.add_submodule_import(
|
|
87
|
+
relative_path, "models", ImportType.LOCAL, alias="_models"
|
|
88
|
+
)
|
|
85
89
|
if self.code_model.need_mixin_abc:
|
|
86
90
|
file_import.add_submodule_import(".._vendor", "MixinABC", ImportType.LOCAL)
|
|
87
91
|
if self.has_abstract_operations:
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
6
|
import abc
|
|
7
7
|
from enum import Enum, auto
|
|
8
|
+
import re
|
|
8
9
|
|
|
9
10
|
from typing import (
|
|
10
11
|
Dict,
|
|
@@ -21,6 +22,8 @@ from .imports import FileImport, ImportType
|
|
|
21
22
|
from .base_model import BaseModel
|
|
22
23
|
from .base_type import BaseType
|
|
23
24
|
from .constant_type import ConstantType
|
|
25
|
+
from .model_type import ModelType
|
|
26
|
+
from .combined_type import CombinedType
|
|
24
27
|
from .utils import add_to_description
|
|
25
28
|
|
|
26
29
|
if TYPE_CHECKING:
|
|
@@ -82,6 +85,7 @@ class _ParameterBase(
|
|
|
82
85
|
self.in_flattened_body: bool = self.yaml_data.get("inFlattenedBody", False)
|
|
83
86
|
self.grouper: bool = self.yaml_data.get("grouper", False)
|
|
84
87
|
self.check_client_input: bool = self.yaml_data.get("checkClientInput", False)
|
|
88
|
+
self.added_on: Optional[str] = self.yaml_data.get("addedOn")
|
|
85
89
|
|
|
86
90
|
@property
|
|
87
91
|
def constant(self) -> bool:
|
|
@@ -152,6 +156,12 @@ class _ParameterBase(
|
|
|
152
156
|
)
|
|
153
157
|
if self.optional and self.client_default_value is None:
|
|
154
158
|
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
|
|
159
|
+
if self.added_on:
|
|
160
|
+
file_import.add_submodule_import(
|
|
161
|
+
f"{'.' if async_mode else ''}.._validation",
|
|
162
|
+
"api_version_validation",
|
|
163
|
+
ImportType.LOCAL,
|
|
164
|
+
)
|
|
155
165
|
return file_import
|
|
156
166
|
|
|
157
167
|
@property
|
|
@@ -228,6 +238,40 @@ class BodyParameter(_BodyParameterBase):
|
|
|
228
238
|
type=code_model.lookup_type(id(yaml_data["type"])),
|
|
229
239
|
)
|
|
230
240
|
|
|
241
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
242
|
+
annotation = super().type_annotation(**kwargs)
|
|
243
|
+
model_seq = BodyParameter.get_model_seq(self.type)
|
|
244
|
+
if self.code_model.options["models_mode"] == "dpg" and model_seq >= 0:
|
|
245
|
+
pattern = re.compile(r"Union\[.*\]")
|
|
246
|
+
union_content = (
|
|
247
|
+
annotation[6:-1] if pattern.match(annotation) else annotation
|
|
248
|
+
)
|
|
249
|
+
items = union_content.split(", ")
|
|
250
|
+
items.insert(model_seq + 1, "JSON")
|
|
251
|
+
annotation = f'Union[{", ".join(items)}]'
|
|
252
|
+
return annotation
|
|
253
|
+
|
|
254
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
255
|
+
docstring = super().docstring_type(**kwargs)
|
|
256
|
+
model_seq = BodyParameter.get_model_seq(self.type)
|
|
257
|
+
if self.code_model.options["models_mode"] == "dpg" and model_seq >= 0:
|
|
258
|
+
items = docstring.split(" or ")
|
|
259
|
+
items.insert(model_seq + 1, "JSON")
|
|
260
|
+
docstring = " or ".join(items)
|
|
261
|
+
return docstring
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def get_model_seq(t: BaseType):
|
|
265
|
+
if isinstance(t, ModelType):
|
|
266
|
+
return 0
|
|
267
|
+
if isinstance(t, CombinedType):
|
|
268
|
+
sub_num = len(t.types)
|
|
269
|
+
for i in range(sub_num):
|
|
270
|
+
sub_seq = BodyParameter.get_model_seq(t.types[i])
|
|
271
|
+
if sub_seq >= 0:
|
|
272
|
+
return i + sub_seq
|
|
273
|
+
return -1
|
|
274
|
+
|
|
231
275
|
|
|
232
276
|
EntryBodyParameterType = TypeVar(
|
|
233
277
|
"EntryBodyParameterType", bound=Union[BodyParameter, "RequestBuilderBodyParameter"]
|
|
@@ -29,6 +29,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
29
29
|
self.type = type
|
|
30
30
|
self.optional: bool = self.yaml_data["optional"]
|
|
31
31
|
self.readonly: bool = self.yaml_data.get("readonly", False)
|
|
32
|
+
self.is_polymorphic: bool = self.yaml_data.get("isPolymorphic", False)
|
|
32
33
|
self.is_discriminator: bool = yaml_data.get("isDiscriminator", False)
|
|
33
34
|
self.client_default_value = yaml_data.get("clientDefaultValue", None)
|
|
34
35
|
if self.client_default_value is None:
|
|
@@ -45,7 +46,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
45
46
|
def description(self, *, is_operation_file: bool) -> str:
|
|
46
47
|
from .model_type import ModelType
|
|
47
48
|
|
|
48
|
-
description = self.yaml_data
|
|
49
|
+
description = self.yaml_data.get("description", "")
|
|
49
50
|
if not (self.optional or self.client_default_value):
|
|
50
51
|
description = add_to_description(description, "Required.")
|
|
51
52
|
# don't want model type documentation as part of property doc
|
|
@@ -124,20 +125,6 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
124
125
|
retval.update(self.type.validation or {})
|
|
125
126
|
return retval or None
|
|
126
127
|
|
|
127
|
-
@property
|
|
128
|
-
def attribute_map(self) -> str:
|
|
129
|
-
if self.flattened_names:
|
|
130
|
-
attribute_key = ".".join(
|
|
131
|
-
n.replace(".", "\\\\.") for n in self.flattened_names
|
|
132
|
-
)
|
|
133
|
-
else:
|
|
134
|
-
attribute_key = self.rest_api_name.replace(".", "\\\\.")
|
|
135
|
-
if self.type.xml_serialization_ctxt:
|
|
136
|
-
xml_metadata = f", 'xml': {{{self.type.xml_serialization_ctxt}}}"
|
|
137
|
-
else:
|
|
138
|
-
xml_metadata = ""
|
|
139
|
-
return f'"{self.client_name}": {{"key": "{attribute_key}", "type": "{self.serialization_type}"{xml_metadata}}},'
|
|
140
|
-
|
|
141
128
|
def imports(self) -> FileImport:
|
|
142
129
|
from .model_type import ModelType
|
|
143
130
|
|
|
@@ -152,6 +139,12 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
152
139
|
TypingSection.TYPING,
|
|
153
140
|
alias="_models",
|
|
154
141
|
)
|
|
142
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
143
|
+
file_import.add_submodule_import(
|
|
144
|
+
".._model_base",
|
|
145
|
+
"rest_discriminator" if self.is_discriminator else "rest_field",
|
|
146
|
+
ImportType.LOCAL,
|
|
147
|
+
)
|
|
155
148
|
return file_import
|
|
156
149
|
|
|
157
150
|
@classmethod
|
|
@@ -24,8 +24,10 @@ class RequestBuilderBodyParameter(BodyParameter):
|
|
|
24
24
|
|
|
25
25
|
def __init__(self, *args, **kwargs) -> None:
|
|
26
26
|
super().__init__(*args, **kwargs)
|
|
27
|
-
if
|
|
28
|
-
|
|
27
|
+
if (
|
|
28
|
+
isinstance(self.type, (BinaryType, StringType))
|
|
29
|
+
or any("xml" in ct for ct in self.content_types)
|
|
30
|
+
or self.code_model.options["models_mode"] == "dpg"
|
|
29
31
|
):
|
|
30
32
|
self.client_name = "content"
|
|
31
33
|
else:
|
|
@@ -16,7 +16,7 @@ from ..models import TokenCredentialType
|
|
|
16
16
|
from .enum_serializer import EnumSerializer
|
|
17
17
|
from .general_serializer import GeneralSerializer
|
|
18
18
|
from .model_init_serializer import ModelInitSerializer
|
|
19
|
-
from .model_serializer import
|
|
19
|
+
from .model_serializer import DpgModelSerializer, MsrestModelSerializer
|
|
20
20
|
from .operations_init_serializer import OperationsInitSerializer
|
|
21
21
|
from .operation_groups_serializer import OperationGroupsSerializer
|
|
22
22
|
from .metadata_serializer import MetadataSerializer
|
|
@@ -215,10 +215,15 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
215
215
|
) -> None:
|
|
216
216
|
# Write the models folder
|
|
217
217
|
models_path = namespace_path / Path("models")
|
|
218
|
+
serializer = (
|
|
219
|
+
DpgModelSerializer
|
|
220
|
+
if self.code_model.options["models_mode"] == "dpg"
|
|
221
|
+
else MsrestModelSerializer
|
|
222
|
+
)
|
|
218
223
|
if self.code_model.model_types:
|
|
219
224
|
self.write_file(
|
|
220
225
|
models_path / Path(f"{self.code_model.models_filename}.py"),
|
|
221
|
-
|
|
226
|
+
serializer(code_model=self.code_model, env=env).serialize(),
|
|
222
227
|
)
|
|
223
228
|
if self.code_model.enums:
|
|
224
229
|
self.write_file(
|
|
@@ -427,6 +432,17 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
|
|
|
427
432
|
namespace_path / Path("_serialization.py"),
|
|
428
433
|
general_serializer.serialize_serialization_file(),
|
|
429
434
|
)
|
|
435
|
+
if self.code_model.options["models_mode"] == "dpg":
|
|
436
|
+
self.write_file(
|
|
437
|
+
namespace_path / Path("_model_base.py"),
|
|
438
|
+
general_serializer.serialize_model_base_file(),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if any(og for og in self.code_model.operation_groups if og.need_validation):
|
|
442
|
+
self.write_file(
|
|
443
|
+
namespace_path / Path("_validation.py"),
|
|
444
|
+
general_serializer.serialize_validation_file(),
|
|
445
|
+
)
|
|
430
446
|
|
|
431
447
|
# Write the config file
|
|
432
448
|
if self.code_model.request_builders:
|