@typespec/http-client-python 0.31.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +20 -169
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/lib.d.ts +6 -0
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +29 -0
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/node-runner.browser.d.ts +12 -0
- package/dist/emitter/node-runner.browser.d.ts.map +1 -0
- package/dist/emitter/node-runner.browser.js +16 -0
- package/dist/emitter/node-runner.browser.js.map +1 -0
- package/dist/emitter/node-runner.d.ts +12 -0
- package/dist/emitter/node-runner.d.ts.map +1 -0
- package/dist/emitter/node-runner.js +180 -0
- package/dist/emitter/node-runner.js.map +1 -0
- package/dist/emitter/pyodide-loader.browser.d.ts +12 -0
- package/dist/emitter/pyodide-loader.browser.d.ts.map +1 -0
- package/dist/emitter/pyodide-loader.browser.js +33 -0
- package/dist/emitter/pyodide-loader.browser.js.map +1 -0
- package/dist/emitter/pyodide-loader.d.ts +3 -0
- package/dist/emitter/pyodide-loader.d.ts.map +1 -0
- package/dist/emitter/pyodide-loader.js +6 -0
- package/dist/emitter/pyodide-loader.js.map +1 -0
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +7 -0
- package/dist/emitter/types.js.map +1 -1
- package/emitter/src/emitter.ts +18 -189
- package/emitter/src/lib.ts +36 -0
- package/emitter/src/node-runner.browser.ts +33 -0
- package/emitter/src/node-runner.ts +220 -0
- package/emitter/src/pyodide-loader.browser.ts +49 -0
- package/emitter/src/pyodide-loader.ts +6 -0
- package/emitter/src/types.ts +7 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/generator/build/lib/pygen/__init__.py +1 -0
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +15 -1
- package/generator/build/lib/pygen/codegen/models/utils.py +20 -5
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +9 -2
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +32 -2
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +34 -1
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +30 -2
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +109 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +168 -52
- package/generator/build/lib/pygen/utils.py +38 -4
- package/generator/component-detection-pip-report.json +2 -2
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +1 -0
- package/generator/pygen/codegen/models/primitive_types.py +15 -1
- package/generator/pygen/codegen/models/utils.py +20 -5
- package/generator/pygen/codegen/serializers/builder_serializer.py +9 -2
- package/generator/pygen/codegen/serializers/general_serializer.py +32 -2
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +34 -1
- package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +30 -2
- package/generator/pygen/codegen/templates/serialization.py.jinja2 +109 -0
- package/generator/pygen/preprocess/__init__.py +168 -52
- package/generator/pygen/utils.py +38 -4
- package/package.json +12 -8
|
@@ -66,7 +66,26 @@ class GeneralSerializer(BaseSerializer):
|
|
|
66
66
|
if dep_version > default_version:
|
|
67
67
|
version_map[dep_name] = str(dep_version)
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
# Project-level pyproject.toml fields that may be preserved across regeneration
|
|
70
|
+
# via the "keep-pyproject-fields" option.
|
|
71
|
+
KEEPABLE_PROJECT_FIELDS = ("authors", "description", "classifiers", "urls")
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _parse_keep_pyproject_fields(value: Any) -> tuple[str, ...]:
|
|
75
|
+
# The "keep-pyproject-fields" option may be a comma-separated string (e.g.
|
|
76
|
+
# "authors,description") or an already-parsed sequence of field names.
|
|
77
|
+
if not value:
|
|
78
|
+
return ()
|
|
79
|
+
if isinstance(value, str):
|
|
80
|
+
return tuple(field.strip() for field in value.split(",") if field.strip())
|
|
81
|
+
return tuple(value)
|
|
82
|
+
|
|
83
|
+
def external_lib_version_map(
|
|
84
|
+
self,
|
|
85
|
+
file_content: str,
|
|
86
|
+
additional_version_map: dict[str, str],
|
|
87
|
+
keep_pyproject_fields: tuple[str, ...] = (),
|
|
88
|
+
) -> dict:
|
|
70
89
|
# Load the pyproject.toml file if it exists and extract fields to keep.
|
|
71
90
|
result: dict = {"KEEP_FIELDS": {}}
|
|
72
91
|
try:
|
|
@@ -85,6 +104,14 @@ class GeneralSerializer(BaseSerializer):
|
|
|
85
104
|
|
|
86
105
|
# Process dependencies
|
|
87
106
|
if "project" in loaded_pyproject_toml:
|
|
107
|
+
project = loaded_pyproject_toml["project"]
|
|
108
|
+
|
|
109
|
+
# Keep manually customized project fields the emitter would otherwise overwrite.
|
|
110
|
+
# Only the fields explicitly listed in the "keep-pyproject-fields" option are preserved.
|
|
111
|
+
for field in keep_pyproject_fields:
|
|
112
|
+
if field in self.KEEPABLE_PROJECT_FIELDS and field in project:
|
|
113
|
+
result["KEEP_FIELDS"][f"project.{field}"] = project[field]
|
|
114
|
+
|
|
88
115
|
# Handle main dependencies
|
|
89
116
|
if "dependencies" in loaded_pyproject_toml["project"]:
|
|
90
117
|
kept_deps = []
|
|
@@ -127,7 +154,10 @@ class GeneralSerializer(BaseSerializer):
|
|
|
127
154
|
|
|
128
155
|
# Add fields to keep from an existing pyproject.toml
|
|
129
156
|
if template_name == "pyproject.toml.jinja2":
|
|
130
|
-
|
|
157
|
+
keep_pyproject_fields = self._parse_keep_pyproject_fields(
|
|
158
|
+
self.code_model.options.get("keep-pyproject-fields")
|
|
159
|
+
)
|
|
160
|
+
params = self.external_lib_version_map(file_content, additional_version_map, keep_pyproject_fields)
|
|
131
161
|
else:
|
|
132
162
|
params = {}
|
|
133
163
|
|
|
@@ -112,6 +112,29 @@ def _serialize_bytes(o, format: typing.Optional[str] = None) -> str:
|
|
|
112
112
|
return encoded
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
def _serialize_duration(td: timedelta, format: typing.Optional[str] = None):
|
|
116
|
+
"""Serialize a timedelta to its wire representation.
|
|
117
|
+
|
|
118
|
+
For the ``seconds``/``milliseconds`` encodings the value is converted to a
|
|
119
|
+
numeric value, otherwise it falls back to an ISO 8601 duration string.
|
|
120
|
+
|
|
121
|
+
:param timedelta td: The timedelta to serialize.
|
|
122
|
+
:param str format: The duration encoding format.
|
|
123
|
+
:rtype: int or float or str
|
|
124
|
+
:return: serialized duration
|
|
125
|
+
"""
|
|
126
|
+
seconds = td.total_seconds()
|
|
127
|
+
if format == "duration-seconds-int":
|
|
128
|
+
return int(seconds)
|
|
129
|
+
if format == "duration-seconds-float":
|
|
130
|
+
return seconds
|
|
131
|
+
if format == "duration-milliseconds-int":
|
|
132
|
+
return int(seconds * 1000)
|
|
133
|
+
if format == "duration-milliseconds-float":
|
|
134
|
+
return seconds * 1000
|
|
135
|
+
return _timedelta_as_isostr(td)
|
|
136
|
+
|
|
137
|
+
|
|
115
138
|
def _serialize_datetime(o, format: typing.Optional[str] = None):
|
|
116
139
|
if hasattr(o, "year") and hasattr(o, "hour"):
|
|
117
140
|
if format == "rfc7231":
|
|
@@ -307,6 +330,12 @@ def _deserialize_duration(attr):
|
|
|
307
330
|
return isodate.parse_duration(attr)
|
|
308
331
|
|
|
309
332
|
|
|
333
|
+
def _deserialize_duration_numeric(attr, unit):
|
|
334
|
+
if isinstance(attr, timedelta):
|
|
335
|
+
return attr
|
|
336
|
+
return timedelta(**{unit: float(attr)})
|
|
337
|
+
|
|
338
|
+
|
|
310
339
|
def _deserialize_decimal(attr):
|
|
311
340
|
if isinstance(attr, decimal.Decimal):
|
|
312
341
|
return attr
|
|
@@ -336,6 +365,10 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
|
|
|
336
365
|
"unix-timestamp": _deserialize_datetime_unix_timestamp,
|
|
337
366
|
"base64": _deserialize_bytes,
|
|
338
367
|
"base64url": _deserialize_bytes_base64,
|
|
368
|
+
"duration-seconds-int": functools.partial(_deserialize_duration_numeric, unit="seconds"),
|
|
369
|
+
"duration-seconds-float": functools.partial(_deserialize_duration_numeric, unit="seconds"),
|
|
370
|
+
"duration-milliseconds-int": functools.partial(_deserialize_duration_numeric, unit="milliseconds"),
|
|
371
|
+
"duration-milliseconds-float": functools.partial(_deserialize_duration_numeric, unit="milliseconds"),
|
|
339
372
|
}
|
|
340
373
|
|
|
341
374
|
|
|
@@ -576,7 +609,7 @@ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-m
|
|
|
576
609
|
pass
|
|
577
610
|
# Last, try datetime.timedelta
|
|
578
611
|
try:
|
|
579
|
-
return
|
|
612
|
+
return _serialize_duration(o, format)
|
|
580
613
|
except AttributeError:
|
|
581
614
|
# This will be raised when it hits value.total_seconds in the method above
|
|
582
615
|
pass
|
|
@@ -11,15 +11,32 @@ build-backend = "setuptools.build_meta"
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "{{ options.get('package-name')|lower }}"
|
|
13
13
|
{% if options.get('package-mode') %}
|
|
14
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.authors') %}
|
|
15
|
+
authors = [
|
|
16
|
+
{% for author in KEEP_FIELDS.get('project.authors') %}
|
|
17
|
+
{ {% for key, val in author.items() %}{{ key }} = {{ val|tojson }}{% if not loop.last %}, {% endif %}{% endfor %} },
|
|
18
|
+
{% endfor %}
|
|
19
|
+
]
|
|
20
|
+
{% else %}
|
|
14
21
|
authors = [
|
|
15
22
|
{ name = "{{ code_model.company_name }}"{% if code_model.is_azure_flavor %}, email = "azpysdkhelp@microsoft.com"{% endif %} },
|
|
16
23
|
]
|
|
17
|
-
{%
|
|
24
|
+
{% endif %}
|
|
25
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.description') %}
|
|
26
|
+
description = {{ KEEP_FIELDS.get('project.description')|tojson }}
|
|
27
|
+
{% elif options.get("azure-arm") %}
|
|
18
28
|
description = "Microsoft Azure {{ options.get('package-pprint-name') }} Client Library for Python"
|
|
19
29
|
{% else %}
|
|
20
30
|
description = "{{ code_model.company_name }} {% if code_model.is_azure_flavor and not options.get('package-pprint-name').startswith('Azure ') %}Azure {% endif %}{{ options.get('package-pprint-name') }} Client Library for Python"
|
|
21
31
|
{% endif %}
|
|
22
32
|
license = "MIT"
|
|
33
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.classifiers') %}
|
|
34
|
+
classifiers = [
|
|
35
|
+
{% for classifier in KEEP_FIELDS.get('project.classifiers') %}
|
|
36
|
+
{{ classifier|tojson }},
|
|
37
|
+
{% endfor %}
|
|
38
|
+
]
|
|
39
|
+
{% else %}
|
|
23
40
|
classifiers = [
|
|
24
41
|
"Development Status :: {{ dev_status }}",
|
|
25
42
|
"Programming Language :: Python",
|
|
@@ -29,10 +46,15 @@ classifiers = [
|
|
|
29
46
|
"Programming Language :: Python :: 3.{{ version }}",
|
|
30
47
|
{% endfor %}
|
|
31
48
|
]
|
|
49
|
+
{% endif %}
|
|
32
50
|
requires-python = ">={{ MIN_PYTHON_VERSION }}"
|
|
33
51
|
{% else %}
|
|
52
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.description') %}
|
|
53
|
+
description = {{ KEEP_FIELDS.get('project.description')|tojson }}
|
|
54
|
+
{% else %}
|
|
34
55
|
description = "{{ options.get('package-name') }}"
|
|
35
56
|
{% endif %}
|
|
57
|
+
{% endif %}
|
|
36
58
|
{% if code_model.is_azure_flavor %}
|
|
37
59
|
keywords = ["azure", "azure sdk"]
|
|
38
60
|
{% endif %}
|
|
@@ -77,7 +99,13 @@ version = "{{ options.get("package-version", "unknown") }}"
|
|
|
77
99
|
]
|
|
78
100
|
{% endfor %}
|
|
79
101
|
{% endif %}
|
|
80
|
-
{% if
|
|
102
|
+
{% if KEEP_FIELDS and KEEP_FIELDS.get('project.urls') %}
|
|
103
|
+
|
|
104
|
+
[project.urls]
|
|
105
|
+
{% for key, val in KEEP_FIELDS.get('project.urls').items() %}
|
|
106
|
+
{{ key }} = {{ val|tojson }}
|
|
107
|
+
{% endfor %}
|
|
108
|
+
{% elif code_model.is_azure_flavor %}
|
|
81
109
|
|
|
82
110
|
[project.urls]
|
|
83
111
|
repository = "https://github.com/Azure/azure-sdk-for-python"
|
|
@@ -516,6 +516,10 @@ class Serializer: # pylint: disable=too-many-public-methods
|
|
|
516
516
|
"rfc-1123": Serializer.serialize_rfc,
|
|
517
517
|
"unix-time": Serializer.serialize_unix,
|
|
518
518
|
"duration": Serializer.serialize_duration,
|
|
519
|
+
"duration-seconds-int": Serializer.serialize_duration_seconds_int,
|
|
520
|
+
"duration-seconds-float": Serializer.serialize_duration_seconds_float,
|
|
521
|
+
"duration-milliseconds-int": Serializer.serialize_duration_milliseconds_int,
|
|
522
|
+
"duration-milliseconds-float": Serializer.serialize_duration_milliseconds_float,
|
|
519
523
|
"date": Serializer.serialize_date,
|
|
520
524
|
"time": Serializer.serialize_time,
|
|
521
525
|
"decimal": Serializer.serialize_decimal,
|
|
@@ -1105,6 +1109,61 @@ class Serializer: # pylint: disable=too-many-public-methods
|
|
|
1105
1109
|
attr = isodate.parse_duration(attr)
|
|
1106
1110
|
return isodate.duration_isoformat(attr)
|
|
1107
1111
|
|
|
1112
|
+
@staticmethod
|
|
1113
|
+
def _serialize_duration_numeric(attr, scale, as_int):
|
|
1114
|
+
"""Serialize a TimeDelta into a numeric value scaled to the wire unit.
|
|
1115
|
+
|
|
1116
|
+
:param TimeDelta attr: Object to be serialized.
|
|
1117
|
+
:param int scale: Multiplier applied to total seconds (1 for seconds, 1000 for milliseconds).
|
|
1118
|
+
:param bool as_int: Whether to truncate the result to an int.
|
|
1119
|
+
:rtype: int or float
|
|
1120
|
+
:return: serialized duration
|
|
1121
|
+
"""
|
|
1122
|
+
if isinstance(attr, str):
|
|
1123
|
+
attr = isodate.parse_duration(attr)
|
|
1124
|
+
value = attr.total_seconds() * scale if isinstance(attr, datetime.timedelta) else attr
|
|
1125
|
+
return int(value) if as_int else float(value)
|
|
1126
|
+
|
|
1127
|
+
@staticmethod
|
|
1128
|
+
def serialize_duration_seconds_int(attr, **kwargs): # pylint: disable=unused-argument
|
|
1129
|
+
"""Serialize TimeDelta object into an integer number of seconds.
|
|
1130
|
+
|
|
1131
|
+
:param TimeDelta attr: Object to be serialized.
|
|
1132
|
+
:rtype: int
|
|
1133
|
+
:return: serialized duration
|
|
1134
|
+
"""
|
|
1135
|
+
return Serializer._serialize_duration_numeric(attr, 1, True)
|
|
1136
|
+
|
|
1137
|
+
@staticmethod
|
|
1138
|
+
def serialize_duration_seconds_float(attr, **kwargs): # pylint: disable=unused-argument
|
|
1139
|
+
"""Serialize TimeDelta object into a floating point number of seconds.
|
|
1140
|
+
|
|
1141
|
+
:param TimeDelta attr: Object to be serialized.
|
|
1142
|
+
:rtype: float
|
|
1143
|
+
:return: serialized duration
|
|
1144
|
+
"""
|
|
1145
|
+
return Serializer._serialize_duration_numeric(attr, 1, False)
|
|
1146
|
+
|
|
1147
|
+
@staticmethod
|
|
1148
|
+
def serialize_duration_milliseconds_int(attr, **kwargs): # pylint: disable=unused-argument
|
|
1149
|
+
"""Serialize TimeDelta object into an integer number of milliseconds.
|
|
1150
|
+
|
|
1151
|
+
:param TimeDelta attr: Object to be serialized.
|
|
1152
|
+
:rtype: int
|
|
1153
|
+
:return: serialized duration
|
|
1154
|
+
"""
|
|
1155
|
+
return Serializer._serialize_duration_numeric(attr, 1000, True)
|
|
1156
|
+
|
|
1157
|
+
@staticmethod
|
|
1158
|
+
def serialize_duration_milliseconds_float(attr, **kwargs): # pylint: disable=unused-argument
|
|
1159
|
+
"""Serialize TimeDelta object into a floating point number of milliseconds.
|
|
1160
|
+
|
|
1161
|
+
:param TimeDelta attr: Object to be serialized.
|
|
1162
|
+
:rtype: float
|
|
1163
|
+
:return: serialized duration
|
|
1164
|
+
"""
|
|
1165
|
+
return Serializer._serialize_duration_numeric(attr, 1000, False)
|
|
1166
|
+
|
|
1108
1167
|
@staticmethod
|
|
1109
1168
|
def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument
|
|
1110
1169
|
"""Serialize Datetime object into RFC-1123 formatted string.
|
|
@@ -1377,6 +1436,10 @@ class Deserializer:
|
|
|
1377
1436
|
"rfc-1123": Deserializer.deserialize_rfc,
|
|
1378
1437
|
"unix-time": Deserializer.deserialize_unix,
|
|
1379
1438
|
"duration": Deserializer.deserialize_duration,
|
|
1439
|
+
"duration-seconds-int": Deserializer.deserialize_duration_seconds,
|
|
1440
|
+
"duration-seconds-float": Deserializer.deserialize_duration_seconds,
|
|
1441
|
+
"duration-milliseconds-int": Deserializer.deserialize_duration_milliseconds,
|
|
1442
|
+
"duration-milliseconds-float": Deserializer.deserialize_duration_milliseconds,
|
|
1380
1443
|
"date": Deserializer.deserialize_date,
|
|
1381
1444
|
"time": Deserializer.deserialize_time,
|
|
1382
1445
|
"decimal": Deserializer.deserialize_decimal,
|
|
@@ -1389,6 +1452,10 @@ class Deserializer:
|
|
|
1389
1452
|
}
|
|
1390
1453
|
self.deserialize_expected_types = {
|
|
1391
1454
|
"duration": (isodate.Duration, datetime.timedelta),
|
|
1455
|
+
"duration-seconds-int": (isodate.Duration, datetime.timedelta),
|
|
1456
|
+
"duration-seconds-float": (isodate.Duration, datetime.timedelta),
|
|
1457
|
+
"duration-milliseconds-int": (isodate.Duration, datetime.timedelta),
|
|
1458
|
+
"duration-milliseconds-float": (isodate.Duration, datetime.timedelta),
|
|
1392
1459
|
"iso-8601": (datetime.datetime),
|
|
1393
1460
|
}
|
|
1394
1461
|
self.dependencies: dict[str, type] = dict(classes) if classes else {}
|
|
@@ -1950,6 +2017,48 @@ class Deserializer:
|
|
|
1950
2017
|
raise DeserializationError(msg) from err
|
|
1951
2018
|
return duration
|
|
1952
2019
|
|
|
2020
|
+
@staticmethod
|
|
2021
|
+
def _deserialize_duration_numeric(attr, unit):
|
|
2022
|
+
"""Deserialize a numeric duration value into a TimeDelta object.
|
|
2023
|
+
|
|
2024
|
+
:param float attr: response value to be deserialized.
|
|
2025
|
+
:param str unit: The wire unit, used as the ``timedelta`` keyword
|
|
2026
|
+
(``"seconds"`` or ``"milliseconds"``).
|
|
2027
|
+
:return: Deserialized duration
|
|
2028
|
+
:rtype: TimeDelta
|
|
2029
|
+
:raises DeserializationError: if value is invalid.
|
|
2030
|
+
"""
|
|
2031
|
+
if isinstance(attr, ET.Element):
|
|
2032
|
+
attr = attr.text
|
|
2033
|
+
try:
|
|
2034
|
+
duration = datetime.timedelta(**{unit: float(attr)}) # type: ignore
|
|
2035
|
+
except (ValueError, OverflowError, TypeError) as err:
|
|
2036
|
+
msg = "Cannot deserialize duration object."
|
|
2037
|
+
raise DeserializationError(msg) from err
|
|
2038
|
+
return duration
|
|
2039
|
+
|
|
2040
|
+
@staticmethod
|
|
2041
|
+
def deserialize_duration_seconds(attr):
|
|
2042
|
+
"""Deserialize a numeric number of seconds into a TimeDelta object.
|
|
2043
|
+
|
|
2044
|
+
:param float attr: response value to be deserialized.
|
|
2045
|
+
:return: Deserialized duration
|
|
2046
|
+
:rtype: TimeDelta
|
|
2047
|
+
:raises DeserializationError: if value is invalid.
|
|
2048
|
+
"""
|
|
2049
|
+
return Deserializer._deserialize_duration_numeric(attr, "seconds")
|
|
2050
|
+
|
|
2051
|
+
@staticmethod
|
|
2052
|
+
def deserialize_duration_milliseconds(attr):
|
|
2053
|
+
"""Deserialize a numeric number of milliseconds into a TimeDelta object.
|
|
2054
|
+
|
|
2055
|
+
:param float attr: response value to be deserialized.
|
|
2056
|
+
:return: Deserialized duration
|
|
2057
|
+
:rtype: TimeDelta
|
|
2058
|
+
:raises DeserializationError: if value is invalid.
|
|
2059
|
+
"""
|
|
2060
|
+
return Deserializer._deserialize_duration_numeric(attr, "milliseconds")
|
|
2061
|
+
|
|
1953
2062
|
@staticmethod
|
|
1954
2063
|
def deserialize_date(attr):
|
|
1955
2064
|
"""Deserialize ISO-8601 formatted string into Date object.
|