@autorest/python 6.7.0 → 6.7.2

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.
@@ -7,6 +7,7 @@
7
7
  # pylint: disable=protected-access, arguments-differ, signature-differs, broad-except
8
8
  # pyright: reportGeneralTypeIssues=false
9
9
 
10
+ import calendar
10
11
  import functools
11
12
  import sys
12
13
  import logging
@@ -14,13 +15,14 @@ import base64
14
15
  import re
15
16
  import copy
16
17
  import typing
18
+ import email
17
19
  from datetime import datetime, date, time, timedelta, timezone
18
20
  from json import JSONEncoder
19
21
  import isodate
20
22
  from azure.core.exceptions import DeserializationError
21
23
  from azure.core import CaseInsensitiveEnumMeta
22
24
  from azure.core.pipeline import PipelineResponse
23
- from azure.core.serialization import _Null # pylint: disable=protected-access
25
+ from azure.core.serialization import _Null
24
26
 
25
27
  if sys.version_info >= (3, 9):
26
28
  from collections.abc import MutableMapping
@@ -31,9 +33,9 @@ _LOGGER = logging.getLogger(__name__)
31
33
 
32
34
  __all__ = ["AzureJSONEncoder", "Model", "rest_field", "rest_discriminator"]
33
35
 
34
-
35
36
  TZ_UTC = timezone.utc
36
37
 
38
+
37
39
  def _timedelta_as_isostr(td: timedelta) -> str:
38
40
  """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
39
41
 
@@ -91,38 +93,20 @@ def _timedelta_as_isostr(td: timedelta) -> str:
91
93
  return "P" + date_str + time_str
92
94
 
93
95
 
94
- def _datetime_as_isostr(dt: typing.Union[datetime, date, time, timedelta]) -> str:
95
- """Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string
96
-
97
- :param timedelta dt: The date object to convert
98
- :rtype: str
99
- :return: ISO8601 version of this datetime
100
- """
101
- # First try datetime.datetime
102
- if hasattr(dt, "year") and hasattr(dt, "hour"):
103
- dt = typing.cast(datetime, dt)
104
- # astimezone() fails for naive times in Python 2.7, so make make sure dt is aware (tzinfo is set)
105
- if not dt.tzinfo:
106
- iso_formatted = dt.replace(tzinfo=TZ_UTC).isoformat()
107
- else:
108
- iso_formatted = dt.astimezone(TZ_UTC).isoformat()
109
- # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt)
110
- return iso_formatted.replace("+00:00", "Z")
111
- # Next try datetime.date or datetime.time
112
- try:
113
- dt = typing.cast(typing.Union[date, time], dt)
114
- return dt.isoformat()
115
- # Last, try datetime.timedelta
116
- except AttributeError:
117
- dt = typing.cast(timedelta, dt)
118
- return _timedelta_as_isostr(dt)
119
-
120
- def _serialize_bytes(o) -> str:
121
- return base64.b64encode(o).decode()
96
+ def _serialize_bytes(o, format: typing.Optional[str] = None) -> str:
97
+ encoded = base64.b64encode(o).decode()
98
+ if format == "base64url":
99
+ return encoded.strip("=").replace("+", "-").replace("/", "_")
100
+ return encoded
122
101
 
123
102
 
124
- def _serialize_datetime(o):
103
+ def _serialize_datetime(o, format: typing.Optional[str] = None):
125
104
  if hasattr(o, "year") and hasattr(o, "hour"):
105
+ if format == "rfc7231":
106
+ return email.utils.format_datetime(o, usegmt=True)
107
+ if format == "unix-timestamp":
108
+ return int(calendar.timegm(o.utctimetuple()))
109
+
126
110
  # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set)
127
111
  if not o.tzinfo:
128
112
  iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat()
@@ -146,7 +130,7 @@ class AzureJSONEncoder(JSONEncoder):
146
130
 
147
131
  def default(self, o): # pylint: disable=too-many-return-statements
148
132
  if _is_model(o):
149
- readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] # pylint: disable=protected-access
133
+ readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
150
134
  return {k: v for k, v in o.items() if k not in readonly_props}
151
135
  if isinstance(o, (bytes, bytearray)):
152
136
  return base64.b64encode(o).decode()
@@ -172,6 +156,10 @@ class AzureJSONEncoder(JSONEncoder):
172
156
 
173
157
 
174
158
  _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}]?")
159
+ _VALID_RFC7231 = re.compile(
160
+ r"(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d{2}\s"
161
+ r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
162
+ )
175
163
 
176
164
 
177
165
  def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
@@ -207,6 +195,36 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
207
195
  return date_obj
208
196
 
209
197
 
198
+ def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime:
199
+ """Deserialize RFC7231 formatted string into Datetime object.
200
+
201
+ :param str attr: response string to be deserialized.
202
+ :rtype: ~datetime.datetime
203
+ :returns: The datetime object from that input
204
+ """
205
+ if isinstance(attr, datetime):
206
+ # i'm already deserialized
207
+ return attr
208
+ match = _VALID_RFC7231.match(attr)
209
+ if not match:
210
+ raise ValueError("Invalid datetime string: " + attr)
211
+
212
+ return email.utils.parsedate_to_datetime(attr)
213
+
214
+
215
+ def _deserialize_datetime_unix_timestamp(attr: typing.Union[float, datetime]) -> datetime:
216
+ """Deserialize unix timestamp into Datetime object.
217
+
218
+ :param str attr: response string to be deserialized.
219
+ :rtype: ~datetime.datetime
220
+ :returns: The datetime object from that input
221
+ """
222
+ if isinstance(attr, datetime):
223
+ # i'm already deserialized
224
+ return attr
225
+ return datetime.fromtimestamp(attr, TZ_UTC)
226
+
227
+
210
228
  def _deserialize_date(attr: typing.Union[str, date]) -> date:
211
229
  """Deserialize ISO-8601 formatted string into Date object.
