@autorest/python 6.2.4 → 6.2.7

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 (45) hide show
  1. package/autorest/black/__init__.py +3 -2
  2. package/autorest/codegen/__init__.py +1 -1
  3. package/autorest/codegen/models/base.py +14 -2
  4. package/autorest/codegen/models/client.py +20 -22
  5. package/autorest/codegen/models/combined_type.py +1 -1
  6. package/autorest/codegen/models/imports.py +2 -2
  7. package/autorest/codegen/models/model_type.py +4 -0
  8. package/autorest/codegen/models/operation.py +6 -6
  9. package/autorest/codegen/models/operation_group.py +12 -11
  10. package/autorest/codegen/models/primitive_types.py +50 -0
  11. package/autorest/codegen/models/property.py +4 -0
  12. package/autorest/codegen/models/request_builder.py +9 -7
  13. package/autorest/codegen/serializers/__init__.py +7 -6
  14. package/autorest/codegen/serializers/builder_serializer.py +67 -30
  15. package/autorest/codegen/serializers/client_serializer.py +22 -8
  16. package/autorest/codegen/serializers/metadata_serializer.py +7 -1
  17. package/autorest/codegen/serializers/model_serializer.py +12 -13
  18. package/autorest/codegen/serializers/operation_groups_serializer.py +1 -0
  19. package/autorest/codegen/serializers/parameter_serializer.py +3 -3
  20. package/autorest/codegen/serializers/patch_serializer.py +2 -4
  21. package/autorest/codegen/serializers/sample_serializer.py +23 -14
  22. package/autorest/codegen/serializers/utils.py +6 -0
  23. package/autorest/codegen/templates/client.py.jinja2 +3 -12
  24. package/autorest/codegen/templates/config.py.jinja2 +2 -5
  25. package/autorest/codegen/templates/keywords.jinja2 +2 -2
  26. package/autorest/codegen/templates/metadata.json.jinja2 +2 -2
  27. package/autorest/codegen/templates/model_base.py.jinja2 +171 -130
  28. package/autorest/codegen/templates/model_dpg.py.jinja2 +1 -1
  29. package/autorest/codegen/templates/packaging_templates/setup.py.jinja2 +1 -0
  30. package/autorest/codegen/templates/request_builder.py.jinja2 +1 -1
  31. package/autorest/codegen/templates/serialization.py.jinja2 +286 -325
  32. package/autorest/jsonrpc/__init__.py +3 -1
  33. package/autorest/jsonrpc/localapi.py +3 -1
  34. package/autorest/jsonrpc/stdstream.py +1 -1
  35. package/autorest/m2r/__init__.py +2 -2
  36. package/autorest/multiapi/models/imports.py +34 -22
  37. package/autorest/multiapi/serializers/import_serializer.py +1 -1
  38. package/autorest/multiapi/templates/multiapi_config.py.jinja2 +2 -8
  39. package/autorest/multiapi/templates/multiapi_service_client.py.jinja2 +1 -1
  40. package/autorest/postprocess/__init__.py +5 -4
  41. package/autorest/preprocess/__init__.py +7 -1
  42. package/autorest/preprocess/helpers.py +14 -2
  43. package/autorest/preprocess/python_mappings.py +27 -0
  44. package/package.json +2 -2
  45. package/setup.py +3 -0
@@ -17,6 +17,7 @@ from datetime import datetime, date, time, timedelta
17
17
  from azure.core.utils._utils import _FixedOffset
18
18
  from collections.abc import MutableMapping
19
19
  from azure.core.exceptions import DeserializationError
20
+ from azure.core import CaseInsensitiveEnumMeta
20
21
  import copy
21
22
 
22
23
  _LOGGER = logging.getLogger(__name__)
@@ -40,8 +41,7 @@ with no data. This gets serialized to `null` on the wire.
40
41
  """
41
42
 
42
43
 
43
- def _timedelta_as_isostr(td):
44
- # type: (timedelta) -> str
44
+ def _timedelta_as_isostr(td: timedelta) -> str:
45
45
  """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
