azure-mgmt-agricultureplatform 1.0.0b1__py3-none-any.whl

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.
Files changed (28) hide show
  1. azure/mgmt/agricultureplatform/__init__.py +32 -0
  2. azure/mgmt/agricultureplatform/_client.py +137 -0
  3. azure/mgmt/agricultureplatform/_configuration.py +80 -0
  4. azure/mgmt/agricultureplatform/_patch.py +21 -0
  5. azure/mgmt/agricultureplatform/_utils/__init__.py +6 -0
  6. azure/mgmt/agricultureplatform/_utils/model_base.py +1337 -0
  7. azure/mgmt/agricultureplatform/_utils/serialization.py +2041 -0
  8. azure/mgmt/agricultureplatform/_version.py +9 -0
  9. azure/mgmt/agricultureplatform/aio/__init__.py +29 -0
  10. azure/mgmt/agricultureplatform/aio/_client.py +141 -0
  11. azure/mgmt/agricultureplatform/aio/_configuration.py +80 -0
  12. azure/mgmt/agricultureplatform/aio/_patch.py +21 -0
  13. azure/mgmt/agricultureplatform/aio/operations/__init__.py +27 -0
  14. azure/mgmt/agricultureplatform/aio/operations/_operations.py +1102 -0
  15. azure/mgmt/agricultureplatform/aio/operations/_patch.py +21 -0
  16. azure/mgmt/agricultureplatform/models/__init__.py +92 -0
  17. azure/mgmt/agricultureplatform/models/_enums.py +103 -0
  18. azure/mgmt/agricultureplatform/models/_models.py +1037 -0
  19. azure/mgmt/agricultureplatform/models/_patch.py +21 -0
  20. azure/mgmt/agricultureplatform/operations/__init__.py +27 -0
  21. azure/mgmt/agricultureplatform/operations/_operations.py +1303 -0
  22. azure/mgmt/agricultureplatform/operations/_patch.py +21 -0
  23. azure/mgmt/agricultureplatform/py.typed +1 -0
  24. azure_mgmt_agricultureplatform-1.0.0b1.dist-info/METADATA +94 -0
  25. azure_mgmt_agricultureplatform-1.0.0b1.dist-info/RECORD +28 -0
  26. azure_mgmt_agricultureplatform-1.0.0b1.dist-info/WHEEL +5 -0
  27. azure_mgmt_agricultureplatform-1.0.0b1.dist-info/licenses/LICENSE +21 -0
  28. azure_mgmt_agricultureplatform-1.0.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1337 @@