212
230
  :param str attr: response string to be deserialized.
@@ -231,13 +249,22 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
231
249
  return isodate.parse_time(attr)
232
250
 
233
251
 
234
- def deserialize_bytes(attr):
252
+ def _deserialize_bytes(attr):
235
253
  if isinstance(attr, (bytes, bytearray)):
236
254
  return attr
237
255
  return bytes(base64.b64decode(attr))
238
256
 
239
257
 
240
- def deserialize_duration(attr):
258
+ def _deserialize_bytes_base64(attr):
259
+ if isinstance(attr, (bytes, bytearray)):
260
+ return attr
261
+ padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore
262
+ attr = attr + padding # type: ignore
263
+ encoded = attr.replace("-", "+").replace("_", "/")
264
+ return bytes(base64.b64decode(encoded))
265
+
266
+
267
+ def _deserialize_duration(attr):
241
268
  if isinstance(attr, timedelta):
242
269
  return attr
243
270
  return isodate.parse_duration(attr)
@@ -247,11 +274,26 @@ _DESERIALIZE_MAPPING = {
247
274
  datetime: _deserialize_datetime,
248
275
  date: _deserialize_date,
249
276
  time: _deserialize_time,
250
- bytes: deserialize_bytes,
251
- timedelta: deserialize_duration,
277
+ bytes: _deserialize_bytes,
278
+ bytearray: _deserialize_bytes,
279
+ timedelta: _deserialize_duration,
252
280
  typing.Any: lambda x: x,
253
281
  }
254
282
 
283
+ _DESERIALIZE_MAPPING_WITHFORMAT = {
284
+ "rfc3339": _deserialize_datetime,
285
+ "rfc7231": _deserialize_datetime_rfc7231,
286
+ "unix-timestamp": _deserialize_datetime_unix_timestamp,
287
+ "base64": _deserialize_bytes,
288
+ "base64url": _deserialize_bytes_base64,
289
+ }
290
+
291
+
292
+ def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
293
+ if rf and rf._format:
294
+ return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
295
+ return _DESERIALIZE_MAPPING.get(annotation)
296
+
255
297
 