46
46
 
47
47
  Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
@@ -91,8 +91,7 @@ def _timedelta_as_isostr(td):
91
91
  return "P" + date_str + time_str
92
92
 
93
93
 
94
- def _datetime_as_isostr(dt):
95
- # type: (typing.Union[datetime, date, time, timedelta]) -> str
94
+ def _datetime_as_isostr(dt: typing.Union[datetime, date, time, timedelta]) -> str:
96
95
  """Converts a datetime.(datetime|date|time|timedelta) object into an ISO 8601 formatted string"""
97
96
  # First try datetime.datetime
98
97
  if hasattr(dt, "year") and hasattr(dt, "hour"):
@@ -121,9 +120,11 @@ try:
121
120
  except ImportError:
122
121
  TZ_UTC = _FixedOffset(0) # type: ignore
123
122
 
123
+
124
124
  def _serialize_bytes(o) -> str:
125
125
  return base64.b64encode(o).decode()
126
126
 
127
+
127
128
  def _serialize_datetime(o):
128
129
  if hasattr(o, "year") and hasattr(o, "hour"):
129
130
  # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set)
@@ -136,12 +137,14 @@ def _serialize_datetime(o):
136
137
  # Next try datetime.date or datetime.time
137
138
  return o.isoformat()
138
139
 
140
+
139
141
  def _is_readonly(p):
140
142
  try:
141
143
  return p._readonly
142
144
  except AttributeError:
143
145
  return False
144
146
 
147
+
145
148
  class AzureJSONEncoder(JSONEncoder):
146
149
  """A JSON encoder that's capable of serializing datetime objects and bytes."""
147
150
 
@@ -170,9 +173,8 @@ class AzureJSONEncoder(JSONEncoder):
170
173
  return super(AzureJSONEncoder, self).default(o)
171
174
 
172
175
 
173
- _VALID_DATE = re.compile(
174
- r'\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}'
175
- r'\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?')
176
+ _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}]?")
177
+
176
178
 
177
179
  def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
178
180
  """Deserialize ISO-8601 formatted string into Datetime object.
@@ -188,7 +190,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
188
190
  if not match:
189
191
  raise ValueError("Invalid datetime string: " + attr)
190
192
 
191
- check_decimal = attr.split('.')
193
+ check_decimal = attr.split(".")
192
194
  if len(check_decimal) > 1:
193
195
  decimal_str = ""
194
196
  for digit in check_decimal[1]:
@@ -205,6 +207,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
205
207
  raise OverflowError("Hit max or min date")
206
208
  return date_obj
207
209
 
210
+
208
211
  def _deserialize_date(attr: typing.Union[str, date]) -> date:
209
212
  """Deserialize ISO-8601 formatted string into Date object.
210
213
  :param str attr: response string to be deserialized.
@@ -215,6 +218,7 @@ def _deserialize_date(attr: typing.Union[str, date]) -> date:
215
218
  return attr
216
219
  return isodate.parse_date(attr, defaultmonth=None, defaultday=None)
217
220
 
221
+
218
222
  def _deserialize_time(attr: typing.Union[str, time]) -> time:
