Skip to content

Commit 3a1483c

Browse files
committed
address comments
1 parent 0e43653 commit 3a1483c

1 file changed

Lines changed: 50 additions & 38 deletions

File tree

‎packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2‎

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{% endif %}
55
# pylint: disable=protected-access, broad-except
66

7+
import contextvars
78
import copy
89
import calendar
910
import decimal
@@ -656,41 +657,32 @@ def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typin
656657
if rf._is_multipart_file_input:
657658
return value
658659
if rf._is_model:
659-
return _deserialize(rf._type, value)
660+
if _is_model(value):
661+
return value
662+
return _deserialize_public(rf._type, value)
660663
if isinstance(value, ET.Element):
661664
value = _deserialize(rf._type, value)
662665
return _serialize(value, rf._format)
663666

664667

665-
def _clone_wire_value(value: typing.Any) -> typing.Any:
666-
if isinstance(value, list):
667-
return [_clone_wire_value(entry) for entry in value]
668-
if isinstance(value, dict):
669-
return {key: _clone_wire_value(entry) for key, entry in value.items()}
670-
if isinstance(value, set):
671-
return {_clone_wire_value(entry) for entry in value}
672-
if isinstance(value, tuple):
673-
return tuple(_clone_wire_value(entry) for entry in value)
674-
return value
675-
676-
677668
def _create_public_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
678-
if rf and rf._is_model:
679-
value = _clone_wire_value(value)
680669
return _create_value(rf, value)
681670

682671

683-
class _OwnedWireValue:
684-
__slots__ = ("value",)
685-
686-
def __init__(self, value: typing.Mapping[str, typing.Any]) -> None:
687-
self.value = value
672+
_WIRE_VALUE = contextvars.ContextVar("_WIRE_VALUE", default=None)
673+
_DESERIALIZING_PUBLIC_VALUE = contextvars.ContextVar("_DESERIALIZING_PUBLIC_VALUE", default=False)
688674

689675

690676
def _construct_from_wire(cls: type, data: typing.Any) -> typing.Any:
691-
# ET.Element payloads are passed through as-is; JSON payloads are wrapped in
692-
# _OwnedWireValue so the constructed model takes zero-copy ownership of the wire data.
693-
return cls(data) if isinstance(data, ET.Element) else cls(_OwnedWireValue(data))
677+
token = _WIRE_VALUE.set(data)
678+
try:
679+
return cls(data)
680+
finally:
681+
_WIRE_VALUE.reset(token)
682+
683+
684+
def _construct_model(cls: type, data: typing.Any) -> typing.Any:
685+
return cls(data) if _DESERIALIZING_PUBLIC_VALUE.get() else _construct_from_wire(cls, data)
694686

695687