256
298
  def _get_model(module_name: str, model_name: str):
257
299
  models = {k: v for k, v in sys.modules[module_name].__dict__.items() if isinstance(v, type)}
@@ -358,12 +400,20 @@ def _is_model(obj: typing.Any) -> bool:
358
400
  return getattr(obj, "_is_model", False)
359
401
 
360
402
 
361
- def _serialize(o):
403
+ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
404
+ if isinstance(o, list):
405
+ return [_serialize(x, format) for x in o]
406
+ if isinstance(o, dict):
407
+ return {k: _serialize(v, format) for k, v in o.items()}
408
+ if isinstance(o, set):
409
+ return {_serialize(x, format) for x in o}
410
+ if isinstance(o, tuple):
411
+ return tuple(_serialize(x, format) for x in o)
362
412
  if isinstance(o, (bytes, bytearray)):
363
- return _serialize_bytes(o)
413
+ return _serialize_bytes(o, format)
364
414
  try:
365
415
  # First try datetime.datetime
366
- return _serialize_datetime(o)
416
+ return _serialize_datetime(o, format)
367
417
  except AttributeError:
368
418
  pass
369
419
  # Last, try datetime.timedelta
@@ -385,7 +435,7 @@ def _get_rest_field(
385
435
 
386
436
 
387
437
  def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
388
- return _deserialize(rf._type, value) if (rf and rf._is_model) else _serialize(value)
438
+ return _deserialize(rf._type, value) if (rf and rf._is_model) else _serialize(value, rf._format if rf else None)
389
439
 
390
440
 
391
441
  class Model(_MyMutableMapping):
@@ -409,10 +459,13 @@ class Model(_MyMutableMapping):
409
459
  if non_attr_kwargs:
410
460
  # actual type errors only throw the first wrong keyword arg they see, so following that.
411
461
  raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'")
412
- dict_to_pass.update({
413
- self._attr_to_rest_field[k]._rest_name: _serialize(v)
414
- for k, v in kwargs.items() if v is not None
415
- })
462
+ dict_to_pass.update(
463
+ {
464
+ self._attr_to_rest_field[k]._rest_name: _serialize(v, self._attr_to_rest_field[k]._format)
465
+ for k, v in kwargs.items()
466
+ if v is not None
467
+ }
468
+ )
416
469
  super().__init__(dict_to_pass)
417
470
 
418
471
  def copy(self) -> "Model":
@@ -464,145 +517,157 @@ class Model(_MyMutableMapping):
464
517
 
465
518
 
466
519
  def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements
467
- annotation: typing.Any, module: typing.Optional[str], rf: typing.Optional["_RestField"] = None
468
- ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
469
- if not annotation or annotation in [int, float]:
470
- return None
520
+ annotation: typing.Any,
521
+ module: typing.Optional[str],
522
+ rf: typing.Optional["_RestField"] = None,
523
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
524
+ if not annotation or annotation in [int, float]:
525
+ return None
471
526
 
472
- try:
473
- if module and _is_model(_get_model(module, annotation)):
474
- if rf:
475
- rf._is_model = True
476
- def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
477
- if _is_model(obj):
478
- return obj
479
- return _deserialize(model_deserializer, obj)
527
+ try:
528
+ if module and _is_model(_get_model(module, annotation)):
529
+ if rf:
530
+ rf._is_model = True
480
531
 
481
- return functools.partial(_deserialize_model, _get_model(module, annotation))
482
- except Exception:
483
- pass
532
+ def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
533
+ if _is_model(obj):
534
+ return obj
535
+ return _deserialize(model_deserializer, obj)
484
536
 
485
- # is it a literal?
486
- try:
487
- if sys.version_info >= (3, 8):
488
- from typing import Literal # pylint: disable=no-name-in-module, ungrouped-imports
489
- else:
490
- from typing_extensions import Literal # type: ignore # pylint: disable=ungrouped-imports
537
+ return functools.partial(_deserialize_model, _get_model(module, annotation))
538
+ except Exception:
539
+ pass
491
540
 
492
- if annotation.__origin__ == Literal:
493
- return None
494
- except AttributeError:
495
- pass
541
+ # is it a literal?
542
+ try:
543
+ if sys.version_info >= (3, 8):
544
+ from typing import (
545
+ Literal,
546
+ ) # pylint: disable=no-name-in-module, ungrouped-imports
547
+ else:
548
+ from typing_extensions import Literal # type: ignore # pylint: disable=ungrouped-imports
496
549
 
497
- if getattr(annotation, "__origin__", None) is typing.Union:
498
- def _deserialize_with_union(union_annotation, obj):
499
- for t in union_annotation.__args__:
500
- try:
501
- return _deserialize(t, obj, module)
502
- except DeserializationError:
503
- pass
504
- raise DeserializationError()
505
- return functools.partial(_deserialize_with_union, annotation)
506
-
507
- # is it optional?
508
- try:
509
- # right now, assuming we don't have unions, since we're getting rid of the only
510
- # union we used to have in msrest models, which was union of str and enum
511
- if any(a for a in annotation.__args__ if a == type(None)):
550
+ if annotation.__origin__ == Literal:
551
+ return None
552
+ except AttributeError:
553
+ pass
512
554
 
513
- if_obj_deserializer = _get_deserialize_callable_from_annotation(
514
- next(a for a in annotation.__args__ if a != type(None)), module, rf
515
- )
555
+ if getattr(annotation, "__origin__", None) is typing.Union:
516
556
 
517
- def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
518
- if obj is None:
519
- return obj
520
- return _deserialize_with_callable(if_obj_deserializer, obj)
557
+ def _deserialize_with_union(union_annotation, obj):
558
+ for t in union_annotation.__args__:
559
+ try:
560
+ return _deserialize(t, obj, module, rf)
561
+ except DeserializationError:
562
+ pass
563
+ raise DeserializationError()
521
564
 
522
- return functools.partial(_deserialize_with_optional, if_obj_deserializer)
523
- except AttributeError:
524
- pass
565
+ return functools.partial(_deserialize_with_union, annotation)
525
566
 
526
- # is it a forward ref / in quotes?
527
- if isinstance(annotation, (str, typing.ForwardRef)):
528
- try:
529
- model_name = annotation.__forward_arg__ # type: ignore
530
- except AttributeError:
531
- model_name = annotation
532
- if module is not None:
533
- annotation = _get_model(module, model_name)
567
+ # is it optional?
568
+ try:
569
+ # right now, assuming we don't have unions, since we're getting rid of the only
570
+ # union we used to have in msrest models, which was union of str and enum
571
+ if any(a for a in annotation.__args__ if a == type(None)):
572
+ if_obj_deserializer = _get_deserialize_callable_from_annotation(
573
+ next(a for a in annotation.__args__ if a != type(None)), module, rf
574
+ )
534
575
 
576
+ def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
577
+ if obj is None:
578
+ return obj
579
+ return _deserialize_with_callable(if_obj_deserializer, obj)
580
+
581
+ return functools.partial(_deserialize_with_optional, if_obj_deserializer)
582
+ except AttributeError:
583
+ pass
584
+
585
+ # is it a forward ref / in quotes?
586
+ if isinstance(annotation, (str, typing.ForwardRef)):
535
587
  try:
536
- if annotation._name == "Dict":
537
- key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
538
- value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module, rf)
539
-
540
- def _deserialize_dict(
541
- key_deserializer: typing.Optional[typing.Callable],
542
- value_deserializer: typing.Optional[typing.Callable],
543
- obj: typing.Dict[typing.Any, typing.Any],
544
- ):
545
- if obj is None:
546
- return obj
547
- return {
548
- _deserialize(key_deserializer, k, module): _deserialize(value_deserializer, v, module) for k, v in obj.items()
549
- }
550
-
551
- return functools.partial(
552
- _deserialize_dict,
553
- key_deserializer,
554
- value_deserializer,
555
- )
556
- except (AttributeError, IndexError):
557
- pass
558
- try:
559
- if annotation._name in ["List", "Set", "Tuple", "Sequence"]:
560
- if len(annotation.__args__) > 1:
561
-
562
- def _deserialize_multiple_sequence(
563
- entry_deserializers: typing.List[typing.Optional[typing.Callable]], obj
564
- ):
565
- if obj is None:
566
- return obj
567
- return type(obj)(
568
- _deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)
569
- )
570
-
571
- entry_deserializers = [
572
- _get_deserialize_callable_from_annotation(dt, module, rf) for dt in annotation.__args__
573
- ]
574
- return functools.partial(_deserialize_multiple_sequence, entry_deserializers)
575
- deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
576
-
577
- def _deserialize_sequence(
578
- deserializer: typing.Optional[typing.Callable],
588
+ model_name = annotation.__forward_arg__ # type: ignore
589
+ except AttributeError:
590
+ model_name = annotation
591
+ if module is not None:
592
+ annotation = _get_model(module, model_name)
593
+
594
+ try:
595
+ if annotation._name == "Dict":
596
+ key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
597
+ value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module, rf)
598
+
599
+ def _deserialize_dict(
600
+ key_deserializer: typing.Optional[typing.Callable],
601
+ value_deserializer: typing.Optional[typing.Callable],
602
+ obj: typing.Dict[typing.Any, typing.Any],
603
+ ):
604
+ if obj is None:
605
+ return obj
606
+ return {
607
+ _deserialize(key_deserializer, k, module): _deserialize(value_deserializer, v, module)
608
+ for k, v in obj.items()
609
+ }
610
+
611
+ return functools.partial(
612
+ _deserialize_dict,
613
+ key_deserializer,
614
+ value_deserializer,
615
+ )
616
+ except (AttributeError, IndexError):
617
+ pass
618
+ try:
619
+ if annotation._name in ["List", "Set", "Tuple", "Sequence"]:
620
+ if len(annotation.__args__) > 1:
621
+
622
+ def _deserialize_multiple_sequence(
623
+ entry_deserializers: typing.List[typing.Optional[typing.Callable]],
579
624
  obj,
580
625
  ):