219
223
  """Deserialize ISO-8601 formatted string into time object.
220
224
 
@@ -225,18 +229,31 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time:
225
229
  return attr
226
230
  return isodate.parse_time(attr)
227
231
 
232
+
228
233
  def deserialize_bytes(attr):
234
+ if isinstance(attr, (bytes, bytearray)):
235
+ return attr
229
236
  return bytes(base64.b64decode(attr))
230
237
 
238
+
239
+ def deserialize_duration(attr):
240
+ if isinstance(attr, timedelta):
241
+ return attr
242
+ return isodate.parse_duration(attr)
243
+
244
+
231
245
  _DESERIALIZE_MAPPING = {
232
246
  datetime: _deserialize_datetime,
233
247
  date: _deserialize_date,
234
248
  time: _deserialize_time,
235
249
  bytes: deserialize_bytes,
250
+ timedelta: deserialize_duration,
251
+ typing.Any: lambda x: x,
236
252
  }
237
253
 
254
+
238
255
  def _get_model(module_name: str, model_name: str):
239
- module_end = module_name.rsplit('.', 1)[0]
256
+ module_end = module_name.rsplit(".", 1)[0]
240
257
  module = sys.modules[module_end]
241
258
  models = {k: v for k, v in module.__dict__.items() if isinstance(v, type)}
242
259
  if model_name not in models:
@@ -244,10 +261,11 @@ def _get_model(module_name: str, model_name: str):
244
261
  return model_name
245
262
  return models[model_name]
246
263
 
264
+
247
265
  _UNSET = object()
248
266
 
249
- class _MyMutableMapping(MutableMapping):
250
267
 
268
+ class _MyMutableMapping(MutableMapping):
251
269
  def __init__(self, data: typing.Dict[str, typing.Any]) -> None:
252
270
  self._data = copy.deepcopy(data)
253
271
 
@@ -332,9 +350,11 @@ class _MyMutableMapping(MutableMapping):
332
350
  def __repr__(self) -> str:
333
351
  return str(self._data)
334
352
 
353
+
335
354
  def _is_model(obj: typing.Any) -> bool:
336
355
  return getattr(obj, "_is_model", False)
337
356
 
357
+
338
358
  def _serialize(o):
339
359
  if isinstance(o, (bytes, bytearray)):
340
360
  return _serialize_bytes(o)
@@ -351,7 +371,10 @@ def _serialize(o):
351
371
  pass
352
372
  return o
353
373
 
354
- def _get_rest_field(attr_to_rest_field: typing.Dict[str, "_RestField"], rest_name: str) -> typing.Optional["_RestField"]:
374
+
375
+ def _get_rest_field(
376
+ attr_to_rest_field: typing.Dict[str, "_RestField"], rest_name: str
377
+ ) -> typing.Optional["_RestField"]:
355
378
  try:
356
379
  return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name)
357
380
  except StopIteration:
@@ -361,8 +384,10 @@ def _get_rest_field(attr_to_rest_field: typing.Dict[str, "_RestField"], rest_nam
361
384
  def _create_value(rest_field: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
362
385
  return _deserialize(rest_field._type, value) if (rest_field and rest_field._is_model) else _serialize(value)
363
386
 
387
+
364
388
  class Model(_MyMutableMapping):
365
389
  _is_model = True
390
+
366
391
  def __init__(self, *args, **kwargs):
367
392
  class_name = self.__class__.__name__
368
393
  if len(args) > 1:
@@ -373,20 +398,15 @@ class Model(_MyMutableMapping):
373
398
  if rest_field._default is not _UNSET
374
399
  }
375
400
  if args:
376
- dict_to_pass.update({
377
- k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v)
378
- for k, v in args[0].items()
379
- })
401
+ dict_to_pass.update(
402
+ {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()}
403
+ )
380
404
  else:
381
405
  non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field]
382
406
  if non_attr_kwargs:
383
407
  # actual type errors only throw the first wrong keyword arg they see, so following that.
384
- raise TypeError(
385
- f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'"
386
- )
387
- dict_to_pass.update({
388
- self._attr_to_rest_field[k]._rest_name: _serialize(v) for k, v in kwargs.items()
389
- })
408
+ raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'")
409
+ dict_to_pass.update({self._attr_to_rest_field[k]._rest_name: _serialize(v) for k, v in kwargs.items()})
390
410
  super().__init__(dict_to_pass)
391
411
 
392
412
  def copy(self):
@@ -394,15 +414,14 @@ class Model(_MyMutableMapping):
394
414
 
395
415
  def __new__(cls, *args: typing.Any, **kwargs: typing.Any):
396
416
  # we know the last three classes in mro are going to be 'Model', 'dict', and 'object'
397
- mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
398
- attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
399
- k: v
400
- for mro_class in mros
401
- for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
417
+ mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
418
+ attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
419
+ k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
402
420
  }
403
421
  annotations = {
404
422
  k: v
405
- for mro_class in mros if hasattr(mro_class, '__annotations__')
423
+ for mro_class in mros
424
+ if hasattr(mro_class, "__annotations__")
406
425
  for k, v in mro_class.__annotations__.items()
407
426
  }
408
427
  for attr, rest_field in attr_to_rest_field.items():
@@ -411,18 +430,15 @@ class Model(_MyMutableMapping):
411
430
  rest_field._type = rest_field._get_deserialize_callable_from_annotation(annotations.get(attr, None))
412
431
  if not rest_field._rest_name_input:
413
432
  rest_field._rest_name_input = attr
414
- cls._attr_to_rest_field: typing.Dict[str, _RestField] = {
415
- k: v
416
- for k, v in attr_to_rest_field.items()
417
- }
433
+ cls._attr_to_rest_field: typing.Dict[str, _RestField] = {k: v for k, v in attr_to_rest_field.items()}
418
434
 
419
435
  return super().__new__(cls)
420
436
 
421
437
  def __init_subclass__(cls, discriminator=None):
422
438
  for base in cls.__bases__:
423
- if hasattr(base, '__mapping__'):
439
+ if hasattr(base, "__mapping__"):
424
440
  base.__mapping__[discriminator or cls.__name__] = cls
425
-
441
+
426
442
  @classmethod
427
443
  def _get_discriminator(cls) -> typing.Optional[str]:
428
444
  for v in cls.__dict__.values():
@@ -432,81 +448,29 @@ class Model(_MyMutableMapping):
432
448
 
433
449
  @classmethod
434
450
  def _deserialize(cls, data):
435
- if not hasattr(cls, '__mapping__'):
451
+ if not hasattr(cls, "__mapping__"):
436
452
  return cls(data)
437
453
  discriminator = cls._get_discriminator()
438
- mapped_cls = cls.__mapping__.get(data.get(discriminator),cls)
454
+ mapped_cls = cls.__mapping__.get(data.get(discriminator), cls)
439
455
  if mapped_cls == cls:
440
456
  return cls(data)
441
457
  return mapped_cls._deserialize(data)
442
458
 
443
459
 
444
- def _deserialize(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
445
- try:
446
- if value is None:
447
- return None
448
- if isinstance(deserializer, type) and issubclass(deserializer, Model):
449
- return deserializer._deserialize(value)
450
- return deserializer(value) if deserializer else value
451
- except Exception as e:
452
- raise DeserializationError() from e
453
-
454
- class _RestField:
455
- def __init__(
456
- self,
457
- *,
458
- name: typing.Optional[str] = None,
459
- type: typing.Optional[typing.Callable] = None,
460
- is_discriminator: bool = False,
461
- readonly: bool = False,
462
- default: typing.Any = _UNSET,
463
- ):
464
- self._type = type
465
- self._rest_name_input = name
466
- self._module: typing.Optional[str] = None
467
- self._is_discriminator = is_discriminator
468
- self._readonly = readonly
469
- self._is_model = False
470
- self._default = default
471
-
472
- @property
473
- def _rest_name(self) -> str:
474
- if self._rest_name_input is None:
475
- raise ValueError("Rest name was never set")
476
- return self._rest_name_input
477
-
478
- def __get__(self, obj: Model, type=None):
479
- # by this point, type and rest_name will have a value bc we default
480
- # them in __new__ of the Model class
481
- item = obj.get(self._rest_name)
482
- if item is None:
483
- return item
484
- return _deserialize(self._type, _serialize(item))
485
-
486
- def __set__(self, obj: Model, value) -> None:
487
- if value is None:
488
- # we want to wipe out entries if users set attr to None
489
- try:
490
- obj.__delitem__(self._rest_name)
491
- except KeyError:
492
- pass
493
- return
494
- if self._is_model and not _is_model(value):
495
- obj.__setitem__(self._rest_name, _deserialize(self._type, value))
496
- obj.__setitem__(self._rest_name, _serialize(value))
497
-
498
- def _get_deserialize_callable_from_annotation(self, annotation: typing.Any) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
460
+ def _get_deserialize_callable_from_annotation(
461
+ annotation: typing.Any, module: str,
462
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
499
463
  if not annotation or annotation in [int, float]:
500
464
  return None
501
465
 
502
466
  try:
503
- if _is_model(_get_model(self._module, annotation)):
504
- self._is_model = True
467
+ if _is_model(_get_model(module, annotation)):
505
468
  def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
506
469
  if _is_model(obj):
507
470
  return obj
508
471
  return _deserialize(model_deserializer, obj)
509
- return functools.partial(_deserialize_model, _get_model(self._module, annotation))
472
+
473
+ return functools.partial(_deserialize_model, _get_model(module, annotation))
510
474
  except Exception:
511
475
  pass
512
476
 
@@ -517,49 +481,61 @@ class _RestField:
517
481
  except AttributeError:
518
482
  pass
519
483
 
484
+ if isinstance(annotation, typing._GenericAlias): # pylint: disable=protected-access
485
+ if annotation.__origin__ is typing.Union:
486
+ def _deserialize_with_union(union_annotation: typing._GenericAlias, obj):
487
+ for t in union_annotation.__args__:
488
+ try:
489
+ return _deserialize(t, obj)
490
+ except DeserializationError:
491
+ pass
492
+ raise DeserializationError()
493
+ return functools.partial(_deserialize_with_union, annotation)
494
+
520
495
  # is it optional?
521
496
  try:
522
497
  # right now, assuming we don't have unions, since we're getting rid of the only
523
498
  # union we used to have in msrest models, which was union of str and enum
524
499
  if any(a for a in annotation.__args__ if a == type(None)):
525
500
 
526
- if_obj_deserializer = self._get_deserialize_callable_from_annotation(
527
- next(a for a in annotation.__args__ if a != type(None)),
501
+ if_obj_deserializer = _get_deserialize_callable_from_annotation(
502
+ next(a for a in annotation.__args__ if a != type(None)), module
528
503
  )
504
+
529
505
  def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
530
506
  if obj is None:
531
507
  return obj
532
- return _deserialize(if_obj_deserializer, obj)
508
+ return _deserialize_with_callable(if_obj_deserializer, obj)
533
509
 
534
510
  return functools.partial(_deserialize_with_optional, if_obj_deserializer)
535
511
  except (AttributeError):
536
512
  pass
537
513
 
538
-
539
514
  # is it a forward ref / in quotes?
540
515
  if isinstance(annotation, str) or type(annotation) == typing.ForwardRef:
541
516
  try:
542
517
  model_name = annotation.__forward_arg__ # type: ignore
543
518
  except AttributeError:
544
519
  model_name = annotation
545
- if self._module is not None:
546
- annotation = _get_model(self._module, model_name)
520
+ if module is not None:
521
+ annotation = _get_model(module, model_name)
547
522
 
548
523
  try:
549
524
  if annotation._name == "Dict":
550
- key_deserializer = self._get_deserialize_callable_from_annotation(annotation.__args__[0])
551
- value_deserializer = self._get_deserialize_callable_from_annotation(annotation.__args__[1])
525
+ key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
526
+ value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module)
527
+
552
528
  def _deserialize_dict(
553
529
  key_deserializer: typing.Optional[typing.Callable],
554
530
  value_deserializer: typing.Optional[typing.Callable],
555
- obj: typing.Dict[typing.Any, typing.Any]
531
+ obj: typing.Dict[typing.Any, typing.Any],
556
532
  ):
557
533
  if obj is None:
558
534
  return obj
559
535
  return {
560
- _deserialize(key_deserializer, k): _deserialize(value_deserializer, v)
561
- for k, v in obj.items()
536
+ _deserialize(key_deserializer, k): _deserialize(value_deserializer, v) for k, v in obj.items()
562
537
  }
538
+
563
539
  return functools.partial(
564
540
  _deserialize_dict,
565
541
  key_deserializer,
@@ -570,38 +546,31 @@ class _RestField:
570
546
  try:
571
547
  if annotation._name in ["List", "Set", "Tuple", "Sequence"]:
572
548
  if len(annotation.__args__) > 1:
549
+
573
550
  def _deserialize_multiple_sequence(
574
- entry_deserializers: typing.List[typing.Optional[typing.Callable]],
575
- obj
551
+ entry_deserializers: typing.List[typing.Optional[typing.Callable]], obj
576
552
  ):
577
553
  if obj is None:
578
554
  return obj
579
555
  return type(obj)(
580
- _deserialize(deserializer, entry)
581
- for entry, deserializer in zip(obj, entry_deserializers)
556
+ _deserialize(deserializer, entry) for entry, deserializer in zip(obj, entry_deserializers)
582
557
  )
558
+
583
559
  entry_deserializers = [
584
- self._get_deserialize_callable_from_annotation(dt)
585
- for dt in annotation.__args__
560
+ _get_deserialize_callable_from_annotation(dt, module) for dt in annotation.__args__
586
561
  ]
587
- return functools.partial(
588
- _deserialize_multiple_sequence,
589
- entry_deserializers
590
- )
591
- deserializer = self._get_deserialize_callable_from_annotation(annotation.__args__[0])
562
+ return functools.partial(_deserialize_multiple_sequence, entry_deserializers)
563
+ deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
564
+
592
565
  def _deserialize_sequence(
593
566
  deserializer: typing.Optional[typing.Callable],
594
567
  obj,
595
568
  ):
596
569
  if obj is None:
597
570
  return obj
598
- return type(obj)(
599
- _deserialize(deserializer, entry) for entry in obj
600
- )
601
- return functools.partial(
602
- _deserialize_sequence,
603
- deserializer
604
- )
571
+ return type(obj)(_deserialize(deserializer, entry) for entry in obj)
572
+
573
+ return functools.partial(_deserialize_sequence, deserializer)
605
574
  except (TypeError, IndexError, AttributeError, SyntaxError):
606
575
  pass
607
576
 
@@ -613,15 +582,84 @@ class _RestField:
613
582
  if obj is None:
614
583
  return obj
615
584
  try:
616
- return _deserialize(annotation, obj)
585
+ return _deserialize_with_callable(annotation, obj)
617
586
  except Exception:
618
587
  pass
619
- return _deserialize(deserializer_from_mapping, obj)
620
- return functools.partial(
621
- _deserialize_default,
622
- annotation,
623
- _DESERIALIZE_MAPPING.get(annotation)
624
- )
588
+ return _deserialize_with_callable(deserializer_from_mapping, obj)
589
+
590
+ return functools.partial(_deserialize_default, annotation, _DESERIALIZE_MAPPING.get(annotation))
591
+
592
+
593
+ def _deserialize_with_callable(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
594
+ try:
595
+ if value is None:
596
+ return None
597
+ if isinstance(deserializer, CaseInsensitiveEnumMeta):
598
+ try:
599
+ return deserializer(value)
600
+ except ValueError:
601
+ # for unknown value, return raw value
602
+ return value
603
+ if isinstance(deserializer, type) and issubclass(deserializer, Model):
604
+ return deserializer._deserialize(value)
605
+ return deserializer(value) if deserializer else value
606
+ except Exception as e:
607
+ raise DeserializationError() from e
608
+
609
+
610
+ def _deserialize(deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], value: typing.Any):
611
+ deserializer = _get_deserialize_callable_from_annotation(deserializer, "")
612
+ return _deserialize_with_callable(deserializer, value)
613
+
614
+ class _RestField:
615
+ def __init__(
616
+ self,
617
+ *,
618
+ name: typing.Optional[str] = None,
619
+ type: typing.Optional[typing.Callable] = None,
620
+ is_discriminator: bool = False,
621
+ readonly: bool = False,
622
+ default: typing.Any = _UNSET,
623
+ ):
624
+ self._type = type
625
+ self._rest_name_input = name
626
+ self._module: typing.Optional[str] = None
627
+ self._is_discriminator = is_discriminator
628
+ self._readonly = readonly
629
+ self._is_model = False
630
+ self._default = default
631
+
632
+ @property
633
+ def _rest_name(self) -> str:
634
+ if self._rest_name_input is None:
635
+ raise ValueError("Rest name was never set")
636
+ return self._rest_name_input
637
+
638
+ def __get__(self, obj: Model, type=None):
639
+ # by this point, type and rest_name will have a value bc we default
640
+ # them in __new__ of the Model class
641
+ item = obj.get(self._rest_name)
642
+ if item is None:
643
+ return item
644
+ return _deserialize(self._type, _serialize(item))
645
+
646
+ def __set__(self, obj: Model, value) -> None:
647
+ if value is None:
648
+ # we want to wipe out entries if users set attr to None
649
+ try:
650
+ obj.__delitem__(self._rest_name)
651
+ except KeyError:
652
+ pass
653
+ return
654
+ if self._is_model and not _is_model(value):
655
+ obj.__setitem__(self._rest_name, _deserialize(self._type, value))
656
+ obj.__setitem__(self._rest_name, _serialize(value))
657
+
658
+ def _get_deserialize_callable_from_annotation(
659
+ self, annotation: typing.Any
660
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
661
+ return _get_deserialize_callable_from_annotation(annotation, self._module)
662
+
625
663
 
626
664
  def rest_field(
627
665
  *,
@@ -632,5 +670,8 @@ def rest_field(
632
670
  ) -> typing.Any:
633
671
  return _RestField(name=name, type=type, readonly=readonly, default=default)
634
672
 
635
- def rest_discriminator(*, name: typing.Optional[str] = None, type: typing.Optional[typing.Callable] = None) -> typing.Any:
673
+
674
+ def rest_discriminator(
675
+ *, name: typing.Optional[str] = None, type: typing.Optional[typing.Callable] = None
676
+ ) -> typing.Any:
636
677
  return _RestField(name=name, type=type, is_discriminator=True)
@@ -9,7 +9,7 @@
9
9
  {% endif %}
10
10
  {% if model.has_readonly_or_constant_property %}
11
11
 
12
- Variables are only populated by the server, and will be ignored when sending a request.
12
+ Readonly variables are only populated by the server, and will be ignored when sending a request.
13
13
  {% endif %}
14
14
  {% if (model.properties | selectattr('optional', "equalto", false) | first) is defined %}
15
15
 
@@ -58,6 +58,7 @@ setup(
58
58
  "Programming Language :: Python :: 3.8",
59
59
  "Programming Language :: Python :: 3.9",
60
60
  "Programming Language :: Python :: 3.10",
61
+ "Programming Language :: Python :: 3.11",
61
62
  "License :: OSI Approved :: MIT License",
62
63
  ],
63
64
  zip_safe=False,
@@ -15,7 +15,7 @@
15
15
  {{ request_builder_serializer.construct_url(request_builder) }}
16
16
  {% if request_builder.parameters.path %}
17
17
  {{ op_tools.serialize(request_builder_serializer.serialize_path(request_builder)) | indent }}
18
- _url = _format_url_section(_url, **path_format_arguments)
18
+ _url: str = _format_url_section(_url, **path_format_arguments) # type: ignore
19
19
  {% endif %}
20
20
 
21
21
  {% if request_builder.parameters.query %}