1
+ # pylint: disable=line-too-long,useless-suppression,too-many-lines
2
+ # coding=utf-8
3
+ # --------------------------------------------------------------------------
4
+ # Copyright (c) Microsoft Corporation. All rights reserved.
5
+ # Licensed under the MIT License. See License.txt in the project root for license information.
6
+ # Code generated by Microsoft (R) Python Code Generator.
7
+ # Changes may cause incorrect behavior and will be lost if the code is regenerated.
8
+ # --------------------------------------------------------------------------
9
+ # pylint: disable=protected-access, broad-except
10
+
11
+ import copy
12
+ import calendar
13
+ import decimal
14
+ import functools
15
+ import sys
16
+ import logging
17
+ import base64
18
+ import re
19
+ import typing
20
+ import enum
21
+ import email.utils
22
+ from datetime import datetime, date, time, timedelta, timezone
23
+ from json import JSONEncoder
24
+ import xml.etree.ElementTree as ET
25
+ from collections.abc import MutableMapping
26
+ from typing_extensions import Self
27
+ import isodate
28
+ from azure.core.exceptions import DeserializationError
29
+ from azure.core import CaseInsensitiveEnumMeta
30
+ from azure.core.pipeline import PipelineResponse
31
+ from azure.core.serialization import _Null
32
+ from azure.core.rest import HttpResponse
33
+
34
+ _LOGGER = logging.getLogger(__name__)
35
+
36
+ __all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"]
37
+
38
+ TZ_UTC = timezone.utc
39
+ _T = typing.TypeVar("_T")
40
+ _NONE_TYPE = type(None)
41
+
42
+
43
+ def _timedelta_as_isostr(td: timedelta) -> str:
44
+ """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
45
+
46
+ Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
47
+
48
+ :param timedelta td: The timedelta to convert
49
+ :rtype: str
50
+ :return: ISO8601 version of this timedelta
51
+ """
52
+
53
+ # Split seconds to larger units
54
+ seconds = td.total_seconds()
55
+ minutes, seconds = divmod(seconds, 60)
56
+ hours, minutes = divmod(minutes, 60)
57
+ days, hours = divmod(hours, 24)
58
+
59
+ days, hours, minutes = list(map(int, (days, hours, minutes)))
60
+ seconds = round(seconds, 6)
61
+
62
+ # Build date
63
+ date_str = ""
64
+ if days:
65
+ date_str = "%sD" % days
66
+
67
+ if hours or minutes or seconds:
68
+ # Build time
69
+ time_str = "T"
70
+
71
+ # Hours
72
+ bigger_exists = date_str or hours
73
+ if bigger_exists:
74
+ time_str += "{:02}H".format(hours)
75
+
76
+ # Minutes
77
+ bigger_exists = bigger_exists or minutes
78
+ if bigger_exists:
79
+ time_str += "{:02}M".format(minutes)
80
+
81
+ # Seconds
82
+ try:
83
+ if seconds.is_integer():
84
+ seconds_string = "{:02}".format(int(seconds))
85
+ else:
86
+ # 9 chars long w/ leading 0, 6 digits after decimal
87
+ seconds_string = "%09.6f" % seconds
88
+ # Remove trailing zeros
89
+ seconds_string = seconds_string.rstrip("0")
90
+ except AttributeError: # int.is_integer() raises
91
+ seconds_string = "{:02}".format(seconds)
92
+
93
+ time_str += "{}S".format(seconds_string)
94
+ else:
95
+ time_str = ""
96
+
97
+ return "P" + date_str + time_str
98
+
99
+
100
+ def _serialize_bytes(o, format: typing.Optional[str] = None) -> str:
101
+ encoded = base64.b64encode(o).decode()
102
+ if format == "base64url":
103
+ return encoded.strip("=").replace("+", "-").replace("/", "_")
104
+ return encoded
105
+
106
+
107
+ def _serialize_datetime(o, format: typing.Optional[str] = None):
108
+ if hasattr(o, "year") and hasattr(o, "hour"):
109
+ if format == "rfc7231":
110
+ return email.utils.format_datetime(o, usegmt=True)
111
+ if format == "unix-timestamp":
112
+ return int(calendar.timegm(o.utctimetuple()))
113
+
114
+ # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set)
115
+ if not o.tzinfo:
116
+ iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat()
117
+ else:
118
+ iso_formatted = o.astimezone(TZ_UTC).isoformat()
119
+ # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt)
120
+ return iso_formatted.replace("+00:00", "Z")
121
+ # Next try datetime.date or datetime.time
122
+ return o.isoformat()
123
+
124
+
125
+ def _is_readonly(p):
126
+ try:
127
+ return p._visibility == ["read"]
128
+ except AttributeError:
129
+ return False
130
+
131
+
132
+ class SdkJSONEncoder(JSONEncoder):
133
+ """A JSON encoder that's capable of serializing datetime objects and bytes."""
134
+
135
+ def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs):
136
+ super().__init__(*args, **kwargs)
137
+ self.exclude_readonly = exclude_readonly
138
+ self.format = format
139
+
140
+ def default(self, o): # pylint: disable=too-many-return-statements
141
+ if _is_model(o):
142
+ if self.exclude_readonly:
143
+ readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
144
+ return {k: v for k, v in o.items() if k not in readonly_props}
145
+ return dict(o.items())
146
+ try:
147
+ return super(SdkJSONEncoder, self).default(o)
148
+ except TypeError:
149
+ if isinstance(o, _Null):
150
+ return None
151
+ if isinstance(o, decimal.Decimal):
152
+ return float(o)
153
+ if isinstance(o, (bytes, bytearray)):
154
+ return _serialize_bytes(o, self.format)
155
+ try:
156
+ # First try datetime.datetime
157
+ return _serialize_datetime(o, self.format)
158
+ except AttributeError:
159
+ pass
160
+ # Last, try datetime.timedelta
161
+ try:
162
+ return _timedelta_as_isostr(o)
163
+ except AttributeError:
164
+ # This will be raised when it hits value.total_seconds in the method above
165
+ pass
166
+ return super(SdkJSONEncoder, self).default(o)
167
+
168
+
169
+ _VALID_DATE = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" + r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
170
+ _VALID_RFC7231 = re.compile(
171
+ r"(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d{2}\s"
172
+ r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
173
+ )
174
+
175
+ _ARRAY_ENCODE_MAPPING = {
176
+ "pipeDelimited": "|",
177
+ "spaceDelimited": " ",
178
+ "commaDelimited": ",",
179
+ "newlineDelimited": "\n",
180
+ }
181
+
182
+
183
+ def _deserialize_array_encoded(delimit: str, attr):
184
+ if isinstance(attr, str):
185
+ if attr == "":
186
+ return []
187
+ return attr.split(delimit)
188
+ return attr
189
+
190
+
191
+ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
192
+ """Deserialize ISO-8601 formatted string into Datetime object.
193
+
194
+ :param str attr: response string to be deserialized.
195
+ :rtype: ~datetime.datetime
196
+ :returns: The datetime object from that input
197
+ """
198
+ if isinstance(attr, datetime):
199
+ # i'm already deserialized
200
+ return attr
201
+ attr = attr.upper()
202
+ match = _VALID_DATE.match(attr)
203
+ if not match:
204
+ raise ValueError("Invalid datetime string: " + attr)
205
+
206
+ check_decimal = attr.split(".")
207
+ if len(check_decimal) > 1:
208
+ decimal_str = ""
209
+ for digit in check_decimal[1]:
210
+ if digit.isdigit():
211
+ decimal_str += digit
212
+ else:
213
+ break
214
+ if len(decimal_str) > 6:
215
+ attr = attr.replace(decimal_str, decimal_str[0:6])
216
+
217
+ date_obj = isodate.parse_datetime(attr)
218
+ test_utc = date_obj.utctimetuple()
219
+ if test_utc.tm_year > 9999 or test_utc.tm_year < 1:
220
+ raise OverflowError("Hit max or min date")
221
+ return date_obj # type: ignore[no-any-return]
222
+
223
+
224
+ def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime:
225
+ """Deserialize RFC7231 formatted string into Datetime object.
226
+
227
+ :param str attr: response string to be deserialized.
228
+ :rtype: ~datetime.datetime
229
+ :returns: The datetime object from that input
230
+ """
231
+ if isinstance(attr, datetime):
232
+ # i'm already deserialized
233
+ return attr
234
+ match = _VALID_RFC7231.match(attr)
235
+ if not match:
236
+ raise ValueError("Invalid datetime string: " + attr)
237
+
238
+ return email.utils.parsedate_to_datetime(attr)
239
+
240
+
241
+ def _deserialize_datetime_unix_timestamp(attr: typing.Union[float, datetime]) -> datetime:
242
+ """Deserialize unix timestamp into Datetime object.
243
+
244
+ :param str attr: response string to be deserialized.
245
+ :rtype: ~datetime.datetime
246
+ :returns: The datetime object from that input
247
+ """
248
+ if isinstance(attr, datetime):
249
+ # i'm already deserialized
250
+ return attr
251
+ return datetime.fromtimestamp(attr, TZ_UTC)
252
+
253
+
254
+ def _deserialize_date(attr: typing.Union[str, date]) -> date:
255
+ """Deserialize ISO-8601 formatted string into Date object.
256
+ :param str attr: response string to be deserialized.
257
+ :rtype: date
258
+ :returns: The date object from that input
259
+ """
260
+ # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception.
261
+ if isinstance(attr, date):
262
+ return attr
263
+ return isodate.parse_date(attr, defaultmonth=None, defaultday=None) # type: ignore
264
+
265
+
266
+ def _deserialize_time(attr: typing.Union[str, time]) -> time:
267
+ """Deserialize ISO-8601 formatted string into time object.
268
+
269
+ :param str attr: response string to be deserialized.
270
+ :rtype: datetime.time
271
+ :returns: The time object from that input
272
+ """
273
+ if isinstance(attr, time):
274
+ return attr
275
+ return isodate.parse_time(attr) # type: ignore[no-any-return]
276
+
277
+
278
+ def _deserialize_bytes(attr):
279
+ if isinstance(attr, (bytes, bytearray)):
280
+ return attr
281
+ return bytes(base64.b64decode(attr))
282
+
283
+
284
+ def _deserialize_bytes_base64(attr):
285
+ if isinstance(attr, (bytes, bytearray)):
286
+ return attr
287
+ padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore
288
+ attr = attr + padding # type: ignore
289
+ encoded = attr.replace("-", "+").replace("_", "/")
290
+ return bytes(base64.b64decode(encoded))
291
+
292
+
293
+ def _deserialize_duration(attr):
294
+ if isinstance(attr, timedelta):
295
+ return attr
296
+ return isodate.parse_duration(attr)
297
+
298
+
299
+ def _deserialize_decimal(attr):
300
+ if isinstance(attr, decimal.Decimal):
301
+ return attr
302
+ return decimal.Decimal(str(attr))
303
+
304
+
305
+ def _deserialize_int_as_str(attr):
306
+ if isinstance(attr, int):
307
+ return attr
308
+ return int(attr)
309
+
310
+
311
+ _DESERIALIZE_MAPPING = {
312
+ datetime: _deserialize_datetime,
313
+ date: _deserialize_date,
314
+ time: _deserialize_time,
315
+ bytes: _deserialize_bytes,
316
+ bytearray: _deserialize_bytes,
317
+ timedelta: _deserialize_duration,
318
+ typing.Any: lambda x: x,
319
+ decimal.Decimal: _deserialize_decimal,
320
+ }
321
+
322
+ _DESERIALIZE_MAPPING_WITHFORMAT = {
323
+ "rfc3339": _deserialize_datetime,
324
+ "rfc7231": _deserialize_datetime_rfc7231,
325
+ "unix-timestamp": _deserialize_datetime_unix_timestamp,
326
+ "base64": _deserialize_bytes,
327
+ "base64url": _deserialize_bytes_base64,
328
+ }
329
+
330
+
331
+ def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
332
+ if annotation is int and rf and rf._format == "str":
333
+ return _deserialize_int_as_str
334
+ if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
335
+ return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
336
+ if rf and rf._format:
337
+ return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
338
+ return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore
339
+
340
+
341
+ def _get_type_alias_type(module_name: str, alias_name: str):
342
+ types = {
343
+ k: v
344
+ for k, v in sys.modules[module_name].__dict__.items()
345
+ if isinstance(v, typing._GenericAlias) # type: ignore
346
+ }
347
+ if alias_name not in types:
348
+ return alias_name
349
+ return types[alias_name]
350
+
351
+
352
+ def _get_model(module_name: str, model_name: str):
353
+ models = {k: v for k, v in sys.modules[module_name].__dict__.items() if isinstance(v, type)}
354
+ module_end = module_name.rsplit(".", 1)[0]
355
+ models.update({k: v for k, v in sys.modules[module_end].__dict__.items() if isinstance(v, type)})
356
+ if isinstance(model_name, str):
357
+ model_name = model_name.split(".")[-1]
358
+ if model_name not in models:
359
+ return model_name
360
+ return models[model_name]
361
+
362
+
363
+ _UNSET = object()
364
+
365
+
366
+ class _MyMutableMapping(MutableMapping[str, typing.Any]):
367
+ def __init__(self, data: dict[str, typing.Any]) -> None:
368
+ self._data = data
369
+
370
+ def __contains__(self, key: typing.Any) -> bool:
371
+ return key in self._data
372
+
373
+ def __getitem__(self, key: str) -> typing.Any:
374
+ # If this key has been deserialized (for mutable types), we need to handle serialization
375
+ if hasattr(self, "_attr_to_rest_field"):
376
+ cache_attr = f"_deserialized_{key}"
377
+ if hasattr(self, cache_attr):
378
+ rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
379
+ if rf:
380
+ value = self._data.get(key)
381
+ if isinstance(value, (dict, list, set)):
382
+ # For mutable types, serialize and return
383
+ # But also update _data with serialized form and clear flag
384
+ # so mutations via this returned value affect _data
385
+ serialized = _serialize(value, rf._format)
386
+ # If serialized form is same type (no transformation needed),
387
+ # return _data directly so mutations work
388
+ if isinstance(serialized, type(value)) and serialized == value:
389
+ return self._data.get(key)
390
+ # Otherwise return serialized copy and clear flag
391
+ try:
392
+ object.__delattr__(self, cache_attr)
393
+ except AttributeError:
394
+ pass
395
+ # Store serialized form back
396
+ self._data[key] = serialized
397
+ return serialized
398
+ return self._data.__getitem__(key)
399
+
400
+ def __setitem__(self, key: str, value: typing.Any) -> None:
401
+ # Clear any cached deserialized value when setting through dictionary access
402
+ cache_attr = f"_deserialized_{key}"
403
+ try:
404
+ object.__delattr__(self, cache_attr)
405
+ except AttributeError:
406
+ pass
407
+ self._data.__setitem__(key, value)
408
+
409
+ def __delitem__(self, key: str) -> None:
410
+ self._data.__delitem__(key)
411
+
412
+ def __iter__(self) -> typing.Iterator[typing.Any]:
413
+ return self._data.__iter__()
414
+
415
+ def __len__(self) -> int:
416
+ return self._data.__len__()
417
+
418
+ def __ne__(self, other: typing.Any) -> bool:
419
+ return not self.__eq__(other)
420
+
421
+ def keys(self) -> typing.KeysView[str]:
422
+ """
423
+ :returns: a set-like object providing a view on D's keys
424
+ :rtype: ~typing.KeysView
425
+ """
426
+ return self._data.keys()
427
+
428
+ def values(self) -> typing.ValuesView[typing.Any]:
429
+ """
430
+ :returns: an object providing a view on D's values
431
+ :rtype: ~typing.ValuesView
432
+ """
433
+ return self._data.values()
434
+
435
+ def items(self) -> typing.ItemsView[str, typing.Any]:
436
+ """
437
+ :returns: set-like object providing a view on D's items
438
+ :rtype: ~typing.ItemsView
439
+ """
440
+ return self._data.items()
441
+
442
+ def get(self, key: str, default: typing.Any = None) -> typing.Any:
443
+ """
444
+ Get the value for key if key is in the dictionary, else default.
445
+ :param str key: The key to look up.
446
+ :param any default: The value to return if key is not in the dictionary. Defaults to None
447
+ :returns: D[k] if k in D, else d.
448
+ :rtype: any
449
+ """
450
+ try:
451
+ return self[key]
452
+ except KeyError:
453
+ return default
454
+
455
+ @typing.overload
456
+ def pop(self, key: str) -> typing.Any: ... # pylint: disable=arguments-differ
457
+
458
+ @typing.overload
459
+ def pop(self, key: str, default: _T) -> _T: ... # pylint: disable=signature-differs
460
+
461
+ @typing.overload
462
+ def pop(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs
463
+
464
+ def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
465
+ """
466
+ Removes specified key and return the corresponding value.
467
+ :param str key: The key to pop.
468
+ :param any default: The value to return if key is not in the dictionary
469
+ :returns: The value corresponding to the key.
470
+ :rtype: any
471
+ :raises KeyError: If key is not found and default is not given.
472
+ """
473
+ if default is _UNSET:
474
+ return self._data.pop(key)
475
+ return self._data.pop(key, default)
476
+
477
+ def popitem(self) -> tuple[str, typing.Any]:
478
+ """
479
+ Removes and returns some (key, value) pair
480
+ :returns: The (key, value) pair.
481
+ :rtype: tuple
482
+ :raises KeyError: if D is empty.
483
+ """
484
+ return self._data.popitem()
485
+
486
+ def clear(self) -> None:
487
+ """
488
+ Remove all items from D.
489
+ """
490
+ self._data.clear()
491
+
492
+ def update(self, *args: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=arguments-differ
493
+ """
494
+ Updates D from mapping/iterable E and F.
495
+ :param any args: Either a mapping object or an iterable of key-value pairs.
496
+ """
497
+ self._data.update(*args, **kwargs)
498
+
499
+ @typing.overload
500
+ def setdefault(self, key: str, default: None = None) -> None: ...
501
+
502
+ @typing.overload
503
+ def setdefault(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs
504
+
505
+ def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
506
+ """
507
+ Same as calling D.get(k, d), and setting D[k]=d if k not found
508
+ :param str key: The key to look up.
509
+ :param any default: The value to set if key is not in the dictionary
510
+ :returns: D[k] if k in D, else d.
511
+ :rtype: any
512
+ """
513
+ if default is _UNSET:
514
+ return self._data.setdefault(key)
515
+ return self._data.setdefault(key, default)
516
+
517
+ def __eq__(self, other: typing.Any) -> bool:
518
+ try:
519
+ other_model = self.__class__(other)
520
+ except Exception:
521
+ return False
522
+ return self._data == other_model._data
523
+
524
+ def __repr__(self) -> str:
525
+ return str(self._data)
526
+
527
+
528
+ def _is_model(obj: typing.Any) -> bool:
529
+ return getattr(obj, "_is_model", False)
530
+
531
+
532
+ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
533
+ if isinstance(o, list):
534
+ if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
535
+ return _ARRAY_ENCODE_MAPPING[format].join(o)
536
+ return [_serialize(x, format) for x in o]
537
+ if isinstance(o, dict):
538
+ return {k: _serialize(v, format) for k, v in o.items()}
539
+ if isinstance(o, set):
540
+ return {_serialize(x, format) for x in o}
541
+ if isinstance(o, tuple):
542
+ return tuple(_serialize(x, format) for x in o)
543
+ if isinstance(o, (bytes, bytearray)):
544
+ return _serialize_bytes(o, format)
545
+ if isinstance(o, decimal.Decimal):
546
+ return float(o)
547
+ if isinstance(o, enum.Enum):
548
+ return o.value
549
+ if isinstance(o, int):
550
+ if format == "str":
551
+ return str(o)
552
+ return o
553
+ try:
554
+ # First try datetime.datetime
555
+ return _serialize_datetime(o, format)
556
+ except AttributeError:
557
+ pass
558
+ # Last, try datetime.timedelta
559
+ try:
560
+ return _timedelta_as_isostr(o)
561
+ except AttributeError:
562
+ # This will be raised when it hits value.total_seconds in the method above
563
+ pass
564
+ return o
565
+
566
+
567
+ def _get_rest_field(attr_to_rest_field: dict[str, "_RestField"], rest_name: str) -> typing.Optional["_RestField"]:
568
+ try:
569
+ return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name)
570
+ except StopIteration:
571
+ return None
572
+
573
+
574
+ def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
575
+ if not rf:
576
+ return _serialize(value, None)
577
+ if rf._is_multipart_file_input:
578
+ return value
579
+ if rf._is_model:
580
+ return _deserialize(rf._type, value)
581
+ if isinstance(value, ET.Element):
582
+ value = _deserialize(rf._type, value)
583
+ return _serialize(value, rf._format)
584
+
585
+
586
+ class Model(_MyMutableMapping):
587
+ _is_model = True
588
+ # label whether current class's _attr_to_rest_field has been calculated
589
+ # could not see _attr_to_rest_field directly because subclass inherits it from parent class
590
+ _calculated: set[str] = set()
591
+
592
+ def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
593
+ class_name = self.__class__.__name__
594
+ if len(args) > 1:
595
+ raise TypeError(f"{class_name}.__init__() takes 2 positional arguments but {len(args) + 1} were given")
596
+ dict_to_pass = {
597
+ rest_field._rest_name: rest_field._default
598
+ for rest_field in self._attr_to_rest_field.values()
599
+ if rest_field._default is not _UNSET
600
+ }
601
+ if args: # pylint: disable=too-many-nested-blocks
602
+ if isinstance(args[0], ET.Element):
603
+ existed_attr_keys = []
604
+ model_meta = getattr(self, "_xml", {})
605
+
606
+ for rf in self._attr_to_rest_field.values():
607
+ prop_meta = getattr(rf, "_xml", {})
608
+ xml_name = prop_meta.get("name", rf._rest_name)
609
+ xml_ns = prop_meta.get("ns", model_meta.get("ns", None))
610
+ if xml_ns:
611
+ xml_name = "{" + xml_ns + "}" + xml_name
612
+
613
+ # attribute
614
+ if prop_meta.get("attribute", False) and args[0].get(xml_name) is not None:
615
+ existed_attr_keys.append(xml_name)
616
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].get(xml_name))
617
+ continue
618
+
619
+ # unwrapped element is array
620
+ if prop_meta.get("unwrapped", False):
621
+ # unwrapped array could either use prop items meta/prop meta
622
+ if prop_meta.get("itemsName"):
623
+ xml_name = prop_meta.get("itemsName")
624
+ xml_ns = prop_meta.get("itemNs")
625
+ if xml_ns:
626
+ xml_name = "{" + xml_ns + "}" + xml_name
627
+ items = args[0].findall(xml_name) # pyright: ignore
628
+ if len(items) > 0:
629
+ existed_attr_keys.append(xml_name)
630
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, items)
631
+ continue
632
+
633
+ # text element is primitive type
634
+ if prop_meta.get("text", False):
635
+ if args[0].text is not None:
636
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].text)
637
+ continue
638
+
639
+ # wrapped element could be normal property or array, it should only have one element
640
+ item = args[0].find(xml_name)
641
+ if item is not None:
642
+ existed_attr_keys.append(xml_name)
643
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, item)
644
+
645
+ # rest thing is additional properties
646
+ for e in args[0]:
647
+ if e.tag not in existed_attr_keys:
648
+ dict_to_pass[e.tag] = _convert_element(e)
649
+ else:
650
+ dict_to_pass.update(
651
+ {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()}
652
+ )
653
+ else:
654
+ non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field]
655
+ if non_attr_kwargs:
656
+ # actual type errors only throw the first wrong keyword arg they see, so following that.
657
+ raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'")
658
+ dict_to_pass.update(
659
+ {
660
+ self._attr_to_rest_field[k]._rest_name: _create_value(self._attr_to_rest_field[k], v)
661
+ for k, v in kwargs.items()
662
+ if v is not None
663
+ }
664
+ )
665
+ super().__init__(dict_to_pass)
666
+
667
+ def copy(self) -> "Model":
668
+ return Model(self.__dict__)
669
+
670
+ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self:
671
+ if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated:
672
+ # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping',
673
+ # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object'
674
+ mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order
675
+ attr_to_rest_field: dict[str, _RestField] = { # map attribute name to rest_field property
676
+ k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
677
+ }
678
+ annotations = {
679
+ k: v
680
+ for mro_class in mros
681
+ if hasattr(mro_class, "__annotations__")
682
+ for k, v in mro_class.__annotations__.items()
683
+ }
684
+ for attr, rf in attr_to_rest_field.items():
685
+ rf._module = cls.__module__
686
+ if not rf._type:
687
+ rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
688
+ if not rf._rest_name_input:
689
+ rf._rest_name_input = attr
690
+ cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items())
691
+ cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")
692
+
693
+ return super().__new__(cls)
694
+
695
+ def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None:
696
+ for base in cls.__bases__:
697
+ if hasattr(base, "__mapping__"):
698
+ base.__mapping__[discriminator or cls.__name__] = cls # type: ignore
699
+
700
+ @classmethod
701
+ def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]:
702
+ for v in cls.__dict__.values():
703
+ if isinstance(v, _RestField) and v._is_discriminator and v._rest_name not in exist_discriminators:
704
+ return v
705
+ return None
706
+
707
+ @classmethod
708
+ def _deserialize(cls, data, exist_discriminators):
709
+ if not hasattr(cls, "__mapping__"):
710
+ return cls(data)
711
+ discriminator = cls._get_discriminator(exist_discriminators)
712
+ if discriminator is None:
713
+ return cls(data)
714
+ exist_discriminators.append(discriminator._rest_name)
715
+ if isinstance(data, ET.Element):
716
+ model_meta = getattr(cls, "_xml", {})
717
+ prop_meta = getattr(discriminator, "_xml", {})
718
+ xml_name = prop_meta.get("name", discriminator._rest_name)
719
+ xml_ns = prop_meta.get("ns", model_meta.get("ns", None))
720
+ if xml_ns:
721
+ xml_name = "{" + xml_ns + "}" + xml_name
722
+
723
+ if data.get(xml_name) is not None:
724
+ discriminator_value = data.get(xml_name)
725
+ else:
726
+ discriminator_value = data.find(xml_name).text # pyright: ignore
727
+ else:
728
+ discriminator_value = data.get(discriminator._rest_name)
729
+ mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore # pylint: disable=no-member
730
+ return mapped_cls._deserialize(data, exist_discriminators)
731
+
732
+ def as_dict(self, *, exclude_readonly: bool = False) -> dict[str, typing.Any]:
733
+ """Return a dict that can be turned into json using json.dump.
734
+
735
+ :keyword bool exclude_readonly: Whether to remove the readonly properties.
736
+ :returns: A dict JSON compatible object
737
+ :rtype: dict
738
+ """
739
+
740
+ result = {}
741
+ readonly_props = []
742
+ if exclude_readonly:
743
+ readonly_props = [p._rest_name for p in self._attr_to_rest_field.values() if _is_readonly(p)]
744
+ for k, v in self.items():
745
+ if exclude_readonly and k in readonly_props: # pyright: ignore
746
+ continue
747
+ is_multipart_file_input = False
748
+ try:
749
+ is_multipart_file_input = next(
750
+ rf for rf in self._attr_to_rest_field.values() if rf._rest_name == k
751
+ )._is_multipart_file_input
752
+ except StopIteration:
753
+ pass
754
+ result[k] = v if is_multipart_file_input else Model._as_dict_value(v, exclude_readonly=exclude_readonly)
755
+ return result
756
+
757
+ @staticmethod
758
+ def _as_dict_value(v: typing.Any, exclude_readonly: bool = False) -> typing.Any:
759
+ if v is None or isinstance(v, _Null):
760
+ return None
761
+ if isinstance(v, (list, tuple, set)):
762
+ return type(v)(Model._as_dict_value(x, exclude_readonly=exclude_readonly) for x in v)
763
+ if isinstance(v, dict):
764
+ return {dk: Model._as_dict_value(dv, exclude_readonly=exclude_readonly) for dk, dv in v.items()}
765
+ return v.as_dict(exclude_readonly=exclude_readonly) if hasattr(v, "as_dict") else v
766
+
767
+
768
+ def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
769
+ if _is_model(obj):
770
+ return obj
771
+ return _deserialize(model_deserializer, obj)
772
+
773
+
774
+ def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
775
+ if obj is None:
776
+ return obj
777
+ return _deserialize_with_callable(if_obj_deserializer, obj)
778
+
779
+
780
+ def _deserialize_with_union(deserializers, obj):
781
+ for deserializer in deserializers:
782
+ try:
783
+ return _deserialize(deserializer, obj)
784
+ except DeserializationError:
785
+ pass
786
+ raise DeserializationError()
787
+
788
+
789
+ def _deserialize_dict(
790
+ value_deserializer: typing.Optional[typing.Callable],
791
+ module: typing.Optional[str],
792
+ obj: dict[typing.Any, typing.Any],
793
+ ):
794
+ if obj is None:
795
+ return obj
796
+ if isinstance(obj, ET.Element):
797
+ obj = {child.tag: child for child in obj}
798
+ return {k: _deserialize(value_deserializer, v, module) for k, v in obj.items()}
799
+
800
+
801
+ def _deserialize_multiple_sequence(
802
+ entry_deserializers: list[typing.Optional[typing.Callable]],
803
+ module: typing.Optional[str],
804
+ obj,
805
+ ):
806
+ if obj is None:
807
+ return obj
808
+ return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers))
809
+
810
+
811
+ def _is_array_encoded_deserializer(deserializer: functools.partial) -> bool:
812
+ return (
813
+ isinstance(deserializer, functools.partial)
814
+ and isinstance(deserializer.args[0], functools.partial)
815
+ and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
816
+ )
817
+
818
+
819
+ def _deserialize_sequence(
820
+ deserializer: typing.Optional[typing.Callable],
821
+ module: typing.Optional[str],
822
+ obj,
823
+ ):
824
+ if obj is None:
825
+ return obj
826
+ if isinstance(obj, ET.Element):
827
+ obj = list(obj)
828
+
829
+ # encoded string may be deserialized to sequence
830
+ if isinstance(obj, str) and isinstance(deserializer, functools.partial):
831
+ # for list[str]
832
+ if _is_array_encoded_deserializer(deserializer):
833
+ return deserializer(obj)
834
+
835
+ # for list[Union[...]]
836
+ if isinstance(deserializer.args[0], list):
837
+ for sub_deserializer in deserializer.args[0]:
838
+ if _is_array_encoded_deserializer(sub_deserializer):
839
+ return sub_deserializer(obj)
840
+
841
+ return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
842
+
843
+
844
+ def _sorted_annotations(types: list[typing.Any]) -> list[typing.Any]:
845
+ return sorted(
846
+ types,
847
+ key=lambda x: hasattr(x, "__name__") and x.__name__.lower() in ("str", "float", "int", "bool"),
848
+ )
849
+
850
+
851
+ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements, too-many-branches
852
+ annotation: typing.Any,
853
+ module: typing.Optional[str],
854
+ rf: typing.Optional["_RestField"] = None,
855
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
856
+ if not annotation:
857
+ return None
858
+
859
+ # is it a type alias?
860
+ if isinstance(annotation, str):
861
+ if module is not None:
862
+ annotation = _get_type_alias_type(module, annotation)
863
+
864
+ # is it a forward ref / in quotes?
865
+ if isinstance(annotation, (str, typing.ForwardRef)):
866
+ try:
867
+ model_name = annotation.__forward_arg__ # type: ignore
868
+ except AttributeError:
869
+ model_name = annotation
870
+ if module is not None:
871
+ annotation = _get_model(module, model_name) # type: ignore
872
+
873
+ try:
874
+ if module and _is_model(annotation):
875
+ if rf:
876
+ rf._is_model = True
877
+
878
+ return functools.partial(_deserialize_model, annotation) # pyright: ignore
879
+ except Exception:
880
+ pass
881
+
882
+ # is it a literal?
883
+ try:
884
+ if annotation.__origin__ is typing.Literal: # pyright: ignore
885
+ return None
886
+ except AttributeError:
887
+ pass
888
+
889
+ # is it optional?
890
+ try:
891
+ if any(a is _NONE_TYPE for a in annotation.__args__): # pyright: ignore
892
+ if len(annotation.__args__) <= 2: # pyright: ignore
893
+ if_obj_deserializer = _get_deserialize_callable_from_annotation(
894
+ next(a for a in annotation.__args__ if a is not _NONE_TYPE), module, rf # pyright: ignore
895
+ )
896
+
897
+ return functools.partial(_deserialize_with_optional, if_obj_deserializer)
898
+ # the type is Optional[Union[...]], we need to remove the None type from the Union
899
+ annotation_copy = copy.copy(annotation)
900
+ annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a is not _NONE_TYPE] # pyright: ignore
901
+ return _get_deserialize_callable_from_annotation(annotation_copy, module, rf)
902
+ except AttributeError:
903
+ pass
904
+
905
+ # is it union?
906
+ if getattr(annotation, "__origin__", None) is typing.Union:
907
+ # initial ordering is we make `string` the last deserialization option, because it is often them most generic
908
+ deserializers = [
909
+ _get_deserialize_callable_from_annotation(arg, module, rf)
910
+ for arg in _sorted_annotations(annotation.__args__) # pyright: ignore
911
+ ]
912
+
913
+ return functools.partial(_deserialize_with_union, deserializers)
914
+
915
+ try:
916
+ annotation_name = (
917
+ annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore
918
+ )
919
+ if annotation_name.lower() == "dict":
920
+ value_deserializer = _get_deserialize_callable_from_annotation(
921
+ annotation.__args__[1], module, rf # pyright: ignore
922
+ )
923
+
924
+ return functools.partial(
925
+ _deserialize_dict,
926
+ value_deserializer,
927
+ module,
928
+ )
929
+ except (AttributeError, IndexError):
930
+ pass
931
+ try:
932
+ annotation_name = (
933
+ annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore
934
+ )
935
+ if annotation_name.lower() in ["list", "set", "tuple", "sequence"]:
936
+ if len(annotation.__args__) > 1: # pyright: ignore
937
+ entry_deserializers = [
938
+ _get_deserialize_callable_from_annotation(dt, module, rf)
939
+ for dt in annotation.__args__ # pyright: ignore
940
+ ]
941
+ return functools.partial(_deserialize_multiple_sequence, entry_deserializers, module)
942
+ deserializer = _get_deserialize_callable_from_annotation(
943
+ annotation.__args__[0], module, rf # pyright: ignore
944
+ )
945
+
946
+ return functools.partial(_deserialize_sequence, deserializer, module)
947
+ except (TypeError, IndexError, AttributeError, SyntaxError):
948
+ pass
949
+
950
+ def _deserialize_default(
951
+ deserializer,
952
+ obj,
953
+ ):
954
+ if obj is None:
955
+ return obj
956
+ try:
957
+ return _deserialize_with_callable(deserializer, obj)
958
+ except Exception:
959
+ pass
960
+ return obj
961
+
962
+ if get_deserializer(annotation, rf):
963
+ return functools.partial(_deserialize_default, get_deserializer(annotation, rf))
964
+
965
+ return functools.partial(_deserialize_default, annotation)
966
+
967
+
968
+ def _deserialize_with_callable(
969
+ deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]],
970
+ value: typing.Any,
971
+ ): # pylint: disable=too-many-return-statements
972
+ try:
973
+ if value is None or isinstance(value, _Null):
974
+ return None
975
+ if isinstance(value, ET.Element):
976
+ if deserializer is str:
977
+ return value.text or ""
978
+ if deserializer is int:
979
+ return int(value.text) if value.text else None
980
+ if deserializer is float:
981
+ return float(value.text) if value.text else None
982
+ if deserializer is bool:
983
+ return value.text == "true" if value.text else None
984
+ if deserializer is None:
985
+ return value
986
+ if deserializer in [int, float, bool]:
987
+ return deserializer(value)
988
+ if isinstance(deserializer, CaseInsensitiveEnumMeta):
989
+ try:
990
+ return deserializer(value)
991
+ except ValueError:
992
+ # for unknown value, return raw value
993
+ return value
994
+ if isinstance(deserializer, type) and issubclass(deserializer, Model):
995
+ return deserializer._deserialize(value, [])
996
+ return typing.cast(typing.Callable[[typing.Any], typing.Any], deserializer)(value)
997
+ except Exception as e:
998
+ raise DeserializationError() from e
999
+
1000
+
1001
+ def _deserialize(
1002
+ deserializer: typing.Any,
1003
+ value: typing.Any,
1004
+ module: typing.Optional[str] = None,
1005
+ rf: typing.Optional["_RestField"] = None,
1006
+ format: typing.Optional[str] = None,
1007
+ ) -> typing.Any:
1008
+ if isinstance(value, PipelineResponse):
1009
+ value = value.http_response.json()
1010
+ if rf is None and format:
1011
+ rf = _RestField(format=format)
1012
+ if not isinstance(deserializer, functools.partial):
1013
+ deserializer = _get_deserialize_callable_from_annotation(deserializer, module, rf)
1014
+ return _deserialize_with_callable(deserializer, value)
1015
+
1016
+
1017
+ def _failsafe_deserialize(
1018
+ deserializer: typing.Any,
1019
+ response: HttpResponse,
1020
+ module: typing.Optional[str] = None,
1021
+ rf: typing.Optional["_RestField"] = None,
1022
+ format: typing.Optional[str] = None,
1023
+ ) -> typing.Any:
1024
+ try:
1025
+ return _deserialize(deserializer, response.json(), module, rf, format)
1026
+ except Exception: # pylint: disable=broad-except
1027
+ _LOGGER.warning(
1028
+ "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
1029
+ )
1030
+ return None
1031
+
1032
+
1033
+ def _failsafe_deserialize_xml(
1034
+ deserializer: typing.Any,
1035
+ response: HttpResponse,
1036
+ ) -> typing.Any:
1037
+ try:
1038
+ return _deserialize_xml(deserializer, response.text())
1039
+ except Exception: # pylint: disable=broad-except
1040
+ _LOGGER.warning(
1041
+ "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
1042
+ )
1043
+ return None
1044
+
1045
+
1046
+ class _RestField:
1047
+ def __init__(
1048
+ self,
1049
+ *,
1050
+ name: typing.Optional[str] = None,
1051
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
1052
+ is_discriminator: bool = False,
1053
+ visibility: typing.Optional[list[str]] = None,
1054
+ default: typing.Any = _UNSET,
1055
+ format: typing.Optional[str] = None,
1056
+ is_multipart_file_input: bool = False,
1057
+ xml: typing.Optional[dict[str, typing.Any]] = None,
1058
+ ):
1059
+ self._type = type
1060
+ self._rest_name_input = name
1061
+ self._module: typing.Optional[str] = None
1062
+ self._is_discriminator = is_discriminator
1063
+ self._visibility = visibility
1064
+ self._is_model = False
1065
+ self._default = default
1066
+ self._format = format
1067
+ self._is_multipart_file_input = is_multipart_file_input
1068
+ self._xml = xml if xml is not None else {}
1069
+
1070
+ @property
1071
+ def _class_type(self) -> typing.Any:
1072
+ result = getattr(self._type, "args", [None])[0]
1073
+ # type may be wrapped by nested functools.partial so we need to check for that
1074
+ if isinstance(result, functools.partial):
1075
+ return getattr(result, "args", [None])[0]
1076
+ return result
1077
+
1078
+ @property
1079
+ def _rest_name(self) -> str:
1080
+ if self._rest_name_input is None:
1081
+ raise ValueError("Rest name was never set")
1082
+ return self._rest_name_input
1083
+
1084
+ def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
1085
+ # by this point, type and rest_name will have a value bc we default
1086
+ # them in __new__ of the Model class
1087
+ # Use _data.get() directly to avoid triggering __getitem__ which clears the cache
1088
+ item = obj._data.get(self._rest_name)
1089
+ if item is None:
1090
+ return item
1091
+ if self._is_model:
1092
+ return item
1093
+
1094
+ # For mutable types, we want mutations to directly affect _data
1095
+ # Check if we've already deserialized this value
1096
+ cache_attr = f"_deserialized_{self._rest_name}"
1097
+ if hasattr(obj, cache_attr):
1098
+ # Return the value from _data directly (it's been deserialized in place)
1099
+ return obj._data.get(self._rest_name)
1100
+
1101
+ deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
1102
+
1103
+ # For mutable types, store the deserialized value back in _data
1104
+ # so mutations directly affect _data
1105
+ if isinstance(deserialized, (dict, list, set)):
1106
+ obj._data[self._rest_name] = deserialized
1107
+ object.__setattr__(obj, cache_attr, True) # Mark as deserialized
1108
+ return deserialized
1109
+
1110
+ return deserialized
1111
+
1112
+ def __set__(self, obj: Model, value) -> None:
1113
+ # Clear the cached deserialized object when setting a new value
1114
+ cache_attr = f"_deserialized_{self._rest_name}"
1115
+ if hasattr(obj, cache_attr):
1116
+ object.__delattr__(obj, cache_attr)
1117
+
1118
+ if value is None:
1119
+ # we want to wipe out entries if users set attr to None
1120
+ try:
1121
+ obj.__delitem__(self._rest_name)
1122
+ except KeyError:
1123
+ pass
1124
+ return
1125
+ if self._is_model:
1126
+ if not _is_model(value):
1127
+ value = _deserialize(self._type, value)
1128
+ obj.__setitem__(self._rest_name, value)
1129
+ return
1130
+ obj.__setitem__(self._rest_name, _serialize(value, self._format))
1131
+
1132
+ def _get_deserialize_callable_from_annotation(
1133
+ self, annotation: typing.Any
1134
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
1135
+ return _get_deserialize_callable_from_annotation(annotation, self._module, self)
1136
+
1137
+
1138
+ def rest_field(
1139
+ *,
1140
+ name: typing.Optional[str] = None,
1141
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
1142
+ visibility: typing.Optional[list[str]] = None,
1143
+ default: typing.Any = _UNSET,
1144
+ format: typing.Optional[str] = None,
1145
+ is_multipart_file_input: bool = False,
1146
+ xml: typing.Optional[dict[str, typing.Any]] = None,
1147
+ ) -> typing.Any:
1148
+ return _RestField(
1149
+ name=name,
1150
+ type=type,
1151
+ visibility=visibility,
1152
+ default=default,
1153
+ format=format,
1154
+ is_multipart_file_input=is_multipart_file_input,
1155
+ xml=xml,
1156
+ )
1157
+
1158
+
1159
+ def rest_discriminator(
1160
+ *,
1161
+ name: typing.Optional[str] = None,
1162
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
1163
+ visibility: typing.Optional[list[str]] = None,
1164
+ xml: typing.Optional[dict[str, typing.Any]] = None,
1165
+ ) -> typing.Any:
1166
+ return _RestField(name=name, type=type, is_discriminator=True, visibility=visibility, xml=xml)
1167
+
1168
+
1169
+ def serialize_xml(model: Model, exclude_readonly: bool = False) -> str:
1170
+ """Serialize a model to XML.
1171
+
1172
+ :param Model model: The model to serialize.
1173
+ :param bool exclude_readonly: Whether to exclude readonly properties.
1174
+ :returns: The XML representation of the model.
1175
+ :rtype: str
1176
+ """
1177
+ return ET.tostring(_get_element(model, exclude_readonly), encoding="unicode") # type: ignore
1178
+
1179
+
1180
+ def _get_element(
1181
+ o: typing.Any,
1182
+ exclude_readonly: bool = False,
1183
+ parent_meta: typing.Optional[dict[str, typing.Any]] = None,
1184
+ wrapped_element: typing.Optional[ET.Element] = None,
1185
+ ) -> typing.Union[ET.Element, list[ET.Element]]:
1186
+ if _is_model(o):
1187
+ model_meta = getattr(o, "_xml", {})
1188
+
1189
+ # if prop is a model, then use the prop element directly, else generate a wrapper of model
1190
+ if wrapped_element is None:
1191
+ wrapped_element = _create_xml_element(
1192
+ model_meta.get("name", o.__class__.__name__),
1193
+ model_meta.get("prefix"),
1194
+ model_meta.get("ns"),
1195
+ )
1196
+
1197
+ readonly_props = []
1198
+ if exclude_readonly:
1199
+ readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
1200
+
1201
+ for k, v in o.items():
1202
+ # do not serialize readonly properties
1203
+ if exclude_readonly and k in readonly_props:
1204
+ continue
1205
+
1206
+ prop_rest_field = _get_rest_field(o._attr_to_rest_field, k)
1207
+ if prop_rest_field:
1208
+ prop_meta = getattr(prop_rest_field, "_xml").copy()
1209
+ # use the wire name as xml name if no specific name is set
1210
+ if prop_meta.get("name") is None:
1211
+ prop_meta["name"] = k
1212
+ else:
1213
+ # additional properties will not have rest field, use the wire name as xml name
1214
+ prop_meta = {"name": k}
1215
+
1216
+ # if no ns for prop, use model's
1217
+ if prop_meta.get("ns") is None and model_meta.get("ns"):
1218
+ prop_meta["ns"] = model_meta.get("ns")
1219
+ prop_meta["prefix"] = model_meta.get("prefix")
1220
+
1221
+ if prop_meta.get("unwrapped", False):
1222
+ # unwrapped could only set on array
1223
+ wrapped_element.extend(_get_element(v, exclude_readonly, prop_meta))
1224
+ elif prop_meta.get("text", False):
1225
+ # text could only set on primitive type
1226
+ wrapped_element.text = _get_primitive_type_value(v)
1227
+ elif prop_meta.get("attribute", False):
1228
+ xml_name = prop_meta.get("name", k)
1229
+ if prop_meta.get("ns"):
1230
+ ET.register_namespace(prop_meta.get("prefix"), prop_meta.get("ns")) # pyright: ignore
1231
+ xml_name = "{" + prop_meta.get("ns") + "}" + xml_name # pyright: ignore
1232
+ # attribute should be primitive type
1233
+ wrapped_element.set(xml_name, _get_primitive_type_value(v))
1234
+ else:
1235
+ # other wrapped prop element
1236
+ wrapped_element.append(_get_wrapped_element(v, exclude_readonly, prop_meta))
1237
+ return wrapped_element
1238
+ if isinstance(o, list):
1239
+ return [_get_element(x, exclude_readonly, parent_meta) for x in o] # type: ignore
1240
+ if isinstance(o, dict):
1241
+ result = []
1242
+ for k, v in o.items():
1243
+ result.append(
1244
+ _get_wrapped_element(
1245
+ v,
1246
+ exclude_readonly,
1247
+ {
1248
+ "name": k,
1249
+ "ns": parent_meta.get("ns") if parent_meta else None,
1250
+ "prefix": parent_meta.get("prefix") if parent_meta else None,
1251
+ },
1252
+ )
1253
+ )
1254
+ return result
1255
+
1256
+ # primitive case need to create element based on parent_meta
1257
+ if parent_meta:
1258
+ return _get_wrapped_element(
1259
+ o,
1260
+ exclude_readonly,
1261
+ {
1262
+ "name": parent_meta.get("itemsName", parent_meta.get("name")),
1263
+ "prefix": parent_meta.get("itemsPrefix", parent_meta.get("prefix")),
1264
+ "ns": parent_meta.get("itemsNs", parent_meta.get("ns")),
1265
+ },
1266
+ )
1267
+
1268
+ raise ValueError("Could not serialize value into xml: " + o)
1269
+
1270
+
1271
+ def _get_wrapped_element(
1272
+ v: typing.Any,
1273
+ exclude_readonly: bool,
1274
+ meta: typing.Optional[dict[str, typing.Any]],
1275
+ ) -> ET.Element:
1276
+ wrapped_element = _create_xml_element(
1277
+ meta.get("name") if meta else None, meta.get("prefix") if meta else None, meta.get("ns") if meta else None
1278
+ )
1279
+ if isinstance(v, (dict, list)):
1280
+ wrapped_element.extend(_get_element(v, exclude_readonly, meta))
1281
+ elif _is_model(v):
1282
+ _get_element(v, exclude_readonly, meta, wrapped_element)
1283
+ else:
1284
+ wrapped_element.text = _get_primitive_type_value(v)
1285
+ return wrapped_element # type: ignore[no-any-return]
1286
+
1287
+
1288
+ def _get_primitive_type_value(v) -> str:
1289
+ if v is True:
1290
+ return "true"
1291
+ if v is False:
1292
+ return "false"
1293
+ if isinstance(v, _Null):
1294
+ return ""
1295
+ return str(v)
1296
+
1297
+
1298
+ def _create_xml_element(
1299
+ tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None
1300
+ ) -> ET.Element:
1301
+ if prefix and ns:
1302
+ ET.register_namespace(prefix, ns)
1303
+ if ns:
1304
+ return ET.Element("{" + ns + "}" + tag)
1305
+ return ET.Element(tag)
1306
+
1307
+
1308
+ def _deserialize_xml(
1309
+ deserializer: typing.Any,
1310
+ value: str,
1311
+ ) -> typing.Any:
1312
+ element = ET.fromstring(value) # nosec
1313
+ return _deserialize(deserializer, element)
1314
+
1315
+
1316
+ def _convert_element(e: ET.Element):
1317
+ # dict case
1318
+ if len(e.attrib) > 0 or len({child.tag for child in e}) > 1:
1319
+ dict_result: dict[str, typing.Any] = {}
1320
+ for child in e:
1321
+ if dict_result.get(child.tag) is not None:
1322
+ if isinstance(dict_result[child.tag], list):
1323
+ dict_result[child.tag].append(_convert_element(child))
1324
+ else:
1325
+ dict_result[child.tag] = [dict_result[child.tag], _convert_element(child)]
1326
+ else:
1327
+ dict_result[child.tag] = _convert_element(child)
1328
+ dict_result.update(e.attrib)
1329
+ return dict_result
1330
+ # array case
1331
+ if len(e) > 0:
1332
+ array_result: list[typing.Any] = []
1333
+ for child in e:
1334
+ array_result.append(_convert_element(child))
1335
+ return array_result
1336
+ # primitive case
1337
+ return e.text