696688
def _create_value_from_wire(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
@@ -707,7 +699,7 @@ def _create_value_from_wire(rf: typing.Optional["_RestField"], value: typing.Any
707699
:rtype: any
708700
"""
709701
if not rf:
710-
return _serialize(value, None)
702+
return value
711703
if rf._is_multipart_file_input:
712704
return value
713705
if rf._is_model:
@@ -890,6 +882,12 @@ def _extract_xml_model_type(rf_type):
890882
return None
891883

892884

885+
def _deserialize_xml_model(model_cls, value):
886+
if isinstance(value, list):
887+
return value
888+
return model_cls._deserialize(value, [])
889+
890+
893891
def _build_xml_field_plan( # pylint: disable=docstring-missing-return, docstring-missing-rtype, unused-variable
894892
cls, attr_to_rest_field: dict
895893
) -> list:
@@ -926,7 +924,7 @@ def _build_xml_field_plan( # pylint: disable=docstring-missing-return, docstrin
926924
if deser is None and rf._type is not None:
927925
model_cls = _extract_xml_model_type(rf._type)
928926
if model_cls is not None:
929-
deser = model_cls
927+
deser = functools.partial(_deserialize_xml_model, model_cls)
930928

931929
if prop_meta.get("attribute", False):
932930
plan.append((rf._rest_name, xml_name, 1, deser, rf._type, is_optional, None))
@@ -967,11 +965,7 @@ class Model(_MyMutableMapping):
967965
else:
968966
rest_field_by_rest_name = self._rest_field_by_rest_name
969967
mapping = args[0]
970-
if isinstance(mapping, _OwnedWireValue):
971-
mapping = mapping.value
972-
create_value = _create_value_from_wire
973-
else:
974-
create_value = _create_public_value
968+
create_value = _create_value_from_wire if _WIRE_VALUE.get() is mapping else _create_public_value
975969
dict_to_pass.update(
976970
{
977971
k: create_value(rest_field_by_rest_name.get(k), v)
@@ -1171,10 +1165,10 @@ class Model(_MyMutableMapping):
11711165
@classmethod
11721166
def _deserialize(cls, data, exist_discriminators):
11731167
if not hasattr(cls, "__mapping__"):
1174-
return _construct_from_wire(cls, data)
1168+
return _construct_model(cls, data)
11751169
discriminator = cls._get_discriminator(exist_discriminators)
11761170
if discriminator is None:
1177-
return _construct_from_wire(cls, data)
1171+
return _construct_model(cls, data)
11781172
exist_discriminators.append(discriminator._rest_name)
11791173
if isinstance(data, ET.Element):
11801174
model_meta = getattr(cls, "_xml", {})
@@ -1317,7 +1311,7 @@ def _deserialize_primitive_sequence(
13171311
"""Deserialize a homogeneous sequence of scalars.
13181312

13191313
Plain ``list``/``tuple``/``set`` inputs are converted with ``map(builtin, obj)``, falling
1320-
back to a per-element conversion that returns the raw entry when it cannot be converted.
1314+
back to the generic per-element deserializer when an entry cannot be converted directly.
13211315
Any other shape (encoded ``str``, ``ET.Element``, ...) is delegated to
13221316
:func:`_deserialize_sequence`.
13231317

@@ -1335,6 +1329,8 @@ def _deserialize_primitive_sequence(
13351329
if obj is None:
13361330
return obj
13371331
if isinstance(obj, (list, tuple, set)):
1332+
if obj and isinstance(next(iter(obj)), ET.Element):
1333+
return _deserialize_sequence(deserializer, module, obj)
13381334
try:
13391335
return type(obj)(map(builtin, obj))
13401336
except (TypeError, ValueError):
@@ -1345,7 +1341,7 @@ def _deserialize_primitive_sequence(
13451341
try:
13461342
return builtin(entry)
13471343
except (TypeError, ValueError):
1348-
return entry
1344+
return _deserialize(deserializer, entry, module)
13491345

13501346
return type(obj)(_lenient(entry) for entry in obj)
13511347
return _deserialize_sequence(deserializer, module, obj)
@@ -1498,10 +1494,15 @@ def _resolve_deserialize_callable_from_annotation( # pylint: disable=too-many-r
14981494
pass
14991495
return obj
15001496

1501-
if get_deserializer(annotation, rf):
1502-
return functools.partial(_deserialize_default, get_deserializer(annotation, rf))
1497+
deserializer = get_deserializer(annotation, rf)
1498+
{% if code_model.has_external_type %}
1499+
if deserializer is TYPE_HANDLER_REGISTRY.get_deserializer(annotation):
1500+
def _deserialize_external_default(obj):
1501+
return _deserialize_default(TYPE_HANDLER_REGISTRY.get_deserializer(annotation) or annotation, obj)
15031502

1504-
return functools.partial(_deserialize_default, annotation)
1503+
return _deserialize_external_default
1504+
{% endif %}
1505+
return functools.partial(_deserialize_default, deserializer or annotation)
15051506

15061507

15071508
def _deserialize_with_callable(
@@ -1557,6 +1558,17 @@ def _deserialize(
15571558
return _deserialize_with_callable(deserializer, value)
15581559

15591560

1561+
def _deserialize_public(
1562+
deserializer: typing.Any,
1563+
value: typing.Any,
1564+
) -> typing.Any:
1565+
token = _DESERIALIZING_PUBLIC_VALUE.set(True)
1566+
try:
1567+
return _deserialize(deserializer, value)
1568+
finally:
1569+
_DESERIALIZING_PUBLIC_VALUE.reset(token)
1570+
1571+
15601572
def _failsafe_deserialize(
15611573
deserializer: typing.Any,
15621574
response: HttpResponse,
@@ -1686,7 +1698,7 @@ class _RestField:
16861698
return
16871699
if self._is_model:
16881700
if not _is_model(value):
1689-
value = _deserialize(self._type, value)
1701+
value = _deserialize_public(self._type, value)
16901702
obj.__setitem__(self._rest_name, value)
16911703
return
16921704
obj.__setitem__(self._rest_name, _serialize(value, self._format))

0 commit comments

Comments
 (0)