581
626
  if obj is None:
582
627
  return obj
583
- return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
628
+ return type(obj)(
629
+ _deserialize(deserializer, entry, module)
630
+ for entry, deserializer in zip(obj, entry_deserializers)
631
+ )
632
+
633
+ entry_deserializers = [
634
+ _get_deserialize_callable_from_annotation(dt, module, rf) for dt in annotation.__args__
635
+ ]
636
+ return functools.partial(_deserialize_multiple_sequence, entry_deserializers)
637
+ deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
638
+
639
+ def _deserialize_sequence(
640
+ deserializer: typing.Optional[typing.Callable],
641
+ obj,
642
+ ):
643
+ if obj is None:
644
+ return obj
645
+ return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
646
+
647
+ return functools.partial(_deserialize_sequence, deserializer)
648
+ except (TypeError, IndexError, AttributeError, SyntaxError):
649
+ pass
584
650
 
585
- return functools.partial(_deserialize_sequence, deserializer)
586
- except (TypeError, IndexError, AttributeError, SyntaxError):
651
+ def _deserialize_default(
652
+ annotation,
653
+ deserializer_from_mapping,
654
+ obj,
655
+ ):
656
+ if obj is None:
657
+ return obj
658
+ try:
659
+ return _deserialize_with_callable(annotation, obj)
660
+ except Exception:
587
661
  pass
