@autorest/python 6.44.0 → 6.45.1
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/property.py +1 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +6 -1
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +2 -0
- package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -5
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +83 -2
- 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/property.py +1 -0
- package/generator/pygen/codegen/serializers/builder_serializer.py +6 -1
- package/generator/pygen/codegen/serializers/model_serializer.py +2 -0
- package/generator/pygen/codegen/templates/macros.jinja2 +12 -5
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +83 -2
- package/generator/pygen/codegen/templates/serialization.py.jinja2 +14 -3
- package/package.json +2 -2
- package/scripts/__pycache__/venvtools.cpython-310.pyc +0 -0
|
@@ -40,6 +40,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
40
40
|
self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
|
|
41
41
|
self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
|
|
42
42
|
self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")
|
|
43
|
+
self.encode: Optional[str] = self.yaml_data.get("encode")
|
|
43
44
|
|
|
44
45
|
def pylint_disable(self) -> str:
|
|
45
46
|
retval: str = ""
|
|
@@ -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})"
|
|
@@ -329,6 +329,8 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
329
329
|
args.append("is_multipart_file_input=True")
|
|
330
330
|
elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
|
|
331
331
|
args.append(f'format="{prop.type.encode}"') # type: ignore
|
|
332
|
+
elif prop.encode:
|
|
333
|
+
args.append(f'format="{prop.encode}"')
|
|
332
334
|
|
|
333
335
|
if prop.xml_metadata:
|
|
334
336
|
args.append(f"xml={prop.xml_metadata}")
|
|
@@ -5,21 +5,28 @@
|
|
|
5
5
|
{% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
|
|
6
6
|
{%- if enable_custom_handling -%}
|
|
7
7
|
{%- set lines = doc_string.split('\n') -%}
|
|
8
|
+
{%- set base_indent = wrap_string.lstrip('\n') -%}
|
|
8
9
|
{%- set result_lines = [] -%}
|
|
9
10
|
{%- for line in lines -%}
|
|
10
11
|
{%- if line.startswith('* ') -%}
|
|
11
12
|
{# Handle bullet points with proper continuation alignment #}
|
|
12
13
|
{%- set bullet_content = line[2:] -%}
|
|
13
|
-
{%- set base_indent = wrap_string.lstrip('\n') -%}
|
|
14
14
|
{%- set bullet_line = base_indent + ' * ' + bullet_content -%}
|
|
15
15
|
{%- set continuation_spaces = base_indent + ' ' -%}
|
|
16
16
|
{%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
|
|
17
17
|
{%- set _ = result_lines.append(wrapped) -%}
|
|
18
18
|
{%- elif line.strip() -%}
|
|
19
|
-
{%- set
|
|
20
|
-
{%- set
|
|
19
|
+
{%- set line_indent = '' if line.strip().startswith(':') or loop.index == 1 else (base_indent + ' ') -%}
|
|
20
|
+
{%- set wrapped = (line_indent + line) | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
|
|
21
|
+
{%- for line in wrapped.split('\n') -%}
|
|
22
|
+
{%- set prefix = "" if loop.index == 1 else " " -%}
|
|
23
|
+
{%- set _ = result_lines.append(prefix + line) -%}
|
|
24
|
+
{%- endfor -%}
|
|
21
25
|
{%- else -%}
|
|
22
|
-
{
|
|
26
|
+
{# Do not add continuous blank lines #}
|
|
27
|
+
{%- if (result_lines and result_lines[-1] != '') or not result_lines -%}
|
|
28
|
+
{%- set _ = result_lines.append('') -%}
|
|
29
|
+
{%- endif -%}
|
|
23
30
|
{%- endif -%}
|
|
24
31
|
{%- endfor -%}
|
|
25
32
|
{%- set original_result = result_lines | join('\n') -%}
|
|
@@ -37,4 +44,4 @@
|
|
|
37
44
|
{% set suffix = suffix_string if list_result | length == loop.index %}
|
|
38
45
|
{{ prefix }}{{ line }}{{ suffix }}
|
|
39
46
|
{% endfor %}
|
|
40
|
-
{% endmacro %}
|
|
47
|
+
{% endmacro %}
|
|
@@ -179,6 +179,19 @@ _VALID_RFC7231 = re.compile(
|
|
|
179
179
|
r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
+
_ARRAY_ENCODE_MAPPING = {
|
|
183
|
+
"pipeDelimited": "|",
|
|
184
|
+
"spaceDelimited": " ",
|
|
185
|
+
"commaDelimited": ",",
|
|
186
|
+
"newlineDelimited": "\n",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def _deserialize_array_encoded(delimit: str, attr):
|
|
190
|
+
if isinstance(attr, str):
|
|
191
|
+
if attr == "":
|
|
192
|
+
return []
|
|
193
|
+
return attr.split(delimit)
|
|
194
|
+
return attr
|
|
182
195
|
|
|
183
196
|
def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
184
197
|
"""Deserialize ISO-8601 formatted string into Datetime object.
|
|
@@ -323,6 +336,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
|
|
|
323
336
|
def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
|
|
324
337
|
if annotation is int and rf and rf._format == "str":
|
|
325
338
|
return _deserialize_int_as_str
|
|
339
|
+
if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
|
|
340
|
+
return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
|
|
326
341
|
if rf and rf._format:
|
|
327
342
|
return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
|
|
328
343
|
{% if code_model.has_external_type %}
|
|
@@ -367,9 +382,39 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
367
382
|
return key in self._data
|
|
368
383
|
|
|
369
384
|
def __getitem__(self, key: str) -> typing.Any:
|
|
385
|
+
# If this key has been deserialized (for mutable types), we need to handle serialization
|
|
386
|
+
if hasattr(self, "_attr_to_rest_field"):
|
|
387
|
+
cache_attr = f"_deserialized_{key}"
|
|
388
|
+
if hasattr(self, cache_attr):
|
|
389
|
+
rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
|
|
390
|
+
if rf:
|
|
391
|
+
value = self._data.get(key)
|
|
392
|
+
if isinstance(value, (dict, list, set)):
|
|
393
|
+
# For mutable types, serialize and return
|
|
394
|
+
# But also update _data with serialized form and clear flag
|
|
395
|
+
# so mutations via this returned value affect _data
|
|
396
|
+
serialized = _serialize(value, rf._format)
|
|
397
|
+
# If serialized form is same type (no transformation needed),
|
|
398
|
+
# return _data directly so mutations work
|
|
399
|
+
if isinstance(serialized, type(value)) and serialized == value:
|
|
400
|
+
return self._data.get(key)
|
|
401
|
+
# Otherwise return serialized copy and clear flag
|
|
402
|
+
try:
|
|
403
|
+
object.__delattr__(self, cache_attr)
|
|
404
|
+
except AttributeError:
|
|
405
|
+
pass
|
|
406
|
+
# Store serialized form back
|
|
407
|
+
self._data[key] = serialized
|
|
408
|
+
return serialized
|
|
370
409
|
return self._data.__getitem__(key)
|
|
371
410
|
|
|
372
411
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
412
|
+
# Clear any cached deserialized value when setting through dictionary access
|
|
413
|
+
cache_attr = f"_deserialized_{key}"
|
|
414
|
+
try:
|
|
415
|
+
object.__delattr__(self, cache_attr)
|
|
416
|
+
except AttributeError:
|
|
417
|
+
pass
|
|
373
418
|
self._data.__setitem__(key, value)
|
|
374
419
|
|
|
375
420
|
def __delitem__(self, key: str) -> None:
|
|
@@ -497,6 +542,8 @@ def _is_model(obj: typing.Any) -> bool:
|
|
|
497
542
|
|
|
498
543
|
def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
|
|
499
544
|
if isinstance(o, list):
|
|
545
|
+
if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
|
|
546
|
+
return _ARRAY_ENCODE_MAPPING[format].join(o)
|
|
500
547
|
return [_serialize(x, format) for x in o]
|
|
501
548
|
if isinstance(o, dict):
|
|
502
549
|
return {k: _serialize(v, format) for k, v in o.items()}
|
|
@@ -809,6 +856,17 @@ def _deserialize_sequence(
|
|
|
809
856
|
return obj
|
|
810
857
|
if isinstance(obj, ET.Element):
|
|
811
858
|
obj = list(obj)
|
|
859
|
+
try:
|
|
860
|
+
if (
|
|
861
|
+
isinstance(obj, str)
|
|
862
|
+
and isinstance(deserializer, functools.partial)
|
|
863
|
+
and isinstance(deserializer.args[0], functools.partial)
|
|
864
|
+
and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
|
|
865
|
+
):
|
|
866
|
+
# encoded string may be deserialized to sequence
|
|
867
|
+
return deserializer(obj)
|
|
868
|
+
except: # pylint: disable=bare-except
|
|
869
|
+
pass
|
|
812
870
|
return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
|
|
813
871
|
|
|
814
872
|
|
|
@@ -1065,14 +1123,37 @@ class _RestField:
|
|
|
1065
1123
|
def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
|
|
1066
1124
|
# by this point, type and rest_name will have a value bc we default
|
|
1067
1125
|
# them in __new__ of the Model class
|
|
1068
|
-
|
|
1126
|
+
# Use _data.get() directly to avoid triggering __getitem__ which clears the cache
|
|
1127
|
+
item = obj._data.get(self._rest_name)
|
|
1069
1128
|
if item is None:
|
|
1070
1129
|
return item
|
|
1071
1130
|
if self._is_model:
|
|
1072
1131
|
return item
|
|
1073
|
-
|
|
1132
|
+
|
|
1133
|
+
# For mutable types, we want mutations to directly affect _data
|
|
1134
|
+
# Check if we've already deserialized this value
|
|
1135
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1136
|
+
if hasattr(obj, cache_attr):
|
|
1137
|
+
# Return the value from _data directly (it's been deserialized in place)
|
|
1138
|
+
return obj._data.get(self._rest_name)
|
|
1139
|
+
|
|
1140
|
+
deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
|
|
1141
|
+
|
|
1142
|
+
# For mutable types, store the deserialized value back in _data
|
|
1143
|
+
# so mutations directly affect _data
|
|
1144
|
+
if isinstance(deserialized, (dict, list, set)):
|
|
1145
|
+
obj._data[self._rest_name] = deserialized
|
|
1146
|
+
object.__setattr__(obj, cache_attr, True) # Mark as deserialized
|
|
1147
|
+
return deserialized
|
|
1148
|
+
|
|
1149
|
+
return deserialized
|
|
1074
1150
|
|
|
1075
1151
|
def __set__(self, obj: Model, value) -> None:
|
|
1152
|
+
# Clear the cached deserialized object when setting a new value
|
|
1153
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1154
|
+
if hasattr(obj, cache_attr):
|
|
1155
|
+
object.__delattr__(obj, cache_attr)
|
|
1156
|
+
|
|
1076
1157
|
if value is None:
|
|
1077
1158
|
# we want to wipe out entries if users set attr to None
|
|
1078
1159
|
try:
|
|
@@ -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
|
|
@@ -40,6 +40,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
|
40
40
|
self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
|
|
41
41
|
self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
|
|
42
42
|
self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")
|
|
43
|
+
self.encode: Optional[str] = self.yaml_data.get("encode")
|
|
43
44
|
|
|
44
45
|
def pylint_disable(self) -> str:
|
|
45
46
|
retval: str = ""
|
|
@@ -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})"
|
|
@@ -329,6 +329,8 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
329
329
|
args.append("is_multipart_file_input=True")
|
|
330
330
|
elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
|
|
331
331
|
args.append(f'format="{prop.type.encode}"') # type: ignore
|
|
332
|
+
elif prop.encode:
|
|
333
|
+
args.append(f'format="{prop.encode}"')
|
|
332
334
|
|
|
333
335
|
if prop.xml_metadata:
|
|
334
336
|
args.append(f"xml={prop.xml_metadata}")
|
|
@@ -5,21 +5,28 @@
|
|
|
5
5
|
{% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
|
|
6
6
|
{%- if enable_custom_handling -%}
|
|
7
7
|
{%- set lines = doc_string.split('\n') -%}
|
|
8
|
+
{%- set base_indent = wrap_string.lstrip('\n') -%}
|
|
8
9
|
{%- set result_lines = [] -%}
|
|
9
10
|
{%- for line in lines -%}
|
|
10
11
|
{%- if line.startswith('* ') -%}
|
|
11
12
|
{# Handle bullet points with proper continuation alignment #}
|
|
12
13
|
{%- set bullet_content = line[2:] -%}
|
|
13
|
-
{%- set base_indent = wrap_string.lstrip('\n') -%}
|
|
14
14
|
{%- set bullet_line = base_indent + ' * ' + bullet_content -%}
|
|
15
15
|
{%- set continuation_spaces = base_indent + ' ' -%}
|
|
16
16
|
{%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
|
|
17
17
|
{%- set _ = result_lines.append(wrapped) -%}
|
|
18
18
|
{%- elif line.strip() -%}
|
|
19
|
-
{%- set
|
|
20
|
-
{%- set
|
|
19
|
+
{%- set line_indent = '' if line.strip().startswith(':') or loop.index == 1 else (base_indent + ' ') -%}
|
|
20
|
+
{%- set wrapped = (line_indent + line) | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
|
|
21
|
+
{%- for line in wrapped.split('\n') -%}
|
|
22
|
+
{%- set prefix = "" if loop.index == 1 else " " -%}
|
|
23
|
+
{%- set _ = result_lines.append(prefix + line) -%}
|
|
24
|
+
{%- endfor -%}
|
|
21
25
|
{%- else -%}
|
|
22
|
-
{
|
|
26
|
+
{# Do not add continuous blank lines #}
|
|
27
|
+
{%- if (result_lines and result_lines[-1] != '') or not result_lines -%}
|
|
28
|
+
{%- set _ = result_lines.append('') -%}
|
|
29
|
+
{%- endif -%}
|
|
23
30
|
{%- endif -%}
|
|
24
31
|
{%- endfor -%}
|
|
25
32
|
{%- set original_result = result_lines | join('\n') -%}
|
|
@@ -37,4 +44,4 @@
|
|
|
37
44
|
{% set suffix = suffix_string if list_result | length == loop.index %}
|
|
38
45
|
{{ prefix }}{{ line }}{{ suffix }}
|
|
39
46
|
{% endfor %}
|
|
40
|
-
{% endmacro %}
|
|
47
|
+
{% endmacro %}
|
|
@@ -179,6 +179,19 @@ _VALID_RFC7231 = re.compile(
|
|
|
179
179
|
r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
+
_ARRAY_ENCODE_MAPPING = {
|
|
183
|
+
"pipeDelimited": "|",
|
|
184
|
+
"spaceDelimited": " ",
|
|
185
|
+
"commaDelimited": ",",
|
|
186
|
+
"newlineDelimited": "\n",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def _deserialize_array_encoded(delimit: str, attr):
|
|
190
|
+
if isinstance(attr, str):
|
|
191
|
+
if attr == "":
|
|
192
|
+
return []
|
|
193
|
+
return attr.split(delimit)
|
|
194
|
+
return attr
|
|
182
195
|
|
|
183
196
|
def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
184
197
|
"""Deserialize ISO-8601 formatted string into Datetime object.
|
|
@@ -323,6 +336,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
|
|
|
323
336
|
def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
|
|
324
337
|
if annotation is int and rf and rf._format == "str":
|
|
325
338
|
return _deserialize_int_as_str
|
|
339
|
+
if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
|
|
340
|
+
return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
|
|
326
341
|
if rf and rf._format:
|
|
327
342
|
return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
|
|
328
343
|
{% if code_model.has_external_type %}
|
|
@@ -367,9 +382,39 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
367
382
|
return key in self._data
|
|
368
383
|
|
|
369
384
|
def __getitem__(self, key: str) -> typing.Any:
|
|
385
|
+
# If this key has been deserialized (for mutable types), we need to handle serialization
|
|
386
|
+
if hasattr(self, "_attr_to_rest_field"):
|
|
387
|
+
cache_attr = f"_deserialized_{key}"
|
|
388
|
+
if hasattr(self, cache_attr):
|
|
389
|
+
rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
|
|
390
|
+
if rf:
|
|
391
|
+
value = self._data.get(key)
|
|
392
|
+
if isinstance(value, (dict, list, set)):
|
|
393
|
+
# For mutable types, serialize and return
|
|
394
|
+
# But also update _data with serialized form and clear flag
|
|
395
|
+
# so mutations via this returned value affect _data
|
|
396
|
+
serialized = _serialize(value, rf._format)
|
|
397
|
+
# If serialized form is same type (no transformation needed),
|
|
398
|
+
# return _data directly so mutations work
|
|
399
|
+
if isinstance(serialized, type(value)) and serialized == value:
|
|
400
|
+
return self._data.get(key)
|
|
401
|
+
# Otherwise return serialized copy and clear flag
|
|
402
|
+
try:
|
|
403
|
+
object.__delattr__(self, cache_attr)
|
|
404
|
+
except AttributeError:
|
|
405
|
+
pass
|
|
406
|
+
# Store serialized form back
|
|
407
|
+
self._data[key] = serialized
|
|
408
|
+
return serialized
|
|
370
409
|
return self._data.__getitem__(key)
|
|
371
410
|
|
|
372
411
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
412
|
+
# Clear any cached deserialized value when setting through dictionary access
|
|
413
|
+
cache_attr = f"_deserialized_{key}"
|
|
414
|
+
try:
|
|
415
|
+
object.__delattr__(self, cache_attr)
|
|
416
|
+
except AttributeError:
|
|
417
|
+
pass
|
|
373
418
|
self._data.__setitem__(key, value)
|
|
374
419
|
|
|
375
420
|
def __delitem__(self, key: str) -> None:
|
|
@@ -497,6 +542,8 @@ def _is_model(obj: typing.Any) -> bool:
|
|
|
497
542
|
|
|
498
543
|
def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
|
|
499
544
|
if isinstance(o, list):
|
|
545
|
+
if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
|
|
546
|
+
return _ARRAY_ENCODE_MAPPING[format].join(o)
|
|
500
547
|
return [_serialize(x, format) for x in o]
|
|
501
548
|
if isinstance(o, dict):
|
|
502
549
|
return {k: _serialize(v, format) for k, v in o.items()}
|
|
@@ -809,6 +856,17 @@ def _deserialize_sequence(
|
|
|
809
856
|
return obj
|
|
810
857
|
if isinstance(obj, ET.Element):
|
|
811
858
|
obj = list(obj)
|
|
859
|
+
try:
|
|
860
|
+
if (
|
|
861
|
+
isinstance(obj, str)
|
|
862
|
+
and isinstance(deserializer, functools.partial)
|
|
863
|
+
and isinstance(deserializer.args[0], functools.partial)
|
|
864
|
+
and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
|
|
865
|
+
):
|
|
866
|
+
# encoded string may be deserialized to sequence
|
|
867
|
+
return deserializer(obj)
|
|
868
|
+
except: # pylint: disable=bare-except
|
|
869
|
+
pass
|
|
812
870
|
return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
|
|
813
871
|
|
|
814
872
|
|
|
@@ -1065,14 +1123,37 @@ class _RestField:
|
|
|
1065
1123
|
def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
|
|
1066
1124
|
# by this point, type and rest_name will have a value bc we default
|
|
1067
1125
|
# them in __new__ of the Model class
|
|
1068
|
-
|
|
1126
|
+
# Use _data.get() directly to avoid triggering __getitem__ which clears the cache
|
|
1127
|
+
item = obj._data.get(self._rest_name)
|
|
1069
1128
|
if item is None:
|
|
1070
1129
|
return item
|
|
1071
1130
|
if self._is_model:
|
|
1072
1131
|
return item
|
|
1073
|
-
|
|
1132
|
+
|
|
1133
|
+
# For mutable types, we want mutations to directly affect _data
|
|
1134
|
+
# Check if we've already deserialized this value
|
|
1135
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1136
|
+
if hasattr(obj, cache_attr):
|
|
1137
|
+
# Return the value from _data directly (it's been deserialized in place)
|
|
1138
|
+
return obj._data.get(self._rest_name)
|
|
1139
|
+
|
|
1140
|
+
deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
|
|
1141
|
+
|
|
1142
|
+
# For mutable types, store the deserialized value back in _data
|
|
1143
|
+
# so mutations directly affect _data
|
|
1144
|
+
if isinstance(deserialized, (dict, list, set)):
|
|
1145
|
+
obj._data[self._rest_name] = deserialized
|
|
1146
|
+
object.__setattr__(obj, cache_attr, True) # Mark as deserialized
|
|
1147
|
+
return deserialized
|
|
1148
|
+
|
|
1149
|
+
return deserialized
|
|
1074
1150
|
|
|
1075
1151
|
def __set__(self, obj: Model, value) -> None:
|
|
1152
|
+
# Clear the cached deserialized object when setting a new value
|
|
1153
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1154
|
+
if hasattr(obj, cache_attr):
|
|
1155
|
+
object.__delattr__(obj, cache_attr)
|
|
1156
|
+
|
|
1076
1157
|
if value is None:
|
|
1077
1158
|
# we want to wipe out entries if users set attr to None
|
|
1078
1159
|
try:
|
|
@@ -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.45.1",
|
|
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.23.1",
|
|
23
23
|
"@autorest/system-requirements": "~1.0.2",
|
|
24
24
|
"fs-extra": "~11.2.0",
|
|
25
25
|
"tsx": "~4.19.1"
|
|
Binary file
|