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