@autorest/python 6.45.0 → 6.46.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/generator/build/lib/pygen/codegen/models/code_model.py +4 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +8 -1
- package/generator/build/lib/pygen/codegen/models/list_type.py +6 -2
- package/generator/build/lib/pygen/codegen/models/model_type.py +2 -2
- package/generator/build/lib/pygen/codegen/models/operation.py +10 -1
- package/generator/build/lib/pygen/codegen/models/operation_group.py +3 -1
- package/generator/build/lib/pygen/codegen/models/request_builder.py +20 -3
- package/generator/build/lib/pygen/codegen/models/response.py +2 -2
- package/generator/build/lib/pygen/codegen/models/utils.py +7 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +26 -12
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +1 -2
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +1 -1
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +3 -0
- package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +3 -1
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +65 -9
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +14 -3
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/codegen/models/code_model.py +4 -0
- package/generator/pygen/codegen/models/enum_type.py +8 -1
- package/generator/pygen/codegen/models/list_type.py +6 -2
- package/generator/pygen/codegen/models/model_type.py +2 -2
- package/generator/pygen/codegen/models/operation.py +10 -1
- package/generator/pygen/codegen/models/operation_group.py +3 -1
- package/generator/pygen/codegen/models/request_builder.py +20 -3
- package/generator/pygen/codegen/models/response.py +2 -2
- package/generator/pygen/codegen/models/utils.py +7 -0
- package/generator/pygen/codegen/serializers/builder_serializer.py +26 -12
- package/generator/pygen/codegen/serializers/client_serializer.py +1 -2
- package/generator/pygen/codegen/serializers/general_serializer.py +1 -1
- package/generator/pygen/codegen/serializers/model_serializer.py +3 -0
- package/generator/pygen/codegen/templates/enum.py.jinja2 +3 -1
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +65 -9
- package/generator/pygen/codegen/templates/serialization.py.jinja2 +14 -3
- package/package.json +2 -2
- package/scripts/__pycache__/venvtools.cpython-310.pyc +0 -0
|
@@ -491,6 +491,10 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
491
491
|
def has_operation_named_list(self) -> bool:
|
|
492
492
|
return any(o.name.lower() == "list" for c in self.clients for og in c.operation_groups for o in og.operations)
|
|
493
493
|
|
|
494
|
+
@property
|
|
495
|
+
def has_property_named_list(self) -> bool:
|
|
496
|
+
return any(p.client_name.lower() == "list" for m in self.model_types for p in m.properties)
|
|
497
|
+
|
|
494
498
|
@property
|
|
495
499
|
def has_padded_model_property(self) -> bool:
|
|
496
500
|
for model_type in self.model_types:
|
|
@@ -7,7 +7,8 @@ from typing import Any, TYPE_CHECKING, Optional, cast
|
|
|
7
7
|
|
|
8
8
|
from .base import BaseType
|
|
9
9
|
from .imports import FileImport, ImportType, TypingSection
|
|
10
|
-
from .utils import NamespaceType
|
|
10
|
+
from .utils import NamespaceType, add_to_pylint_disable
|
|
11
|
+
from ...utils import NAME_LENGTH_LIMIT
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
@@ -189,6 +190,12 @@ class EnumType(BaseType):
|
|
|
189
190
|
return f"Union[{self.value_type.type_annotation(**kwargs)}, {model_name}]"
|
|
190
191
|
return self.value_type.type_annotation(**kwargs)
|
|
191
192
|
|
|
193
|
+
def pylint_disable(self) -> str:
|
|
194
|
+
retval: str = ""
|
|
195
|
+
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
196
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
197
|
+
return retval
|
|
198
|
+
|
|
192
199
|
def get_declaration(self, value: Any) -> str:
|
|
193
200
|
return self.value_type.get_declaration(value)
|
|
194
201
|
|
|
@@ -41,8 +41,12 @@ class ListType(BaseType):
|
|
|
41
41
|
# this means we're version tolerant XML, we just return the XML element
|
|
42
42
|
return self.element_type.type_annotation(**kwargs)
|
|
43
43
|
|
|
44
|
-
# if there is a function named `list` we have to make sure there's no conflict with the built-in `list`
|
|
45
|
-
|
|
44
|
+
# if there is a function/property named `list` we have to make sure there's no conflict with the built-in `list`
|
|
45
|
+
is_operation_file = kwargs.get("is_operation_file", False)
|
|
46
|
+
use_list_import = (self.code_model.has_operation_named_list and is_operation_file) or (
|
|
47
|
+
self.code_model.has_property_named_list and not is_operation_file
|
|
48
|
+
)
|
|
49
|
+
list_type = "List" if use_list_import else "list"
|
|
46
50
|
return f"{list_type}[{self.element_type.type_annotation(**kwargs)}]"
|
|
47
51
|
|
|
48
52
|
def description(self, *, is_operation_file: bool) -> str:
|
|
@@ -7,7 +7,7 @@ from enum import Enum
|
|
|
7
7
|
from collections import OrderedDict
|
|
8
8
|
from typing import Any, Optional, TYPE_CHECKING, cast
|
|
9
9
|
import sys
|
|
10
|
-
from .utils import add_to_pylint_disable, NamespaceType
|
|
10
|
+
from .utils import add_to_pylint_disable, NamespaceType, LOCALS_LENGTH_LIMIT
|
|
11
11
|
from .base import BaseType
|
|
12
12
|
from .constant_type import ConstantType
|
|
13
13
|
from .property import Property
|
|
@@ -244,7 +244,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes, too-
|
|
|
244
244
|
@property
|
|
245
245
|
def init_pylint_disable(self) -> str:
|
|
246
246
|
retval: str = ""
|
|
247
|
-
if len(self.properties) >
|
|
247
|
+
if len(self.properties) > LOCALS_LENGTH_LIMIT:
|
|
248
248
|
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
249
249
|
return retval
|
|
250
250
|
|
|
@@ -16,7 +16,7 @@ from typing import (
|
|
|
16
16
|
|
|
17
17
|
from .request_builder_parameter import RequestBuilderParameter
|
|
18
18
|
|
|
19
|
-
from .utils import OrderedSet, add_to_pylint_disable
|
|
19
|
+
from .utils import OrderedSet, add_to_pylint_disable, LOCALS_LENGTH_LIMIT, OPERATION_BODY_VARIABLES_LENGTH
|
|
20
20
|
from .base_builder import BaseBuilder
|
|
21
21
|
from .imports import FileImport, ImportType, TypingSection
|
|
22
22
|
from .response import (
|
|
@@ -143,6 +143,12 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
143
143
|
retval = add_to_pylint_disable(retval, "inconsistent-return-statements")
|
|
144
144
|
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
145
145
|
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
146
|
+
method_params = self.parameters.method
|
|
147
|
+
if self.is_overload and len(method_params) > LOCALS_LENGTH_LIMIT:
|
|
148
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
149
|
+
elif not self.is_overload and len(method_params) > (LOCALS_LENGTH_LIMIT - OPERATION_BODY_VARIABLES_LENGTH):
|
|
150
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
151
|
+
|
|
146
152
|
return retval
|
|
147
153
|
|
|
148
154
|
def cls_type_annotation(self, *, async_mode: bool, **kwargs: Any) -> str:
|
|
@@ -408,6 +414,9 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
408
414
|
file_import.merge(self.get_request_builder_import(self.request_builder, async_mode, serialize_namespace))
|
|
409
415
|
if self.overloads:
|
|
410
416
|
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
417
|
+
for overload in self.overloads:
|
|
418
|
+
if overload.parameters.has_body:
|
|
419
|
+
file_import.merge(overload.parameters.body_parameter.type.imports(**kwargs))
|
|
411
420
|
if self.code_model.options["models-mode"] == "dpg":
|
|
412
421
|
relative_path = self.code_model.get_relative_import_path(
|
|
413
422
|
serialize_namespace, module_name="_utils.model_base"
|
|
@@ -93,7 +93,9 @@ class OperationGroup(BaseModel):
|
|
|
93
93
|
@property
|
|
94
94
|
def need_validation(self) -> bool:
|
|
95
95
|
"""Whether any of its operations need validation"""
|
|
96
|
-
return any(o for o in self.operations if o.need_validation)
|
|
96
|
+
return any(o for o in self.operations if o.need_validation) or any(
|
|
97
|
+
og for og in self.operation_groups if og.need_validation
|
|
98
|
+
)
|
|
97
99
|
|
|
98
100
|
def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
|
|
99
101
|
file_import = FileImport(self.code_model)
|
|
@@ -16,11 +16,12 @@ from typing import (
|
|
|
16
16
|
from abc import abstractmethod
|
|
17
17
|
|
|
18
18
|
from .base_builder import BaseBuilder
|
|
19
|
-
from .utils import add_to_pylint_disable
|
|
19
|
+
from .utils import add_to_pylint_disable, LOCALS_LENGTH_LIMIT, REQUEST_BUILDER_BODY_VARIABLES_LENGTH
|
|
20
20
|
from .parameter_list import (
|
|
21
21
|
RequestBuilderParameterList,
|
|
22
22
|
OverloadedRequestBuilderParameterList,
|
|
23
23
|
)
|
|
24
|
+
from .parameter import ParameterLocation
|
|
24
25
|
from .imports import FileImport, ImportType, TypingSection, MsrestImportType
|
|
25
26
|
from ...utils import NAME_LENGTH_LIMIT
|
|
26
27
|
|
|
@@ -67,9 +68,25 @@ class RequestBuilderBase(BaseBuilder[ParameterListType, Sequence["RequestBuilder
|
|
|
67
68
|
return self.yaml_data.get("discriminator") in ("lro", "lropaging")
|
|
68
69
|
|
|
69
70
|
def pylint_disable(self, async_mode: bool) -> str:
|
|
71
|
+
retval = ""
|
|
70
72
|
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
74
|
+
method_params = self.parameters.method
|
|
75
|
+
if len(method_params) > LOCALS_LENGTH_LIMIT - REQUEST_BUILDER_BODY_VARIABLES_LENGTH:
|
|
76
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
77
|
+
if (
|
|
78
|
+
len(
|
|
79
|
+
[
|
|
80
|
+
p
|
|
81
|
+
for p in method_params
|
|
82
|
+
if p.optional and p.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
> LOCALS_LENGTH_LIMIT
|
|
86
|
+
):
|
|
87
|
+
retval = add_to_pylint_disable(retval, "too-many-statements")
|
|
88
|
+
retval = add_to_pylint_disable(retval, "too-many-branches")
|
|
89
|
+
return retval
|
|
73
90
|
|
|
74
91
|
def response_type_annotation(self, **kwargs) -> str:
|
|
75
92
|
return "HttpRequest"
|
|
@@ -233,7 +233,7 @@ class LROResponse(Response):
|
|
|
233
233
|
|
|
234
234
|
def get_no_polling_method(self, async_mode: bool) -> str:
|
|
235
235
|
"""Get the default no polling method"""
|
|
236
|
-
return self.get_no_polling_method_path(async_mode).
|
|
236
|
+
return self.get_no_polling_method_path(async_mode).rsplit(".", maxsplit=1)[-1]
|
|
237
237
|
|
|
238
238
|
@staticmethod
|
|
239
239
|
def get_base_polling_method_path(async_mode: bool) -> str:
|
|
@@ -242,7 +242,7 @@ class LROResponse(Response):
|
|
|
242
242
|
|
|
243
243
|
def get_base_polling_method(self, async_mode: bool) -> str:
|
|
244
244
|
"""Get the base polling method."""
|
|
245
|
-
return self.get_base_polling_method_path(async_mode).
|
|
245
|
+
return self.get_base_polling_method_path(async_mode).rsplit(".", maxsplit=1)[-1]
|
|
246
246
|
|
|
247
247
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
248
248
|
return f"{self.get_poller(kwargs.get('async_mode', False))}[{super().type_annotation(**kwargs)}]"
|
|
@@ -30,3 +30,10 @@ class NamespaceType(str, Enum):
|
|
|
30
30
|
OPERATION = "operation"
|
|
31
31
|
CLIENT = "client"
|
|
32
32
|
TYPES_FILE = "types_file"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
LOCALS_LENGTH_LIMIT = 25
|
|
36
|
+
|
|
37
|
+
REQUEST_BUILDER_BODY_VARIABLES_LENGTH = 6 # how many body variables are present in a request builder
|
|
38
|
+
|
|
39
|
+
OPERATION_BODY_VARIABLES_LENGTH = 14 # how many body variables are present in an operation
|
|
@@ -403,7 +403,12 @@ class RequestBuilderSerializer(_BuilderBaseSerializer[RequestBuilderType]):
|
|
|
403
403
|
builder: RequestBuilderType,
|
|
404
404
|
) -> list[str]:
|
|
405
405
|
def _get_value(param):
|
|
406
|
-
|
|
406
|
+
if param.constant:
|
|
407
|
+
declaration = param.get_declaration()
|
|
408
|
+
elif param.client_default_value_declaration is not None:
|
|
409
|
+
declaration = param.client_default_value_declaration
|
|
410
|
+
else:
|
|
411
|
+
declaration = None
|
|
407
412
|
if param.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]:
|
|
408
413
|
kwarg_dict = "headers" if param.location == ParameterLocation.HEADER else "params"
|
|
409
414
|
return f"_{kwarg_dict}.pop('{param.wire_name}', {declaration})"
|
|
@@ -776,8 +781,14 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
776
781
|
client_names = [
|
|
777
782
|
overload.request_builder.parameters.body_parameter.client_name for overload in builder.overloads
|
|
778
783
|
]
|
|
779
|
-
|
|
780
|
-
|
|
784
|
+
all_dpg_model_overloads = False
|
|
785
|
+
if self.code_model.options["models-mode"] == "dpg" and builder.overloads:
|
|
786
|
+
all_dpg_model_overloads = all(
|
|
787
|
+
isinstance(o.parameters.body_parameter.type, DPGModelType) for o in builder.overloads
|
|
788
|
+
)
|
|
789
|
+
if not all_dpg_model_overloads:
|
|
790
|
+
for v in sorted(set(client_names), key=client_names.index):
|
|
791
|
+
retval.append(f"_{v} = None")
|
|
781
792
|
try:
|
|
782
793
|
# if there is a binary overload, we do a binary check first.
|
|
783
794
|
binary_overload = cast(
|
|
@@ -803,17 +814,20 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
803
814
|
f'"{other_overload.parameters.body_parameter.default_content_type}"{check_body_suffix}'
|
|
804
815
|
)
|
|
805
816
|
except StopIteration:
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if body_param.default_content_type and not same_content_type:
|
|
817
|
+
if all_dpg_model_overloads:
|
|
818
|
+
retval.extend(f"{l}" for l in self._create_body_parameter(cast(OperationType, builder.overloads[0])))
|
|
819
|
+
else:
|
|
820
|
+
for idx, overload in enumerate(builder.overloads):
|
|
821
|
+
if_statement = "if" if idx == 0 else "elif"
|
|
822
|
+
body_param = overload.parameters.body_parameter
|
|
813
823
|
retval.append(
|
|
814
|
-
f
|
|
824
|
+
f"{if_statement} {body_param.type.instance_check_template.format(body_param.client_name)}:"
|
|
815
825
|
)
|
|
816
|
-
|
|
826
|
+
if body_param.default_content_type and not same_content_type:
|
|
827
|
+
retval.append(
|
|
828
|
+
f' content_type = content_type or "{body_param.default_content_type}"{check_body_suffix}'
|
|
829
|
+
)
|
|
830
|
+
retval.extend(f" {l}" for l in self._create_body_parameter(cast(OperationType, overload)))
|
|
817
831
|
return retval
|
|
818
832
|
|
|
819
833
|
def _create_request_builder_call(
|
|
@@ -182,11 +182,10 @@ class ClientSerializer:
|
|
|
182
182
|
retval.append("self._serialize.client_side_validation = False")
|
|
183
183
|
operation_groups = [og for og in self.client.operation_groups if not og.is_mixin]
|
|
184
184
|
for og in operation_groups:
|
|
185
|
-
api_version = ""
|
|
186
185
|
retval.extend(
|
|
187
186
|
[
|
|
188
187
|
f"self.{og.property_name} = {og.class_name}(",
|
|
189
|
-
|
|
188
|
+
" self._client, self._config, self._serialize, self._deserialize",
|
|
190
189
|
")",
|
|
191
190
|
]
|
|
192
191
|
)
|
|
@@ -285,6 +285,9 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
285
285
|
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
286
286
|
file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
|
|
287
287
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
288
|
+
# if there is a property named `list` we have to make sure there's no conflict with the built-in `list`
|
|
289
|
+
if self.code_model.has_property_named_list:
|
|
290
|
+
file_import.define_mypy_type("List", "list")
|
|
288
291
|
return file_import
|
|
289
292
|
|
|
290
293
|
def declare_model(self, model: ModelType) -> str:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
class {{ enum.name }}({{
|
|
2
|
+
class {{ enum.name }}({{enum.pylint_disable()}}
|
|
3
|
+
{{ enum.value_type.type_annotation(is_operation_file=False) }}, Enum, metaclass=CaseInsensitiveEnumMeta
|
|
4
|
+
):
|
|
3
5
|
{% if enum.yaml_data.get("description") %}
|
|
4
6
|
"""{{ op_tools.wrap_string(enum.yaml_data["description"], "\n ") }}
|
|
5
7
|
"""
|
|
@@ -36,6 +36,7 @@ __all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"]
|
|
|
36
36
|
|
|
37
37
|
TZ_UTC = timezone.utc
|
|
38
38
|
_T = typing.TypeVar("_T")
|
|
39
|
+
_NONE_TYPE = type(None)
|
|
39
40
|
|
|
40
41
|
{% if code_model.has_external_type %}
|
|
41
42
|
TYPE_HANDLER_REGISTRY = TypeHandlerRegistry()
|
|
@@ -223,7 +224,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
223
224
|
test_utc = date_obj.utctimetuple()
|
|
224
225
|
if test_utc.tm_year > 9999 or test_utc.tm_year < 1:
|
|
225
226
|
raise OverflowError("Hit max or min date")
|
|
226
|
-
return date_obj
|
|
227
|
+
return date_obj # type: ignore[no-any-return]
|
|
227
228
|
|
|
228
229
|
|
|
229
230
|
def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime:
|
|
@@ -277,7 +278,7 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
|
|
|
277
278
|
"""
|
|
278
279
|
if isinstance(attr, time):
|
|
279
280
|
return attr
|
|
280
|
-
return isodate.parse_time(attr)
|
|
281
|
+
return isodate.parse_time(attr) # type: ignore[no-any-return]
|
|
281
282
|
|
|
282
283
|
|
|
283
284
|
def _deserialize_bytes(attr):
|
|
@@ -382,9 +383,39 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
382
383
|
return key in self._data
|
|
383
384
|
|
|
384
385
|
def __getitem__(self, key: str) -> typing.Any:
|
|
386
|
+
# If this key has been deserialized (for mutable types), we need to handle serialization
|
|
387
|
+
if hasattr(self, "_attr_to_rest_field"):
|
|
388
|
+
cache_attr = f"_deserialized_{key}"
|
|
389
|
+
if hasattr(self, cache_attr):
|
|
390
|
+
rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
|
|
391
|
+
if rf:
|
|
392
|
+
value = self._data.get(key)
|
|
393
|
+
if isinstance(value, (dict, list, set)):
|
|
394
|
+
# For mutable types, serialize and return
|
|
395
|
+
# But also update _data with serialized form and clear flag
|
|
396
|
+
# so mutations via this returned value affect _data
|
|
397
|
+
serialized = _serialize(value, rf._format)
|
|
398
|
+
# If serialized form is same type (no transformation needed),
|
|
399
|
+
# return _data directly so mutations work
|
|
400
|
+
if isinstance(serialized, type(value)) and serialized == value:
|
|
401
|
+
return self._data.get(key)
|
|
402
|
+
# Otherwise return serialized copy and clear flag
|
|
403
|
+
try:
|
|
404
|
+
object.__delattr__(self, cache_attr)
|
|
405
|
+
except AttributeError:
|
|
406
|
+
pass
|
|
407
|
+
# Store serialized form back
|
|
408
|
+
self._data[key] = serialized
|
|
409
|
+
return serialized
|
|
385
410
|
return self._data.__getitem__(key)
|
|
386
411
|
|
|
387
412
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
413
|
+
# Clear any cached deserialized value when setting through dictionary access
|
|
414
|
+
cache_attr = f"_deserialized_{key}"
|
|
415
|
+
try:
|
|
416
|
+
object.__delattr__(self, cache_attr)
|
|
417
|
+
except AttributeError:
|
|
418
|
+
pass
|
|
388
419
|
self._data.__setitem__(key, value)
|
|
389
420
|
|
|
390
421
|
def __delitem__(self, key: str) -> None:
|
|
@@ -886,16 +917,16 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
|
|
|
886
917
|
|
|
887
918
|
# is it optional?
|
|
888
919
|
try:
|
|
889
|
-
if any(a for a in annotation.__args__
|
|
920
|
+
if any(a is _NONE_TYPE for a in annotation.__args__): # pyright: ignore
|
|
890
921
|
if len(annotation.__args__) <= 2: # pyright: ignore
|
|
891
922
|
if_obj_deserializer = _get_deserialize_callable_from_annotation(
|
|
892
|
-
next(a for a in annotation.__args__ if a
|
|
923
|
+
next(a for a in annotation.__args__ if a is not _NONE_TYPE), module, rf # pyright: ignore
|
|
893
924
|
)
|
|
894
925
|
|
|
895
926
|
return functools.partial(_deserialize_with_optional, if_obj_deserializer)
|
|
896
927
|
# the type is Optional[Union[...]], we need to remove the None type from the Union
|
|
897
928
|
annotation_copy = copy.copy(annotation)
|
|
898
|
-
annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a
|
|
929
|
+
annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a is not _NONE_TYPE] # pyright: ignore
|
|
899
930
|
return _get_deserialize_callable_from_annotation(annotation_copy, module, rf)
|
|
900
931
|
except AttributeError:
|
|
901
932
|
pass
|
|
@@ -1093,14 +1124,37 @@ class _RestField:
|
|
|
1093
1124
|
def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
|
|
1094
1125
|
# by this point, type and rest_name will have a value bc we default
|
|
1095
1126
|
# them in __new__ of the Model class
|
|
1096
|
-
|
|
1127
|
+
# Use _data.get() directly to avoid triggering __getitem__ which clears the cache
|
|
1128
|
+
item = obj._data.get(self._rest_name)
|
|
1097
1129
|
if item is None:
|
|
1098
1130
|
return item
|
|
1099
1131
|
if self._is_model:
|
|
1100
1132
|
return item
|
|
1101
|
-
|
|
1133
|
+
|
|
1134
|
+
# For mutable types, we want mutations to directly affect _data
|
|
1135
|
+
# Check if we've already deserialized this value
|
|
1136
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1137
|
+
if hasattr(obj, cache_attr):
|
|
1138
|
+
# Return the value from _data directly (it's been deserialized in place)
|
|
1139
|
+
return obj._data.get(self._rest_name)
|
|
1140
|
+
|
|
1141
|
+
deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
|
|
1142
|
+
|
|
1143
|
+
# For mutable types, store the deserialized value back in _data
|
|
1144
|
+
# so mutations directly affect _data
|
|
1145
|
+
if isinstance(deserialized, (dict, list, set)):
|
|
1146
|
+
obj._data[self._rest_name] = deserialized
|
|
1147
|
+
object.__setattr__(obj, cache_attr, True) # Mark as deserialized
|
|
1148
|
+
return deserialized
|
|
1149
|
+
|
|
1150
|
+
return deserialized
|
|
1102
1151
|
|
|
1103
1152
|
def __set__(self, obj: Model, value) -> None:
|
|
1153
|
+
# Clear the cached deserialized object when setting a new value
|
|
1154
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1155
|
+
if hasattr(obj, cache_attr):
|
|
1156
|
+
object.__delattr__(obj, cache_attr)
|
|
1157
|
+
|
|
1104
1158
|
if value is None:
|
|
1105
1159
|
# we want to wipe out entries if users set attr to None
|
|
1106
1160
|
try:
|
|
@@ -1274,7 +1328,7 @@ def _get_wrapped_element(
|
|
|
1274
1328
|
_get_element(v, exclude_readonly, meta, wrapped_element)
|
|
1275
1329
|
else:
|
|
1276
1330
|
wrapped_element.text = _get_primitive_type_value(v)
|
|
1277
|
-
return wrapped_element
|
|
1331
|
+
return wrapped_element # type: ignore[no-any-return]
|
|
1278
1332
|
|
|
1279
1333
|
|
|
1280
1334
|
def _get_primitive_type_value(v) -> str:
|
|
@@ -1287,7 +1341,9 @@ def _get_primitive_type_value(v) -> str:
|
|
|
1287
1341
|
return str(v)
|
|
1288
1342
|
|
|
1289
1343
|
|
|
1290
|
-
def _create_xml_element(
|
|
1344
|
+
def _create_xml_element(
|
|
1345
|
+
tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None
|
|
1346
|
+
) -> ET.Element:
|
|
1291
1347
|
if prefix and ns:
|
|
1292
1348
|
ET.register_namespace(prefix, ns)
|
|
1293
1349
|
if ns:
|
|
@@ -817,13 +817,20 @@ class Serializer: # pylint: disable=too-many-public-methods
|
|
|
817
817
|
:param str data_type: Type of object in the iterable.
|
|
818
818
|
:rtype: str, int, float, bool
|
|
819
819
|
:return: serialized object
|
|
820
|
+
:raises TypeError: raise if data_type is not one of str, int, float, bool.
|
|
820
821
|
"""
|
|
821
822
|
custom_serializer = cls._get_custom_serializers(data_type, **kwargs)
|
|
822
823
|
if custom_serializer:
|
|
823
824
|
return custom_serializer(data)
|
|
824
825
|
if data_type == "str":
|
|
825
826
|
return cls.serialize_unicode(data)
|
|
826
|
-
|
|
827
|
+
if data_type == "int":
|
|
828
|
+
return int(data)
|
|
829
|
+
if data_type == "float":
|
|
830
|
+
return float(data)
|
|
831
|
+
if data_type == "bool":
|
|
832
|
+
return bool(data)
|
|
833
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
827
834
|
|
|
828
835
|
@classmethod
|
|
829
836
|
def serialize_unicode(cls, data):
|
|
@@ -1753,7 +1760,7 @@ class Deserializer:
|
|
|
1753
1760
|
:param str data_type: deserialization data type.
|
|
1754
1761
|
:return: Deserialized basic type.
|
|
1755
1762
|
:rtype: str, int, float or bool
|
|
1756
|
-
:raises TypeError: if string format is not valid.
|
|
1763
|
+
:raises TypeError: if string format is not valid or data_type is not one of str, int, float, bool.
|
|
1757
1764
|
"""
|
|
1758
1765
|
# If we're here, data is supposed to be a basic type.
|
|
1759
1766
|
# If it's still an XML node, take the text
|
|
@@ -1779,7 +1786,11 @@ class Deserializer:
|
|
|
1779
1786
|
|
|
1780
1787
|
if data_type == "str":
|
|
1781
1788
|
return self.deserialize_unicode(attr)
|
|
1782
|
-
|
|
1789
|
+
if data_type == "int":
|
|
1790
|
+
return int(attr)
|
|
1791
|
+
if data_type == "float":
|
|
1792
|
+
return float(attr)
|
|
1793
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
1783
1794
|
|
|
1784
1795
|
@staticmethod
|
|
1785
1796
|
def deserialize_unicode(data):
|
|
Binary file
|
|
@@ -491,6 +491,10 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
491
491
|
def has_operation_named_list(self) -> bool:
|
|
492
492
|
return any(o.name.lower() == "list" for c in self.clients for og in c.operation_groups for o in og.operations)
|
|
493
493
|
|
|
494
|
+
@property
|
|
495
|
+
def has_property_named_list(self) -> bool:
|
|
496
|
+
return any(p.client_name.lower() == "list" for m in self.model_types for p in m.properties)
|
|
497
|
+
|
|
494
498
|
@property
|
|
495
499
|
def has_padded_model_property(self) -> bool:
|
|
496
500
|
for model_type in self.model_types:
|
|
@@ -7,7 +7,8 @@ from typing import Any, TYPE_CHECKING, Optional, cast
|
|
|
7
7
|
|
|
8
8
|
from .base import BaseType
|
|
9
9
|
from .imports import FileImport, ImportType, TypingSection
|
|
10
|
-
from .utils import NamespaceType
|
|
10
|
+
from .utils import NamespaceType, add_to_pylint_disable
|
|
11
|
+
from ...utils import NAME_LENGTH_LIMIT
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
@@ -189,6 +190,12 @@ class EnumType(BaseType):
|
|
|
189
190
|
return f"Union[{self.value_type.type_annotation(**kwargs)}, {model_name}]"
|
|
190
191
|
return self.value_type.type_annotation(**kwargs)
|
|
191
192
|
|
|
193
|
+
def pylint_disable(self) -> str:
|
|
194
|
+
retval: str = ""
|
|
195
|
+
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
196
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
197
|
+
return retval
|
|
198
|
+
|
|
192
199
|
def get_declaration(self, value: Any) -> str:
|
|
193
200
|
return self.value_type.get_declaration(value)
|
|
194
201
|
|
|
@@ -41,8 +41,12 @@ class ListType(BaseType):
|
|
|
41
41
|
# this means we're version tolerant XML, we just return the XML element
|
|
42
42
|
return self.element_type.type_annotation(**kwargs)
|
|
43
43
|
|
|
44
|
-
# if there is a function named `list` we have to make sure there's no conflict with the built-in `list`
|
|
45
|
-
|
|
44
|
+
# if there is a function/property named `list` we have to make sure there's no conflict with the built-in `list`
|
|
45
|
+
is_operation_file = kwargs.get("is_operation_file", False)
|
|
46
|
+
use_list_import = (self.code_model.has_operation_named_list and is_operation_file) or (
|
|
47
|
+
self.code_model.has_property_named_list and not is_operation_file
|
|
48
|
+
)
|
|
49
|
+
list_type = "List" if use_list_import else "list"
|
|
46
50
|
return f"{list_type}[{self.element_type.type_annotation(**kwargs)}]"
|
|
47
51
|
|
|
48
52
|
def description(self, *, is_operation_file: bool) -> str:
|
|
@@ -7,7 +7,7 @@ from enum import Enum
|
|
|
7
7
|
from collections import OrderedDict
|
|
8
8
|
from typing import Any, Optional, TYPE_CHECKING, cast
|
|
9
9
|
import sys
|
|
10
|
-
from .utils import add_to_pylint_disable, NamespaceType
|
|
10
|
+
from .utils import add_to_pylint_disable, NamespaceType, LOCALS_LENGTH_LIMIT
|
|
11
11
|
from .base import BaseType
|
|
12
12
|
from .constant_type import ConstantType
|
|
13
13
|
from .property import Property
|
|
@@ -244,7 +244,7 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes, too-
|
|
|
244
244
|
@property
|
|
245
245
|
def init_pylint_disable(self) -> str:
|
|
246
246
|
retval: str = ""
|
|
247
|
-
if len(self.properties) >
|
|
247
|
+
if len(self.properties) > LOCALS_LENGTH_LIMIT:
|
|
248
248
|
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
249
249
|
return retval
|
|
250
250
|
|
|
@@ -16,7 +16,7 @@ from typing import (
|
|
|
16
16
|
|
|
17
17
|
from .request_builder_parameter import RequestBuilderParameter
|
|
18
18
|
|
|
19
|
-
from .utils import OrderedSet, add_to_pylint_disable
|
|
19
|
+
from .utils import OrderedSet, add_to_pylint_disable, LOCALS_LENGTH_LIMIT, OPERATION_BODY_VARIABLES_LENGTH
|
|
20
20
|
from .base_builder import BaseBuilder
|
|
21
21
|
from .imports import FileImport, ImportType, TypingSection
|
|
22
22
|
from .response import (
|
|
@@ -143,6 +143,12 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
143
143
|
retval = add_to_pylint_disable(retval, "inconsistent-return-statements")
|
|
144
144
|
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
145
145
|
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
146
|
+
method_params = self.parameters.method
|
|
147
|
+
if self.is_overload and len(method_params) > LOCALS_LENGTH_LIMIT:
|
|
148
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
149
|
+
elif not self.is_overload and len(method_params) > (LOCALS_LENGTH_LIMIT - OPERATION_BODY_VARIABLES_LENGTH):
|
|
150
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
151
|
+
|
|
146
152
|
return retval
|
|
147
153
|
|
|
148
154
|
def cls_type_annotation(self, *, async_mode: bool, **kwargs: Any) -> str:
|
|
@@ -408,6 +414,9 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
408
414
|
file_import.merge(self.get_request_builder_import(self.request_builder, async_mode, serialize_namespace))
|
|
409
415
|
if self.overloads:
|
|
410
416
|
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
417
|
+
for overload in self.overloads:
|
|
418
|
+
if overload.parameters.has_body:
|
|
419
|
+
file_import.merge(overload.parameters.body_parameter.type.imports(**kwargs))
|
|
411
420
|
if self.code_model.options["models-mode"] == "dpg":
|
|
412
421
|
relative_path = self.code_model.get_relative_import_path(
|
|
413
422
|
serialize_namespace, module_name="_utils.model_base"
|
|
@@ -93,7 +93,9 @@ class OperationGroup(BaseModel):
|
|
|
93
93
|
@property
|
|
94
94
|
def need_validation(self) -> bool:
|
|
95
95
|
"""Whether any of its operations need validation"""
|
|
96
|
-
return any(o for o in self.operations if o.need_validation)
|
|
96
|
+
return any(o for o in self.operations if o.need_validation) or any(
|
|
97
|
+
og for og in self.operation_groups if og.need_validation
|
|
98
|
+
)
|
|
97
99
|
|
|
98
100
|
def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
|
|
99
101
|
file_import = FileImport(self.code_model)
|
|
@@ -16,11 +16,12 @@ from typing import (
|
|
|
16
16
|
from abc import abstractmethod
|
|
17
17
|
|
|
18
18
|
from .base_builder import BaseBuilder
|
|
19
|
-
from .utils import add_to_pylint_disable
|
|
19
|
+
from .utils import add_to_pylint_disable, LOCALS_LENGTH_LIMIT, REQUEST_BUILDER_BODY_VARIABLES_LENGTH
|
|
20
20
|
from .parameter_list import (
|
|
21
21
|
RequestBuilderParameterList,
|
|
22
22
|
OverloadedRequestBuilderParameterList,
|
|
23
23
|
)
|
|
24
|
+
from .parameter import ParameterLocation
|
|
24
25
|
from .imports import FileImport, ImportType, TypingSection, MsrestImportType
|
|
25
26
|
from ...utils import NAME_LENGTH_LIMIT
|
|
26
27
|
|
|
@@ -67,9 +68,25 @@ class RequestBuilderBase(BaseBuilder[ParameterListType, Sequence["RequestBuilder
|
|
|
67
68
|
return self.yaml_data.get("discriminator") in ("lro", "lropaging")
|
|
68
69
|
|
|
69
70
|
def pylint_disable(self, async_mode: bool) -> str:
|
|
71
|
+
retval = ""
|
|
70
72
|
if len(self.name) > NAME_LENGTH_LIMIT:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
retval = add_to_pylint_disable(retval, "name-too-long")
|
|
74
|
+
method_params = self.parameters.method
|
|
75
|
+
if len(method_params) > LOCALS_LENGTH_LIMIT - REQUEST_BUILDER_BODY_VARIABLES_LENGTH:
|
|
76
|
+
retval = add_to_pylint_disable(retval, "too-many-locals")
|
|
77
|
+
if (
|
|
78
|
+
len(
|
|
79
|
+
[
|
|
80
|
+
p
|
|
81
|
+
for p in method_params
|
|
82
|
+
if p.optional and p.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
> LOCALS_LENGTH_LIMIT
|
|
86
|
+
):
|
|
87
|
+
retval = add_to_pylint_disable(retval, "too-many-statements")
|
|
88
|
+
retval = add_to_pylint_disable(retval, "too-many-branches")
|
|
89
|
+
return retval
|
|
73
90
|
|
|
74
91
|
def response_type_annotation(self, **kwargs) -> str:
|
|
75
92
|
return "HttpRequest"
|
|
@@ -233,7 +233,7 @@ class LROResponse(Response):
|
|
|
233
233
|
|
|
234
234
|
def get_no_polling_method(self, async_mode: bool) -> str:
|
|
235
235
|
"""Get the default no polling method"""
|
|
236
|
-
return self.get_no_polling_method_path(async_mode).
|
|
236
|
+
return self.get_no_polling_method_path(async_mode).rsplit(".", maxsplit=1)[-1]
|
|
237
237
|
|
|
238
238
|
@staticmethod
|
|
239
239
|
def get_base_polling_method_path(async_mode: bool) -> str:
|
|
@@ -242,7 +242,7 @@ class LROResponse(Response):
|
|
|
242
242
|
|
|
243
243
|
def get_base_polling_method(self, async_mode: bool) -> str:
|
|
244
244
|
"""Get the base polling method."""
|
|
245
|
-
return self.get_base_polling_method_path(async_mode).
|
|
245
|
+
return self.get_base_polling_method_path(async_mode).rsplit(".", maxsplit=1)[-1]
|
|
246
246
|
|
|
247
247
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
248
248
|
return f"{self.get_poller(kwargs.get('async_mode', False))}[{super().type_annotation(**kwargs)}]"
|
|
@@ -30,3 +30,10 @@ class NamespaceType(str, Enum):
|
|
|
30
30
|
OPERATION = "operation"
|
|
31
31
|
CLIENT = "client"
|
|
32
32
|
TYPES_FILE = "types_file"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
LOCALS_LENGTH_LIMIT = 25
|
|
36
|
+
|
|
37
|
+
REQUEST_BUILDER_BODY_VARIABLES_LENGTH = 6 # how many body variables are present in a request builder
|
|
38
|
+
|
|
39
|
+
OPERATION_BODY_VARIABLES_LENGTH = 14 # how many body variables are present in an operation
|
|
@@ -403,7 +403,12 @@ class RequestBuilderSerializer(_BuilderBaseSerializer[RequestBuilderType]):
|
|
|
403
403
|
builder: RequestBuilderType,
|
|
404
404
|
) -> list[str]:
|
|
405
405
|
def _get_value(param):
|
|
406
|
-
|
|
406
|
+
if param.constant:
|
|
407
|
+
declaration = param.get_declaration()
|
|
408
|
+
elif param.client_default_value_declaration is not None:
|
|
409
|
+
declaration = param.client_default_value_declaration
|
|
410
|
+
else:
|
|
411
|
+
declaration = None
|
|
407
412
|
if param.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]:
|
|
408
413
|
kwarg_dict = "headers" if param.location == ParameterLocation.HEADER else "params"
|
|
409
414
|
return f"_{kwarg_dict}.pop('{param.wire_name}', {declaration})"
|
|
@@ -776,8 +781,14 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
776
781
|
client_names = [
|
|
777
782
|
overload.request_builder.parameters.body_parameter.client_name for overload in builder.overloads
|
|
778
783
|
]
|
|
779
|
-
|
|
780
|
-
|
|
784
|
+
all_dpg_model_overloads = False
|
|
785
|
+
if self.code_model.options["models-mode"] == "dpg" and builder.overloads:
|
|
786
|
+
all_dpg_model_overloads = all(
|
|
787
|
+
isinstance(o.parameters.body_parameter.type, DPGModelType) for o in builder.overloads
|
|
788
|
+
)
|
|
789
|
+
if not all_dpg_model_overloads:
|
|
790
|
+
for v in sorted(set(client_names), key=client_names.index):
|
|
791
|
+
retval.append(f"_{v} = None")
|
|
781
792
|
try:
|
|
782
793
|
# if there is a binary overload, we do a binary check first.
|
|
783
794
|
binary_overload = cast(
|
|
@@ -803,17 +814,20 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
803
814
|
f'"{other_overload.parameters.body_parameter.default_content_type}"{check_body_suffix}'
|
|
804
815
|
)
|
|
805
816
|
except StopIteration:
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if body_param.default_content_type and not same_content_type:
|
|
817
|
+
if all_dpg_model_overloads:
|
|
818
|
+
retval.extend(f"{l}" for l in self._create_body_parameter(cast(OperationType, builder.overloads[0])))
|
|
819
|
+
else:
|
|
820
|
+
for idx, overload in enumerate(builder.overloads):
|
|
821
|
+
if_statement = "if" if idx == 0 else "elif"
|
|
822
|
+
body_param = overload.parameters.body_parameter
|
|
813
823
|
retval.append(
|
|
814
|
-
f
|
|
824
|
+
f"{if_statement} {body_param.type.instance_check_template.format(body_param.client_name)}:"
|
|
815
825
|
)
|
|
816
|
-
|
|
826
|
+
if body_param.default_content_type and not same_content_type:
|
|
827
|
+
retval.append(
|
|
828
|
+
f' content_type = content_type or "{body_param.default_content_type}"{check_body_suffix}'
|
|
829
|
+
)
|
|
830
|
+
retval.extend(f" {l}" for l in self._create_body_parameter(cast(OperationType, overload)))
|
|
817
831
|
return retval
|
|
818
832
|
|
|
819
833
|
def _create_request_builder_call(
|
|
@@ -182,11 +182,10 @@ class ClientSerializer:
|
|
|
182
182
|
retval.append("self._serialize.client_side_validation = False")
|
|
183
183
|
operation_groups = [og for og in self.client.operation_groups if not og.is_mixin]
|
|
184
184
|
for og in operation_groups:
|
|
185
|
-
api_version = ""
|
|
186
185
|
retval.extend(
|
|
187
186
|
[
|
|
188
187
|
f"self.{og.property_name} = {og.class_name}(",
|
|
189
|
-
|
|
188
|
+
" self._client, self._config, self._serialize, self._deserialize",
|
|
190
189
|
")",
|
|
191
190
|
]
|
|
192
191
|
)
|
|
@@ -285,6 +285,9 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
285
285
|
file_import.add_submodule_import("typing", "overload", ImportType.STDLIB)
|
|
286
286
|
file_import.add_submodule_import("typing", "Mapping", ImportType.STDLIB)
|
|
287
287
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
288
|
+
# if there is a property named `list` we have to make sure there's no conflict with the built-in `list`
|
|
289
|
+
if self.code_model.has_property_named_list:
|
|
290
|
+
file_import.define_mypy_type("List", "list")
|
|
288
291
|
return file_import
|
|
289
292
|
|
|
290
293
|
def declare_model(self, model: ModelType) -> str:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
class {{ enum.name }}({{
|
|
2
|
+
class {{ enum.name }}({{enum.pylint_disable()}}
|
|
3
|
+
{{ enum.value_type.type_annotation(is_operation_file=False) }}, Enum, metaclass=CaseInsensitiveEnumMeta
|
|
4
|
+
):
|
|
3
5
|
{% if enum.yaml_data.get("description") %}
|
|
4
6
|
"""{{ op_tools.wrap_string(enum.yaml_data["description"], "\n ") }}
|
|
5
7
|
"""
|
|
@@ -36,6 +36,7 @@ __all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"]
|
|
|
36
36
|
|
|
37
37
|
TZ_UTC = timezone.utc
|
|
38
38
|
_T = typing.TypeVar("_T")
|
|
39
|
+
_NONE_TYPE = type(None)
|
|
39
40
|
|
|
40
41
|
{% if code_model.has_external_type %}
|
|
41
42
|
TYPE_HANDLER_REGISTRY = TypeHandlerRegistry()
|
|
@@ -223,7 +224,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
|
223
224
|
test_utc = date_obj.utctimetuple()
|
|
224
225
|
if test_utc.tm_year > 9999 or test_utc.tm_year < 1:
|
|
225
226
|
raise OverflowError("Hit max or min date")
|
|
226
|
-
return date_obj
|
|
227
|
+
return date_obj # type: ignore[no-any-return]
|
|
227
228
|
|
|
228
229
|
|
|
229
230
|
def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime:
|
|
@@ -277,7 +278,7 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
|
|
|
277
278
|
"""
|
|
278
279
|
if isinstance(attr, time):
|
|
279
280
|
return attr
|
|
280
|
-
return isodate.parse_time(attr)
|
|
281
|
+
return isodate.parse_time(attr) # type: ignore[no-any-return]
|
|
281
282
|
|
|
282
283
|
|
|
283
284
|
def _deserialize_bytes(attr):
|
|
@@ -382,9 +383,39 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
382
383
|
return key in self._data
|
|
383
384
|
|
|
384
385
|
def __getitem__(self, key: str) -> typing.Any:
|
|
386
|
+
# If this key has been deserialized (for mutable types), we need to handle serialization
|
|
387
|
+
if hasattr(self, "_attr_to_rest_field"):
|
|
388
|
+
cache_attr = f"_deserialized_{key}"
|
|
389
|
+
if hasattr(self, cache_attr):
|
|
390
|
+
rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
|
|
391
|
+
if rf:
|
|
392
|
+
value = self._data.get(key)
|
|
393
|
+
if isinstance(value, (dict, list, set)):
|
|
394
|
+
# For mutable types, serialize and return
|
|
395
|
+
# But also update _data with serialized form and clear flag
|
|
396
|
+
# so mutations via this returned value affect _data
|
|
397
|
+
serialized = _serialize(value, rf._format)
|
|
398
|
+
# If serialized form is same type (no transformation needed),
|
|
399
|
+
# return _data directly so mutations work
|
|
400
|
+
if isinstance(serialized, type(value)) and serialized == value:
|
|
401
|
+
return self._data.get(key)
|
|
402
|
+
# Otherwise return serialized copy and clear flag
|
|
403
|
+
try:
|
|
404
|
+
object.__delattr__(self, cache_attr)
|
|
405
|
+
except AttributeError:
|
|
406
|
+
pass
|
|
407
|
+
# Store serialized form back
|
|
408
|
+
self._data[key] = serialized
|
|
409
|
+
return serialized
|
|
385
410
|
return self._data.__getitem__(key)
|
|
386
411
|
|
|
387
412
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
413
|
+
# Clear any cached deserialized value when setting through dictionary access
|
|
414
|
+
cache_attr = f"_deserialized_{key}"
|
|
415
|
+
try:
|
|
416
|
+
object.__delattr__(self, cache_attr)
|
|
417
|
+
except AttributeError:
|
|
418
|
+
pass
|
|
388
419
|
self._data.__setitem__(key, value)
|
|
389
420
|
|
|
390
421
|
def __delitem__(self, key: str) -> None:
|
|
@@ -886,16 +917,16 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
|
|
|
886
917
|
|
|
887
918
|
# is it optional?
|
|
888
919
|
try:
|
|
889
|
-
if any(a for a in annotation.__args__
|
|
920
|
+
if any(a is _NONE_TYPE for a in annotation.__args__): # pyright: ignore
|
|
890
921
|
if len(annotation.__args__) <= 2: # pyright: ignore
|
|
891
922
|
if_obj_deserializer = _get_deserialize_callable_from_annotation(
|
|
892
|
-
next(a for a in annotation.__args__ if a
|
|
923
|
+
next(a for a in annotation.__args__ if a is not _NONE_TYPE), module, rf # pyright: ignore
|
|
893
924
|
)
|
|
894
925
|
|
|
895
926
|
return functools.partial(_deserialize_with_optional, if_obj_deserializer)
|
|
896
927
|
# the type is Optional[Union[...]], we need to remove the None type from the Union
|
|
897
928
|
annotation_copy = copy.copy(annotation)
|
|
898
|
-
annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a
|
|
929
|
+
annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a is not _NONE_TYPE] # pyright: ignore
|
|
899
930
|
return _get_deserialize_callable_from_annotation(annotation_copy, module, rf)
|
|
900
931
|
except AttributeError:
|
|
901
932
|
pass
|
|
@@ -1093,14 +1124,37 @@ class _RestField:
|
|
|
1093
1124
|
def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
|
|
1094
1125
|
# by this point, type and rest_name will have a value bc we default
|
|
1095
1126
|
# them in __new__ of the Model class
|
|
1096
|
-
|
|
1127
|
+
# Use _data.get() directly to avoid triggering __getitem__ which clears the cache
|
|
1128
|
+
item = obj._data.get(self._rest_name)
|
|
1097
1129
|
if item is None:
|
|
1098
1130
|
return item
|
|
1099
1131
|
if self._is_model:
|
|
1100
1132
|
return item
|
|
1101
|
-
|
|
1133
|
+
|
|
1134
|
+
# For mutable types, we want mutations to directly affect _data
|
|
1135
|
+
# Check if we've already deserialized this value
|
|
1136
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1137
|
+
if hasattr(obj, cache_attr):
|
|
1138
|
+
# Return the value from _data directly (it's been deserialized in place)
|
|
1139
|
+
return obj._data.get(self._rest_name)
|
|
1140
|
+
|
|
1141
|
+
deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
|
|
1142
|
+
|
|
1143
|
+
# For mutable types, store the deserialized value back in _data
|
|
1144
|
+
# so mutations directly affect _data
|
|
1145
|
+
if isinstance(deserialized, (dict, list, set)):
|
|
1146
|
+
obj._data[self._rest_name] = deserialized
|
|
1147
|
+
object.__setattr__(obj, cache_attr, True) # Mark as deserialized
|
|
1148
|
+
return deserialized
|
|
1149
|
+
|
|
1150
|
+
return deserialized
|
|
1102
1151
|
|
|
1103
1152
|
def __set__(self, obj: Model, value) -> None:
|
|
1153
|
+
# Clear the cached deserialized object when setting a new value
|
|
1154
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1155
|
+
if hasattr(obj, cache_attr):
|
|
1156
|
+
object.__delattr__(obj, cache_attr)
|
|
1157
|
+
|
|
1104
1158
|
if value is None:
|
|
1105
1159
|
# we want to wipe out entries if users set attr to None
|
|
1106
1160
|
try:
|
|
@@ -1274,7 +1328,7 @@ def _get_wrapped_element(
|
|
|
1274
1328
|
_get_element(v, exclude_readonly, meta, wrapped_element)
|
|
1275
1329
|
else:
|
|
1276
1330
|
wrapped_element.text = _get_primitive_type_value(v)
|
|
1277
|
-
return wrapped_element
|
|
1331
|
+
return wrapped_element # type: ignore[no-any-return]
|
|
1278
1332
|
|
|
1279
1333
|
|
|
1280
1334
|
def _get_primitive_type_value(v) -> str:
|
|
@@ -1287,7 +1341,9 @@ def _get_primitive_type_value(v) -> str:
|
|
|
1287
1341
|
return str(v)
|
|
1288
1342
|
|
|
1289
1343
|
|
|
1290
|
-
def _create_xml_element(
|
|
1344
|
+
def _create_xml_element(
|
|
1345
|
+
tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None
|
|
1346
|
+
) -> ET.Element:
|
|
1291
1347
|
if prefix and ns:
|
|
1292
1348
|
ET.register_namespace(prefix, ns)
|
|
1293
1349
|
if ns:
|
|
@@ -817,13 +817,20 @@ class Serializer: # pylint: disable=too-many-public-methods
|
|
|
817
817
|
:param str data_type: Type of object in the iterable.
|
|
818
818
|
:rtype: str, int, float, bool
|
|
819
819
|
:return: serialized object
|
|
820
|
+
:raises TypeError: raise if data_type is not one of str, int, float, bool.
|
|
820
821
|
"""
|
|
821
822
|
custom_serializer = cls._get_custom_serializers(data_type, **kwargs)
|
|
822
823
|
if custom_serializer:
|
|
823
824
|
return custom_serializer(data)
|
|
824
825
|
if data_type == "str":
|
|
825
826
|
return cls.serialize_unicode(data)
|
|
826
|
-
|
|
827
|
+
if data_type == "int":
|
|
828
|
+
return int(data)
|
|
829
|
+
if data_type == "float":
|
|
830
|
+
return float(data)
|
|
831
|
+
if data_type == "bool":
|
|
832
|
+
return bool(data)
|
|
833
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
827
834
|
|
|
828
835
|
@classmethod
|
|
829
836
|
def serialize_unicode(cls, data):
|
|
@@ -1753,7 +1760,7 @@ class Deserializer:
|
|
|
1753
1760
|
:param str data_type: deserialization data type.
|
|
1754
1761
|
:return: Deserialized basic type.
|
|
1755
1762
|
:rtype: str, int, float or bool
|
|
1756
|
-
:raises TypeError: if string format is not valid.
|
|
1763
|
+
:raises TypeError: if string format is not valid or data_type is not one of str, int, float, bool.
|
|
1757
1764
|
"""
|
|
1758
1765
|
# If we're here, data is supposed to be a basic type.
|
|
1759
1766
|
# If it's still an XML node, take the text
|
|
@@ -1779,7 +1786,11 @@ class Deserializer:
|
|
|
1779
1786
|
|
|
1780
1787
|
if data_type == "str":
|
|
1781
1788
|
return self.deserialize_unicode(attr)
|
|
1782
|
-
|
|
1789
|
+
if data_type == "int":
|
|
1790
|
+
return int(attr)
|
|
1791
|
+
if data_type == "float":
|
|
1792
|
+
return float(attr)
|
|
1793
|
+
raise TypeError("Unknown basic data type: {}".format(data_type))
|
|
1783
1794
|
|
|
1784
1795
|
@staticmethod
|
|
1785
1796
|
def deserialize_unicode(data):
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autorest/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.46.0",
|
|
4
4
|
"description": "The Python extension for generators in AutoRest.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@typespec/http-client-python": "~0.
|
|
22
|
+
"@typespec/http-client-python": "~0.24.0",
|
|
23
23
|
"@autorest/system-requirements": "~1.0.2",
|
|
24
24
|
"fs-extra": "~11.2.0",
|
|
25
25
|
"tsx": "~4.19.1"
|
|
Binary file
|