662
+ return _deserialize_with_callable(deserializer_from_mapping, obj)
588
663
 
589
- def _deserialize_default(
590
- annotation,
591
- deserializer_from_mapping,
592
- obj,
593
- ):
594
- if obj is None:
595
- return obj
596
- try:
597
- return _deserialize_with_callable(annotation, obj)
598
- except Exception:
599
- pass
600
- return _deserialize_with_callable(deserializer_from_mapping, obj)
601
-
602
- return functools.partial(_deserialize_default, annotation, _DESERIALIZE_MAPPING.get(annotation))
664
+ return functools.partial(_deserialize_default, annotation, get_deserializer(annotation, rf))
603
665
 
604
666
 
605
- def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
667
+ def _deserialize_with_callable(
668
+ deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]],
669
+ value: typing.Any,
670
+ ):
606
671
  try:
607
672
  if value is None:
608
673
  return None
@@ -621,12 +686,18 @@ def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[ty
621
686
  raise DeserializationError() from e
622
687
 
623
688
 
624
- def _deserialize(deserializer: typing.Any, value: typing.Any, module: typing.Optional[str] = None) -> typing.Any:
689
+ def _deserialize(
690
+ deserializer: typing.Any,
691
+ value: typing.Any,
692
+ module: typing.Optional[str] = None,
693
+ rf: typing.Optional["_RestField"] = None,
694
+ ) -> typing.Any:
625
695
  if isinstance(value, PipelineResponse):
626
696
  value = value.http_response.json()
627
- deserializer = _get_deserialize_callable_from_annotation(deserializer, module)
697
+ deserializer = _get_deserialize_callable_from_annotation(deserializer, module, rf)
628
698
  return _deserialize_with_callable(deserializer, value)
629
699
 
700
+
630
701
  class _RestField:
631
702
  def __init__(
632
703
  self,
@@ -636,6 +707,7 @@ class _RestField:
636
707
  is_discriminator: bool = False,
637
708
  visibility: typing.Optional[typing.List[str]] = None,
638
709
  default: typing.Any = _UNSET,
710
+ format: typing.Optional[str] = None,
639
711
  ):
640
712
  self._type = type
641
713
  self._rest_name_input = name
@@ -644,6 +716,7 @@ class _RestField:
644
716
  self._visibility = visibility
645
717
  self._is_model = False
646
718
  self._default = default
719
+ self._format = format
647
720
 
648
721
  @property
649
722
  def _rest_name(self) -> str:
@@ -657,7 +730,7 @@ class _RestField:
657
730
  item = obj.get(self._rest_name)
658
731
  if item is None:
659
732
  return item
660
- return _deserialize(self._type, _serialize(item))
733
+ return _deserialize(self._type, _serialize(item, self._format), rf=self)
661
734
 
662
735
  def __set__(self, obj: Model, value) -> None:
663
736
  if value is None:
@@ -669,7 +742,7 @@ class _RestField:
669
742
  return
670
743
  if self._is_model and not _is_model(value):
671
744
  obj.__setitem__(self._rest_name, _deserialize(self._type, value))
672
- obj.__setitem__(self._rest_name, _serialize(value))
745
+ obj.__setitem__(self._rest_name, _serialize(value, self._format))
673
746
 
674
747
  def _get_deserialize_callable_from_annotation(
675
748
  self, annotation: typing.Any
@@ -683,8 +756,9 @@ def rest_field(
683
756
  type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
684
757
  visibility: typing.Optional[typing.List[str]] = None,
685
758
  default: typing.Any = _UNSET,
759
+ format: typing.Optional[str] = None,
686
760
  ) -> typing.Any:
687
- return _RestField(name=name, type=type, visibility=visibility, default=default)
761
+ return _RestField(name=name, type=type, visibility=visibility, default=default, format=format)
688
762
 
689
763
 
690
764
  def rest_discriminator(
@@ -90,7 +90,7 @@ setup(
90
90
  {% if azure_arm %}
91
91
  "azure-mgmt-core<2.0.0,>=1.3.2",
92
92
  {% else %}
93
- "azure-core<2.0.0,>=1.27.0",
93
+ "azure-core<2.0.0,>=1.28.0",
94
94
  {% endif %}
95
95
  {% if code_model.need_typing_extensions %}
96
96
  "typing-extensions>=4.3.0; python_version<'3.8.0'",
@@ -744,6 +744,8 @@ class Serializer(object):
744
744
 
745
745
  :param data: The data to be serialized.
746
746
  :param str data_type: The type to be serialized from.
747
+ :keyword bool skip_quote: Whether to skip quote the serialized result.
748
+ Defaults to False.
747
749
  :rtype: str
748
750
  :raises: TypeError if serialization fails.
749
751
  :raises: ValueError if data is None
@@ -752,10 +754,8 @@ class Serializer(object):
752
754
  # Treat the list aside, since we don't want to encode the div separator
753
755
  if data_type.startswith("["):
754
756
  internal_data_type = data_type[1:-1]
755
- data = [self.serialize_data(d, internal_data_type, **kwargs) if d is not None else "" for d in data]
756
- if not kwargs.get("skip_quote", False):
757
- data = [quote(str(d), safe="") for d in data]
758
- return str(self.serialize_iter(data, internal_data_type, **kwargs))
757
+ do_quote = not kwargs.get('skip_quote', False)
758
+ return str(self.serialize_iter(data, internal_data_type, do_quote=do_quote, **kwargs))
759
759
 
760
760
  # Not a list, regular serialization
761
761
  output = self.serialize_data(data, data_type, **kwargs)
@@ -894,6 +894,8 @@ class Serializer(object):
894
894
  not be None or empty.
895
895
  :param str div: If set, this str will be used to combine the elements
896
896
  in the iterable into a combined string. Default is 'None'.
897
+ :keyword bool do_quote: Whether to quote the serialized result of each iterable element.
898
+ Defaults to False.
897
899
  :rtype: list, str
898
900
  """
899
901
  if isinstance(data, str):
@@ -911,6 +913,13 @@ class Serializer(object):
911
913
  raise
912
914
  serialized.append(None)
913
915
 
916
+ if kwargs.get('do_quote', False):
917
+ serialized = [
918
+ '' if s is None else quote(str(s), safe='')
919
+ for s
920
+ in serialized
921
+ ]
922
+
914
923
  if div:
915
924
  serialized = ["" if s is None else str(s) for s in serialized]
916
925
  serialized = div.join(serialized)
@@ -32,3 +32,32 @@ def raise_if_not_implemented(cls, abstract_methods):
32
32
  cls.__name__, '\', \''.join(not_implemented))
33
33
  )
34
34
  {% endif %}
35
+
36
+ {% if code_model.has_etag %}
37
+ def quote_etag(etag: Optional[str]) -> Optional[str]:
38
+ if not etag or etag == "*":
39
+ return etag
40
+ if etag.startswith('"') and etag.endswith('"'):
41
+ return etag
42
+ if etag.startswith("'") and etag.endswith("'"):
43
+ return etag
44
+ return '"' + etag + '"'
45
+
46
+
47
+ def prep_if_match(etag: Optional[str], match_condition: Optional[MatchConditions]) -> Optional[str]:
48
+ if match_condition == MatchConditions.IfNotModified:
49
+ if_match = quote_etag(etag) if etag else None
50
+ return if_match
51
+ if match_condition == MatchConditions.IfPresent:
52
+ return "*"
53
+ return None
54
+
55
+
56
+ def prep_if_none_match(etag: Optional[str], match_condition: Optional[MatchConditions]) -> Optional[str]:
57
+ if match_condition == MatchConditions.IfModified:
58
+ if_none_match = quote_etag(etag) if etag else None
59
+ return if_none_match
60
+ if match_condition == MatchConditions.IfMissing:
61
+ return "*"
62
+ return None
63
+ {% endif %}