azure-quantum 3.5.1.dev1__py3-none-any.whl → 3.6.0__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.
- azure/quantum/_client/__init__.py +2 -2
- azure/quantum/_client/_client.py +18 -57
- azure/quantum/_client/_configuration.py +13 -22
- azure/quantum/_client/_patch.py +7 -6
- azure/quantum/_client/_utils/__init__.py +6 -0
- azure/quantum/_client/{_model_base.py → _utils/model_base.py} +210 -45
- azure/quantum/_client/{_serialization.py → _utils/serialization.py} +74 -151
- azure/quantum/_client/_validation.py +66 -0
- azure/quantum/_client/_version.py +1 -1
- azure/quantum/_client/aio/__init__.py +29 -0
- azure/quantum/_client/aio/_client.py +110 -0
- azure/quantum/_client/aio/_configuration.py +75 -0
- azure/quantum/_client/aio/_patch.py +21 -0
- azure/quantum/_client/aio/operations/__init__.py +25 -0
- azure/quantum/_client/aio/operations/_operations.py +1988 -0
- azure/quantum/_client/aio/operations/_patch.py +21 -0
- azure/quantum/_client/models/__init__.py +8 -4
- azure/quantum/_client/models/_enums.py +28 -23
- azure/quantum/_client/models/_models.py +198 -106
- azure/quantum/_client/models/_patch.py +7 -6
- azure/quantum/_client/operations/__init__.py +2 -12
- azure/quantum/_client/operations/_operations.py +900 -715
- azure/quantum/_client/operations/_patch.py +7 -6
- azure/quantum/_constants.py +5 -0
- azure/quantum/_mgmt_client.py +18 -8
- azure/quantum/_workspace_connection_params.py +27 -2
- azure/quantum/job/base_job.py +8 -0
- azure/quantum/job/job.py +1 -1
- azure/quantum/job/session.py +11 -0
- azure/quantum/target/target.py +5 -1
- azure/quantum/target/target_factory.py +14 -7
- azure/quantum/version.py +1 -1
- azure/quantum/workspace.py +35 -31
- {azure_quantum-3.5.1.dev1.dist-info → azure_quantum-3.6.0.dist-info}/METADATA +1 -1
- azure_quantum-3.6.0.dist-info/RECORD +74 -0
- azure_quantum-3.5.1.dev1.dist-info/RECORD +0 -65
- {azure_quantum-3.5.1.dev1.dist-info → azure_quantum-3.6.0.dist-info}/WHEEL +0 -0
- {azure_quantum-3.5.1.dev1.dist-info → azure_quantum-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
# pylint: disable=too-many-lines
|
|
1
|
+
# pylint: disable=line-too-long,useless-suppression,too-many-lines
|
|
2
2
|
# coding=utf-8
|
|
3
3
|
# --------------------------------------------------------------------------
|
|
4
4
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
5
|
-
# Licensed under the MIT License. See License.txt in the project root for
|
|
6
|
-
#
|
|
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.
|
|
7
8
|
# --------------------------------------------------------------------------
|
|
8
9
|
# pylint: disable=protected-access, broad-except
|
|
9
10
|
|
|
@@ -21,17 +22,14 @@ import email.utils
|
|
|
21
22
|
from datetime import datetime, date, time, timedelta, timezone
|
|
22
23
|
from json import JSONEncoder
|
|
23
24
|
import xml.etree.ElementTree as ET
|
|
25
|
+
from collections.abc import MutableMapping
|
|
24
26
|
from typing_extensions import Self
|
|
25
27
|
import isodate
|
|
26
28
|
from azure.core.exceptions import DeserializationError
|
|
27
29
|
from azure.core import CaseInsensitiveEnumMeta
|
|
28
30
|
from azure.core.pipeline import PipelineResponse
|
|
29
31
|
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
|
|
32
|
+
from azure.core.rest import HttpResponse
|
|
35
33
|
|
|
36
34
|
_LOGGER = logging.getLogger(__name__)
|
|
37
35
|
|
|
@@ -173,6 +171,21 @@ _VALID_RFC7231 = re.compile(
|
|
|
173
171
|
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
172
|
)
|
|
175
173
|
|
|
174
|
+
_ARRAY_ENCODE_MAPPING = {
|
|
175
|
+
"pipeDelimited": "|",
|
|
176
|
+
"spaceDelimited": " ",
|
|
177
|
+
"commaDelimited": ",",
|
|
178
|
+
"newlineDelimited": "\n",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _deserialize_array_encoded(delimit: str, attr):
|
|
183
|
+
if isinstance(attr, str):
|
|
184
|
+
if attr == "":
|
|
185
|
+
return []
|
|
186
|
+
return attr.split(delimit)
|
|
187
|
+
return attr
|
|
188
|
+
|
|
176
189
|
|
|
177
190
|
def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
|
|
178
191
|
"""Deserialize ISO-8601 formatted string into Datetime object.
|
|
@@ -317,6 +330,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
|
|
|
317
330
|
def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
|
|
318
331
|
if annotation is int and rf and rf._format == "str":
|
|
319
332
|
return _deserialize_int_as_str
|
|
333
|
+
if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
|
|
334
|
+
return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
|
|
320
335
|
if rf and rf._format:
|
|
321
336
|
return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
|
|
322
337
|
return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore
|
|
@@ -347,17 +362,47 @@ def _get_model(module_name: str, model_name: str):
|
|
|
347
362
|
_UNSET = object()
|
|
348
363
|
|
|
349
364
|
|
|
350
|
-
class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
351
|
-
def __init__(self, data:
|
|
365
|
+
class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
366
|
+
def __init__(self, data: dict[str, typing.Any]) -> None:
|
|
352
367
|
self._data = data
|
|
353
368
|
|
|
354
369
|
def __contains__(self, key: typing.Any) -> bool:
|
|
355
370
|
return key in self._data
|
|
356
371
|
|
|
357
372
|
def __getitem__(self, key: str) -> typing.Any:
|
|
373
|
+
# If this key has been deserialized (for mutable types), we need to handle serialization
|
|
374
|
+
if hasattr(self, "_attr_to_rest_field"):
|
|
375
|
+
cache_attr = f"_deserialized_{key}"
|
|
376
|
+
if hasattr(self, cache_attr):
|
|
377
|
+
rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key)
|
|
378
|
+
if rf:
|
|
379
|
+
value = self._data.get(key)
|
|
380
|
+
if isinstance(value, (dict, list, set)):
|
|
381
|
+
# For mutable types, serialize and return
|
|
382
|
+
# But also update _data with serialized form and clear flag
|
|
383
|
+
# so mutations via this returned value affect _data
|
|
384
|
+
serialized = _serialize(value, rf._format)
|
|
385
|
+
# If serialized form is same type (no transformation needed),
|
|
386
|
+
# return _data directly so mutations work
|
|
387
|
+
if isinstance(serialized, type(value)) and serialized == value:
|
|
388
|
+
return self._data.get(key)
|
|
389
|
+
# Otherwise return serialized copy and clear flag
|
|
390
|
+
try:
|
|
391
|
+
object.__delattr__(self, cache_attr)
|
|
392
|
+
except AttributeError:
|
|
393
|
+
pass
|
|
394
|
+
# Store serialized form back
|
|
395
|
+
self._data[key] = serialized
|
|
396
|
+
return serialized
|
|
358
397
|
return self._data.__getitem__(key)
|
|
359
398
|
|
|
360
399
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
400
|
+
# Clear any cached deserialized value when setting through dictionary access
|
|
401
|
+
cache_attr = f"_deserialized_{key}"
|
|
402
|
+
try:
|
|
403
|
+
object.__delattr__(self, cache_attr)
|
|
404
|
+
except AttributeError:
|
|
405
|
+
pass
|
|
361
406
|
self._data.__setitem__(key, value)
|
|
362
407
|
|
|
363
408
|
def __delitem__(self, key: str) -> None:
|
|
@@ -373,50 +418,97 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]): # pylint: disable=uns
|
|
|
373
418
|
return not self.__eq__(other)
|
|
374
419
|
|
|
375
420
|
def keys(self) -> typing.KeysView[str]:
|
|
421
|
+
"""
|
|
422
|
+
:returns: a set-like object providing a view on D's keys
|
|
423
|
+
:rtype: ~typing.KeysView
|
|
424
|
+
"""
|
|
376
425
|
return self._data.keys()
|
|
377
426
|
|
|
378
427
|
def values(self) -> typing.ValuesView[typing.Any]:
|
|
428
|
+
"""
|
|
429
|
+
:returns: an object providing a view on D's values
|
|
430
|
+
:rtype: ~typing.ValuesView
|
|
431
|
+
"""
|
|
379
432
|
return self._data.values()
|
|
380
433
|
|
|
381
434
|
def items(self) -> typing.ItemsView[str, typing.Any]:
|
|
435
|
+
"""
|
|
436
|
+
:returns: set-like object providing a view on D's items
|
|
437
|
+
:rtype: ~typing.ItemsView
|
|
438
|
+
"""
|
|
382
439
|
return self._data.items()
|
|
383
440
|
|
|
384
441
|
def get(self, key: str, default: typing.Any = None) -> typing.Any:
|
|
442
|
+
"""
|
|
443
|
+
Get the value for key if key is in the dictionary, else default.
|
|
444
|
+
:param str key: The key to look up.
|
|
445
|
+
:param any default: The value to return if key is not in the dictionary. Defaults to None
|
|
446
|
+
:returns: D[k] if k in D, else d.
|
|
447
|
+
:rtype: any
|
|
448
|
+
"""
|
|
385
449
|
try:
|
|
386
450
|
return self[key]
|
|
387
451
|
except KeyError:
|
|
388
452
|
return default
|
|
389
453
|
|
|
390
454
|
@typing.overload
|
|
391
|
-
def pop(self, key: str) -> typing.Any: ...
|
|
455
|
+
def pop(self, key: str) -> typing.Any: ... # pylint: disable=arguments-differ
|
|
392
456
|
|
|
393
457
|
@typing.overload
|
|
394
|
-
def pop(self, key: str, default: _T) -> _T: ...
|
|
458
|
+
def pop(self, key: str, default: _T) -> _T: ... # pylint: disable=signature-differs
|
|
395
459
|
|
|
396
460
|
@typing.overload
|
|
397
|
-
def pop(self, key: str, default: typing.Any) -> typing.Any: ...
|
|
461
|
+
def pop(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs
|
|
398
462
|
|
|
399
463
|
def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
|
|
464
|
+
"""
|
|
465
|
+
Removes specified key and return the corresponding value.
|
|
466
|
+
:param str key: The key to pop.
|
|
467
|
+
:param any default: The value to return if key is not in the dictionary
|
|
468
|
+
:returns: The value corresponding to the key.
|
|
469
|
+
:rtype: any
|
|
470
|
+
:raises KeyError: If key is not found and default is not given.
|
|
471
|
+
"""
|
|
400
472
|
if default is _UNSET:
|
|
401
473
|
return self._data.pop(key)
|
|
402
474
|
return self._data.pop(key, default)
|
|
403
475
|
|
|
404
|
-
def popitem(self) ->
|
|
476
|
+
def popitem(self) -> tuple[str, typing.Any]:
|
|
477
|
+
"""
|
|
478
|
+
Removes and returns some (key, value) pair
|
|
479
|
+
:returns: The (key, value) pair.
|
|
480
|
+
:rtype: tuple
|
|
481
|
+
:raises KeyError: if D is empty.
|
|
482
|
+
"""
|
|
405
483
|
return self._data.popitem()
|
|
406
484
|
|
|
407
485
|
def clear(self) -> None:
|
|
486
|
+
"""
|
|
487
|
+
Remove all items from D.
|
|
488
|
+
"""
|
|
408
489
|
self._data.clear()
|
|
409
490
|
|
|
410
|
-
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
491
|
+
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=arguments-differ
|
|
492
|
+
"""
|
|
493
|
+
Updates D from mapping/iterable E and F.
|
|
494
|
+
:param any args: Either a mapping object or an iterable of key-value pairs.
|
|
495
|
+
"""
|
|
411
496
|
self._data.update(*args, **kwargs)
|
|
412
497
|
|
|
413
498
|
@typing.overload
|
|
414
499
|
def setdefault(self, key: str, default: None = None) -> None: ...
|
|
415
500
|
|
|
416
501
|
@typing.overload
|
|
417
|
-
def setdefault(self, key: str, default: typing.Any) -> typing.Any: ...
|
|
502
|
+
def setdefault(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs
|
|
418
503
|
|
|
419
504
|
def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
|
|
505
|
+
"""
|
|
506
|
+
Same as calling D.get(k, d), and setting D[k]=d if k not found
|
|
507
|
+
:param str key: The key to look up.
|
|
508
|
+
:param any default: The value to set if key is not in the dictionary
|
|
509
|
+
:returns: D[k] if k in D, else d.
|
|
510
|
+
:rtype: any
|
|
511
|
+
"""
|
|
420
512
|
if default is _UNSET:
|
|
421
513
|
return self._data.setdefault(key)
|
|
422
514
|
return self._data.setdefault(key, default)
|
|
@@ -438,6 +530,8 @@ def _is_model(obj: typing.Any) -> bool:
|
|
|
438
530
|
|
|
439
531
|
def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
|
|
440
532
|
if isinstance(o, list):
|
|
533
|
+
if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
|
|
534
|
+
return _ARRAY_ENCODE_MAPPING[format].join(o)
|
|
441
535
|
return [_serialize(x, format) for x in o]
|
|
442
536
|
if isinstance(o, dict):
|
|
443
537
|
return {k: _serialize(v, format) for k, v in o.items()}
|
|
@@ -469,9 +563,7 @@ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-m
|
|
|
469
563
|
return o
|
|
470
564
|
|
|
471
565
|
|
|
472
|
-
def _get_rest_field(
|
|
473
|
-
attr_to_rest_field: typing.Dict[str, "_RestField"], rest_name: str
|
|
474
|
-
) -> typing.Optional["_RestField"]:
|
|
566
|
+
def _get_rest_field(attr_to_rest_field: dict[str, "_RestField"], rest_name: str) -> typing.Optional["_RestField"]:
|
|
475
567
|
try:
|
|
476
568
|
return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name)
|
|
477
569
|
except StopIteration:
|
|
@@ -494,7 +586,7 @@ class Model(_MyMutableMapping):
|
|
|
494
586
|
_is_model = True
|
|
495
587
|
# label whether current class's _attr_to_rest_field has been calculated
|
|
496
588
|
# could not see _attr_to_rest_field directly because subclass inherits it from parent class
|
|
497
|
-
_calculated:
|
|
589
|
+
_calculated: set[str] = set()
|
|
498
590
|
|
|
499
591
|
def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
500
592
|
class_name = self.__class__.__name__
|
|
@@ -579,7 +671,7 @@ class Model(_MyMutableMapping):
|
|
|
579
671
|
# we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping',
|
|
580
672
|
# 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object'
|
|
581
673
|
mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order
|
|
582
|
-
attr_to_rest_field:
|
|
674
|
+
attr_to_rest_field: dict[str, _RestField] = { # map attribute name to rest_field property
|
|
583
675
|
k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
|
|
584
676
|
}
|
|
585
677
|
annotations = {
|
|
@@ -594,10 +686,10 @@ class Model(_MyMutableMapping):
|
|
|
594
686
|
rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
|
|
595
687
|
if not rf._rest_name_input:
|
|
596
688
|
rf._rest_name_input = attr
|
|
597
|
-
cls._attr_to_rest_field:
|
|
689
|
+
cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items())
|
|
598
690
|
cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")
|
|
599
691
|
|
|
600
|
-
return super().__new__(cls)
|
|
692
|
+
return super().__new__(cls)
|
|
601
693
|
|
|
602
694
|
def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None:
|
|
603
695
|
for base in cls.__bases__:
|
|
@@ -633,10 +725,10 @@ class Model(_MyMutableMapping):
|
|
|
633
725
|
discriminator_value = data.find(xml_name).text # pyright: ignore
|
|
634
726
|
else:
|
|
635
727
|
discriminator_value = data.get(discriminator._rest_name)
|
|
636
|
-
mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore
|
|
728
|
+
mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore # pylint: disable=no-member
|
|
637
729
|
return mapped_cls._deserialize(data, exist_discriminators)
|
|
638
730
|
|
|
639
|
-
def as_dict(self, *, exclude_readonly: bool = False) ->
|
|
731
|
+
def as_dict(self, *, exclude_readonly: bool = False) -> dict[str, typing.Any]:
|
|
640
732
|
"""Return a dict that can be turned into json using json.dump.
|
|
641
733
|
|
|
642
734
|
:keyword bool exclude_readonly: Whether to remove the readonly properties.
|
|
@@ -696,7 +788,7 @@ def _deserialize_with_union(deserializers, obj):
|
|
|
696
788
|
def _deserialize_dict(
|
|
697
789
|
value_deserializer: typing.Optional[typing.Callable],
|
|
698
790
|
module: typing.Optional[str],
|
|
699
|
-
obj:
|
|
791
|
+
obj: dict[typing.Any, typing.Any],
|
|
700
792
|
):
|
|
701
793
|
if obj is None:
|
|
702
794
|
return obj
|
|
@@ -706,7 +798,7 @@ def _deserialize_dict(
|
|
|
706
798
|
|
|
707
799
|
|
|
708
800
|
def _deserialize_multiple_sequence(
|
|
709
|
-
entry_deserializers:
|
|
801
|
+
entry_deserializers: list[typing.Optional[typing.Callable]],
|
|
710
802
|
module: typing.Optional[str],
|
|
711
803
|
obj,
|
|
712
804
|
):
|
|
@@ -724,17 +816,28 @@ def _deserialize_sequence(
|
|
|
724
816
|
return obj
|
|
725
817
|
if isinstance(obj, ET.Element):
|
|
726
818
|
obj = list(obj)
|
|
819
|
+
try:
|
|
820
|
+
if (
|
|
821
|
+
isinstance(obj, str)
|
|
822
|
+
and isinstance(deserializer, functools.partial)
|
|
823
|
+
and isinstance(deserializer.args[0], functools.partial)
|
|
824
|
+
and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
|
|
825
|
+
):
|
|
826
|
+
# encoded string may be deserialized to sequence
|
|
827
|
+
return deserializer(obj)
|
|
828
|
+
except: # pylint: disable=bare-except
|
|
829
|
+
pass
|
|
727
830
|
return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
|
|
728
831
|
|
|
729
832
|
|
|
730
|
-
def _sorted_annotations(types:
|
|
833
|
+
def _sorted_annotations(types: list[typing.Any]) -> list[typing.Any]:
|
|
731
834
|
return sorted(
|
|
732
835
|
types,
|
|
733
836
|
key=lambda x: hasattr(x, "__name__") and x.__name__.lower() in ("str", "float", "int", "bool"),
|
|
734
837
|
)
|
|
735
838
|
|
|
736
839
|
|
|
737
|
-
def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-branches
|
|
840
|
+
def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements, too-many-branches
|
|
738
841
|
annotation: typing.Any,
|
|
739
842
|
module: typing.Optional[str],
|
|
740
843
|
rf: typing.Optional["_RestField"] = None,
|
|
@@ -799,7 +902,10 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
|
|
|
799
902
|
return functools.partial(_deserialize_with_union, deserializers)
|
|
800
903
|
|
|
801
904
|
try:
|
|
802
|
-
|
|
905
|
+
annotation_name = (
|
|
906
|
+
annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore
|
|
907
|
+
)
|
|
908
|
+
if annotation_name.lower() == "dict":
|
|
803
909
|
value_deserializer = _get_deserialize_callable_from_annotation(
|
|
804
910
|
annotation.__args__[1], module, rf # pyright: ignore
|
|
805
911
|
)
|
|
@@ -812,7 +918,10 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
|
|
|
812
918
|
except (AttributeError, IndexError):
|
|
813
919
|
pass
|
|
814
920
|
try:
|
|
815
|
-
|
|
921
|
+
annotation_name = (
|
|
922
|
+
annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore
|
|
923
|
+
)
|
|
924
|
+
if annotation_name.lower() in ["list", "set", "tuple", "sequence"]:
|
|
816
925
|
if len(annotation.__args__) > 1: # pyright: ignore
|
|
817
926
|
entry_deserializers = [
|
|
818
927
|
_get_deserialize_callable_from_annotation(dt, module, rf)
|
|
@@ -894,6 +1003,35 @@ def _deserialize(
|
|
|
894
1003
|
return _deserialize_with_callable(deserializer, value)
|
|
895
1004
|
|
|
896
1005
|
|
|
1006
|
+
def _failsafe_deserialize(
|
|
1007
|
+
deserializer: typing.Any,
|
|
1008
|
+
response: HttpResponse,
|
|
1009
|
+
module: typing.Optional[str] = None,
|
|
1010
|
+
rf: typing.Optional["_RestField"] = None,
|
|
1011
|
+
format: typing.Optional[str] = None,
|
|
1012
|
+
) -> typing.Any:
|
|
1013
|
+
try:
|
|
1014
|
+
return _deserialize(deserializer, response.json(), module, rf, format)
|
|
1015
|
+
except DeserializationError:
|
|
1016
|
+
_LOGGER.warning(
|
|
1017
|
+
"Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
|
|
1018
|
+
)
|
|
1019
|
+
return None
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def _failsafe_deserialize_xml(
|
|
1023
|
+
deserializer: typing.Any,
|
|
1024
|
+
response: HttpResponse,
|
|
1025
|
+
) -> typing.Any:
|
|
1026
|
+
try:
|
|
1027
|
+
return _deserialize_xml(deserializer, response.text())
|
|
1028
|
+
except DeserializationError:
|
|
1029
|
+
_LOGGER.warning(
|
|
1030
|
+
"Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
|
|
1031
|
+
)
|
|
1032
|
+
return None
|
|
1033
|
+
|
|
1034
|
+
|
|
897
1035
|
class _RestField:
|
|
898
1036
|
def __init__(
|
|
899
1037
|
self,
|
|
@@ -901,11 +1039,11 @@ class _RestField:
|
|
|
901
1039
|
name: typing.Optional[str] = None,
|
|
902
1040
|
type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
|
|
903
1041
|
is_discriminator: bool = False,
|
|
904
|
-
visibility: typing.Optional[
|
|
1042
|
+
visibility: typing.Optional[list[str]] = None,
|
|
905
1043
|
default: typing.Any = _UNSET,
|
|
906
1044
|
format: typing.Optional[str] = None,
|
|
907
1045
|
is_multipart_file_input: bool = False,
|
|
908
|
-
xml: typing.Optional[
|
|
1046
|
+
xml: typing.Optional[dict[str, typing.Any]] = None,
|
|
909
1047
|
):
|
|
910
1048
|
self._type = type
|
|
911
1049
|
self._rest_name_input = name
|
|
@@ -920,7 +1058,11 @@ class _RestField:
|
|
|
920
1058
|
|
|
921
1059
|
@property
|
|
922
1060
|
def _class_type(self) -> typing.Any:
|
|
923
|
-
|
|
1061
|
+
result = getattr(self._type, "args", [None])[0]
|
|
1062
|
+
# type may be wrapped by nested functools.partial so we need to check for that
|
|
1063
|
+
if isinstance(result, functools.partial):
|
|
1064
|
+
return getattr(result, "args", [None])[0]
|
|
1065
|
+
return result
|
|
924
1066
|
|
|
925
1067
|
@property
|
|
926
1068
|
def _rest_name(self) -> str:
|
|
@@ -931,14 +1073,37 @@ class _RestField:
|
|
|
931
1073
|
def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
|
|
932
1074
|
# by this point, type and rest_name will have a value bc we default
|
|
933
1075
|
# them in __new__ of the Model class
|
|
934
|
-
|
|
1076
|
+
# Use _data.get() directly to avoid triggering __getitem__ which clears the cache
|
|
1077
|
+
item = obj._data.get(self._rest_name)
|
|
935
1078
|
if item is None:
|
|
936
1079
|
return item
|
|
937
1080
|
if self._is_model:
|
|
938
1081
|
return item
|
|
939
|
-
|
|
1082
|
+
|
|
1083
|
+
# For mutable types, we want mutations to directly affect _data
|
|
1084
|
+
# Check if we've already deserialized this value
|
|
1085
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1086
|
+
if hasattr(obj, cache_attr):
|
|
1087
|
+
# Return the value from _data directly (it's been deserialized in place)
|
|
1088
|
+
return obj._data.get(self._rest_name)
|
|
1089
|
+
|
|
1090
|
+
deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self)
|
|
1091
|
+
|
|
1092
|
+
# For mutable types, store the deserialized value back in _data
|
|
1093
|
+
# so mutations directly affect _data
|
|
1094
|
+
if isinstance(deserialized, (dict, list, set)):
|
|
1095
|
+
obj._data[self._rest_name] = deserialized
|
|
1096
|
+
object.__setattr__(obj, cache_attr, True) # Mark as deserialized
|
|
1097
|
+
return deserialized
|
|
1098
|
+
|
|
1099
|
+
return deserialized
|
|
940
1100
|
|
|
941
1101
|
def __set__(self, obj: Model, value) -> None:
|
|
1102
|
+
# Clear the cached deserialized object when setting a new value
|
|
1103
|
+
cache_attr = f"_deserialized_{self._rest_name}"
|
|
1104
|
+
if hasattr(obj, cache_attr):
|
|
1105
|
+
object.__delattr__(obj, cache_attr)
|
|
1106
|
+
|
|
942
1107
|
if value is None:
|
|
943
1108
|
# we want to wipe out entries if users set attr to None
|
|
944
1109
|
try:
|
|
@@ -963,11 +1128,11 @@ def rest_field(
|
|
|
963
1128
|
*,
|
|
964
1129
|
name: typing.Optional[str] = None,
|
|
965
1130
|
type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
|
|
966
|
-
visibility: typing.Optional[
|
|
1131
|
+
visibility: typing.Optional[list[str]] = None,
|
|
967
1132
|
default: typing.Any = _UNSET,
|
|
968
1133
|
format: typing.Optional[str] = None,
|
|
969
1134
|
is_multipart_file_input: bool = False,
|
|
970
|
-
xml: typing.Optional[
|
|
1135
|
+
xml: typing.Optional[dict[str, typing.Any]] = None,
|
|
971
1136
|
) -> typing.Any:
|
|
972
1137
|
return _RestField(
|
|
973
1138
|
name=name,
|
|
@@ -984,8 +1149,8 @@ def rest_discriminator(
|
|
|
984
1149
|
*,
|
|
985
1150
|
name: typing.Optional[str] = None,
|
|
986
1151
|
type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
|
|
987
|
-
visibility: typing.Optional[
|
|
988
|
-
xml: typing.Optional[
|
|
1152
|
+
visibility: typing.Optional[list[str]] = None,
|
|
1153
|
+
xml: typing.Optional[dict[str, typing.Any]] = None,
|
|
989
1154
|
) -> typing.Any:
|
|
990
1155
|
return _RestField(name=name, type=type, is_discriminator=True, visibility=visibility, xml=xml)
|
|
991
1156
|
|
|
@@ -1004,9 +1169,9 @@ def serialize_xml(model: Model, exclude_readonly: bool = False) -> str:
|
|
|
1004
1169
|
def _get_element(
|
|
1005
1170
|
o: typing.Any,
|
|
1006
1171
|
exclude_readonly: bool = False,
|
|
1007
|
-
parent_meta: typing.Optional[
|
|
1172
|
+
parent_meta: typing.Optional[dict[str, typing.Any]] = None,
|
|
1008
1173
|
wrapped_element: typing.Optional[ET.Element] = None,
|
|
1009
|
-
) -> typing.Union[ET.Element,
|
|
1174
|
+
) -> typing.Union[ET.Element, list[ET.Element]]:
|
|
1010
1175
|
if _is_model(o):
|
|
1011
1176
|
model_meta = getattr(o, "_xml", {})
|
|
1012
1177
|
|
|
@@ -1095,7 +1260,7 @@ def _get_element(
|
|
|
1095
1260
|
def _get_wrapped_element(
|
|
1096
1261
|
v: typing.Any,
|
|
1097
1262
|
exclude_readonly: bool,
|
|
1098
|
-
meta: typing.Optional[
|
|
1263
|
+
meta: typing.Optional[dict[str, typing.Any]],
|
|
1099
1264
|
) -> ET.Element:
|
|
1100
1265
|
wrapped_element = _create_xml_element(
|
|
1101
1266
|
meta.get("name") if meta else None, meta.get("prefix") if meta else None, meta.get("ns") if meta else None
|
|
@@ -1138,7 +1303,7 @@ def _deserialize_xml(
|
|
|
1138
1303
|
def _convert_element(e: ET.Element):
|
|
1139
1304
|
# dict case
|
|
1140
1305
|
if len(e.attrib) > 0 or len({child.tag for child in e}) > 1:
|
|
1141
|
-
dict_result:
|
|
1306
|
+
dict_result: dict[str, typing.Any] = {}
|
|
1142
1307
|
for child in e:
|
|
1143
1308
|
if dict_result.get(child.tag) is not None:
|
|
1144
1309
|
if isinstance(dict_result[child.tag], list):
|
|
@@ -1151,7 +1316,7 @@ def _convert_element(e: ET.Element):
|
|
|
1151
1316
|
return dict_result
|
|
1152
1317
|
# array case
|
|
1153
1318
|
if len(e) > 0:
|
|
1154
|
-
array_result:
|
|
1319
|
+
array_result: list[typing.Any] = []
|
|
1155
1320
|
for child in e:
|
|
1156
1321
|
array_result.append(_convert_element(child))
|
|
1157
1322
|
return array_result
|