@typespec/http-client-python 0.34.1 → 0.35.0
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.
- package/dist/emitter/code-model.d.ts.map +1 -1
- package/dist/emitter/code-model.js.map +1 -1
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +5 -1
- package/dist/emitter/types.js.map +1 -1
- package/emitter/src/code-model.ts +1 -2
- package/emitter/src/types.ts +6 -2
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/ci/config/pylintrc +5 -1
- package/eng/scripts/ci/dev_requirements.txt +3 -3
- package/eng/scripts/ci/regenerate-common.ts +3 -0
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/generator/build/lib/pygen/__init__.py +2 -0
- package/generator/build/lib/pygen/codegen/models/client.py +15 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +7 -0
- package/generator/build/lib/pygen/codegen/models/operation.py +10 -2
- package/generator/build/lib/pygen/codegen/models/operation_group.py +2 -0
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +5 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +50 -5
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +23 -3
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +41 -11
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +27 -10
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +5 -1
- package/generator/build/lib/pygen/preprocess/__init__.py +19 -0
- package/generator/build/lib/pygen/utils.py +1 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +2 -0
- package/generator/pygen/codegen/models/client.py +15 -0
- package/generator/pygen/codegen/models/enum_type.py +7 -0
- package/generator/pygen/codegen/models/operation.py +10 -2
- package/generator/pygen/codegen/models/operation_group.py +2 -0
- package/generator/pygen/codegen/models/primitive_types.py +5 -0
- package/generator/pygen/codegen/serializers/builder_serializer.py +50 -5
- package/generator/pygen/codegen/serializers/model_serializer.py +23 -3
- package/generator/pygen/codegen/serializers/types_serializer.py +41 -11
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +27 -10
- package/generator/pygen/codegen/templates/serialization.py.jinja2 +5 -1
- package/generator/pygen/preprocess/__init__.py +19 -0
- package/generator/pygen/utils.py +1 -1
- package/package.json +35 -34
|
@@ -50,6 +50,11 @@ class PrimitiveType(BaseType):
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class BooleanType(PrimitiveType):
|
|
53
|
+
def __init__(self, yaml_data: dict[str, Any], code_model: "CodeModel") -> None:
|
|
54
|
+
super().__init__(yaml_data=yaml_data, code_model=code_model)
|
|
55
|
+
if yaml_data.get("encode") == "string":
|
|
56
|
+
self.encode = "str"
|
|
57
|
+
|
|
53
58
|
def serialization_type(self, **kwargs: Any) -> str:
|
|
54
59
|
return "bool"
|
|
55
60
|
|
|
@@ -793,6 +793,34 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
793
793
|
retval.extend(self._serialize_body_parameter(builder))
|
|
794
794
|
return retval
|
|
795
795
|
|
|
796
|
+
@staticmethod
|
|
797
|
+
def _collapsed_binary_bytes_overload(builder: OperationType) -> Optional[OperationType]:
|
|
798
|
+
"""Return the binary overload of a binary ``bytes`` body, or ``None``.
|
|
799
|
+
|
|
800
|
+
A binary ``bytes`` body pairs a ``bytes`` overload (binary content type) with the added
|
|
801
|
+
``IO[bytes]`` overload. Both serialize to raw content on the same content kwarg, so body
|
|
802
|
+
serialization collapses to a single unconditional assignment. This returns the binary
|
|
803
|
+
overload to serialize in that case, or ``None`` when the isinstance branch is needed.
|
|
804
|
+
"""
|
|
805
|
+
if not builder.overloads:
|
|
806
|
+
return None
|
|
807
|
+
binary_ov = next(
|
|
808
|
+
(o for o in builder.overloads if isinstance(o.parameters.body_parameter.type, BinaryType)), None
|
|
809
|
+
)
|
|
810
|
+
other_ov = next(
|
|
811
|
+
(o for o in builder.overloads if not isinstance(o.parameters.body_parameter.type, BinaryType)), None
|
|
812
|
+
)
|
|
813
|
+
if (
|
|
814
|
+
binary_ov is not None
|
|
815
|
+
and other_ov is not None
|
|
816
|
+
and isinstance(other_ov.parameters.body_parameter.type, ByteArraySchema)
|
|
817
|
+
and other_ov.parameters.body_parameter.default_content_type != "application/json"
|
|
818
|
+
and binary_ov.request_builder.parameters.body_parameter.client_name
|
|
819
|
+
== other_ov.request_builder.parameters.body_parameter.client_name
|
|
820
|
+
):
|
|
821
|
+
return cast(OperationType, binary_ov)
|
|
822
|
+
return None
|
|
823
|
+
|
|
796
824
|
def _initialize_overloads(self, builder: OperationType, is_paging: bool = False) -> list[str]:
|
|
797
825
|
retval: list[str] = []
|
|
798
826
|
# For paging, we put body parameter in local place outside `prepare_request`
|
|
@@ -817,9 +845,26 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
817
845
|
all_dpg_model_overloads = all(
|
|
818
846
|
_is_dpg_or_typeddict_body(o.parameters.body_parameter) for o in builder.overloads
|
|
819
847
|
)
|
|
820
|
-
|
|
848
|
+
# A binary `bytes` body pairs a `bytes` overload (binary content type) with the added
|
|
849
|
+
# `IO[bytes]` overload. Both serialize to raw content on the same content kwarg, so we
|
|
850
|
+
# emit a single unconditional assignment instead of an `isinstance` branch (which would be
|
|
851
|
+
# redundant and confuse mypy's type narrowing). Since the assignment is unconditional, the
|
|
852
|
+
# `_<body> = None` pre-init below is skipped for this case as well.
|
|
853
|
+
collapsed_binary_bytes_overload = self._collapsed_binary_bytes_overload(builder)
|
|
854
|
+
|
|
855
|
+
if not all_dpg_model_overloads and collapsed_binary_bytes_overload is None:
|
|
821
856
|
for v in sorted(set(client_names), key=client_names.index):
|
|
822
857
|
retval.append(f"_{v} = None")
|
|
858
|
+
|
|
859
|
+
if collapsed_binary_bytes_overload is not None:
|
|
860
|
+
collapsed_body_param = collapsed_binary_bytes_overload.parameters.body_parameter
|
|
861
|
+
if collapsed_body_param.default_content_type and not same_content_type:
|
|
862
|
+
retval.append(
|
|
863
|
+
f'content_type = content_type or "{collapsed_body_param.default_content_type}"{check_body_suffix}'
|
|
864
|
+
)
|
|
865
|
+
retval.extend(self._create_body_parameter(collapsed_binary_bytes_overload))
|
|
866
|
+
return retval
|
|
867
|
+
|
|
823
868
|
try:
|
|
824
869
|
# if there is a binary overload, we do a binary check first.
|
|
825
870
|
binary_overload = cast(
|
|
@@ -827,6 +872,10 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
827
872
|
next((o for o in builder.overloads if isinstance(o.parameters.body_parameter.type, BinaryType))),
|
|
828
873
|
)
|
|
829
874
|
binary_body_param = binary_overload.parameters.body_parameter
|
|
875
|
+
other_overload = cast(
|
|
876
|
+
OperationType,
|
|
877
|
+
next((o for o in builder.overloads if not isinstance(o.parameters.body_parameter.type, BinaryType))),
|
|
878
|
+
)
|
|
830
879
|
retval.append(f"if {binary_body_param.type.instance_check_template.format(binary_body_param.client_name)}:")
|
|
831
880
|
if binary_body_param.default_content_type and not same_content_type:
|
|
832
881
|
retval.append(
|
|
@@ -834,10 +883,6 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
|
|
|
834
883
|
)
|
|
835
884
|
retval.extend(f" {l}" for l in self._create_body_parameter(binary_overload))
|
|
836
885
|
retval.append("else:")
|
|
837
|
-
other_overload = cast(
|
|
838
|
-
OperationType,
|
|
839
|
-
next((o for o in builder.overloads if not isinstance(o.parameters.body_parameter.type, BinaryType))),
|
|
840
|
-
)
|
|
841
886
|
retval.extend(f" {l}" for l in self._create_body_parameter(other_overload))
|
|
842
887
|
if other_overload.parameters.body_parameter.default_content_type and not same_content_type:
|
|
843
888
|
retval.append(
|
|
@@ -23,7 +23,7 @@ from ..models.primitive_types import (
|
|
|
23
23
|
)
|
|
24
24
|
from .import_serializer import FileImportSerializer
|
|
25
25
|
from .base_serializer import BaseSerializer
|
|
26
|
-
from ..models.utils import NamespaceType
|
|
26
|
+
from ..models.utils import NamespaceType, add_to_pylint_disable
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def _get_xml_deserializer_name(prop: Property) -> Optional[str]: # pylint: disable=too-many-return-statements
|
|
@@ -199,6 +199,16 @@ class _ModelSerializer(BaseSerializer, ABC):
|
|
|
199
199
|
def pylint_disable(self, model: ModelType) -> str:
|
|
200
200
|
return " # pylint: disable=" + ", ".join(self.pylint_disable_items(model))
|
|
201
201
|
|
|
202
|
+
def class_pylint_disable(self, model: ModelType) -> str:
|
|
203
|
+
"""Class-level pylint disables for the model declaration line."""
|
|
204
|
+
retval = model.pylint_disable()
|
|
205
|
+
# When the model's only constructor is ``def __init__(self, *args, **kwargs)`` (i.e. no
|
|
206
|
+
# typed overload is generated), the guideline checker reports the ``*args`` vararg as an
|
|
207
|
+
# undocumented param. There is no meaningful param to document, so silence the check.
|
|
208
|
+
if not self.need_init(model) and self.initialize_properties(model):
|
|
209
|
+
retval = add_to_pylint_disable(retval, "docstring-missing-param")
|
|
210
|
+
return retval
|
|
211
|
+
|
|
202
212
|
def global_pylint_disables(self) -> str:
|
|
203
213
|
return ""
|
|
204
214
|
|
|
@@ -244,7 +254,7 @@ class MsrestModelSerializer(_ModelSerializer):
|
|
|
244
254
|
)
|
|
245
255
|
if model.parents:
|
|
246
256
|
basename = ", ".join([m.name for m in model.parents])
|
|
247
|
-
return f"class {model.name}({basename}):{
|
|
257
|
+
return f"class {model.name}({basename}):{self.class_pylint_disable(model)}"
|
|
248
258
|
|
|
249
259
|
@staticmethod
|
|
250
260
|
def get_properties_to_initialize(model: ModelType) -> list[Property]:
|
|
@@ -407,7 +417,17 @@ class DpgModelSerializer(_ModelSerializer):
|
|
|
407
417
|
basename = ", ".join([m.name for m in model.parents])
|
|
408
418
|
if model.discriminator_value:
|
|
409
419
|
basename += f", discriminator='{model.discriminator_value}'"
|
|
410
|
-
return f"class {model.name}({basename}):{
|
|
420
|
+
return f"class {model.name}({basename}):{self.class_pylint_disable(model)}"
|
|
421
|
+
|
|
422
|
+
def class_pylint_disable(self, model: ModelType) -> str:
|
|
423
|
+
retval = super().class_pylint_disable(model)
|
|
424
|
+
# DPG models render an empty ``@overload def __init__(self, *, ...)`` body, so the
|
|
425
|
+
# guideline checker validates the keyword-only arguments against the *class* docstring.
|
|
426
|
+
# We only document those arguments as instance variables (``:ivar:``) to avoid a redundant
|
|
427
|
+
# ``:keyword:`` entry per property, so silence the keyword/keyword-only correlation check.
|
|
428
|
+
if self._init_line_parameters(model):
|
|
429
|
+
retval = add_to_pylint_disable(retval, "docstring-keyword-should-match-keyword-only")
|
|
430
|
+
return retval
|
|
411
431
|
|
|
412
432
|
@staticmethod
|
|
413
433
|
def get_properties_to_declare(model: ModelType) -> list[Property]:
|
|
@@ -40,13 +40,38 @@ _BUILTIN_TYPE_NAMES = frozenset(
|
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
# Matches a single- or double-quoted string literal (escape-aware). Used to split an
|
|
44
|
+
# annotation so that bare-identifier rewriting/detection skips text inside string literals
|
|
45
|
+
# such as ``Literal["type"]`` values or quoted forward references like ``"SomeModel"``.
|
|
46
|
+
_STRING_LITERAL_RE = re.compile(r"""('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")""")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _annotation_references_builtin(annotation: str, name: str) -> bool:
|
|
50
|
+
"""Whether ``name`` appears as a bare (non-dotted) identifier outside any string literal."""
|
|
51
|
+
pattern = re.compile(rf"(?<!\.)\b{name}\b")
|
|
52
|
+
# re.split with a single capturing group yields code/string/code/string/... segments.
|
|
53
|
+
for index, segment in enumerate(_STRING_LITERAL_RE.split(annotation)):
|
|
54
|
+
if index % 2 == 0 and pattern.search(segment):
|
|
55
|
+
return True
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
|
|
43
59
|
def _qualify_shadowed_builtins(annotation: str, shadowed: frozenset[str]) -> str:
|
|
44
|
-
"""Replace bare builtin type references with builtins.X when shadowed by a field name.
|
|
60
|
+
"""Replace bare builtin type references with builtins.X when shadowed by a field name.
|
|
61
|
+
|
|
62
|
+
Only bare identifiers in real type-expression positions are rewritten; identifiers
|
|
63
|
+
inside string literals (e.g. ``Literal["type"]`` values, quoted forward references)
|
|
64
|
+
and already-dotted names are left untouched.
|
|
65
|
+
"""
|
|
45
66
|
if not shadowed:
|
|
46
67
|
return annotation
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
segments = _STRING_LITERAL_RE.split(annotation)
|
|
69
|
+
for index in range(0, len(segments), 2): # even indices are code, odd are string literals
|
|
70
|
+
segment = segments[index]
|
|
71
|
+
for name in shadowed:
|
|
72
|
+
segment = re.sub(rf"(?<!\.)\b{name}\b", f"builtins.{name}", segment)
|
|
73
|
+
segments[index] = segment
|
|
74
|
+
return "".join(segments)
|
|
50
75
|
|
|
51
76
|
|
|
52
77
|
class TypesSerializer(BaseSerializer):
|
|
@@ -225,22 +250,27 @@ class TypesSerializer(BaseSerializer):
|
|
|
225
250
|
"""Whether any property wire_name is a Python keyword or requires functional TypedDict form."""
|
|
226
251
|
return any(keyword.iskeyword(p.wire_name) or not p.wire_name.isidentifier() for p in model.properties)
|
|
227
252
|
|
|
228
|
-
|
|
229
|
-
def get_shadowed_builtins(model: ModelType) -> frozenset[str]:
|
|
253
|
+
def get_shadowed_builtins(self, model: ModelType) -> frozenset[str]:
|
|
230
254
|
"""Return the set of builtin type names shadowed by property wire_names in this model.
|
|
231
255
|
|
|
232
|
-
Only includes a builtin if it is both used as a wire_name AND referenced
|
|
233
|
-
in
|
|
256
|
+
Only includes a builtin if it is both used as a wire_name AND referenced as a bare
|
|
257
|
+
type in an annotation within the same model (otherwise no shadowing occurs). Detection
|
|
258
|
+
uses the exact ``TYPES_FILE`` annotation emitted for each property so that types which
|
|
259
|
+
change under the types-file serialization (e.g. ``bytes``/``datetime`` -> ``str``,
|
|
260
|
+
``decimal`` -> ``float``) are evaluated as they actually appear in the output.
|
|
234
261
|
"""
|
|
235
262
|
wire_builtins = {p.wire_name for p in model.properties if p.wire_name in _BUILTIN_TYPE_NAMES}
|
|
236
263
|
if not wire_builtins:
|
|
237
264
|
return frozenset()
|
|
238
|
-
# Check which of these builtins actually appear in type annotations
|
|
265
|
+
# Check which of these builtins actually appear in emitted type annotations
|
|
239
266
|
used = set()
|
|
240
267
|
for prop in model.properties:
|
|
241
|
-
annotation = prop.type_annotation(
|
|
268
|
+
annotation = prop.type_annotation(
|
|
269
|
+
serialize_namespace=self.serialize_namespace,
|
|
270
|
+
serialize_namespace_type=NamespaceType.TYPES_FILE,
|
|
271
|
+
)
|
|
242
272
|
for name in wire_builtins:
|
|
243
|
-
if
|
|
273
|
+
if _annotation_references_builtin(annotation, name):
|
|
244
274
|
used.add(name)
|
|
245
275
|
return frozenset(used)
|
|
246
276
|
|
|
@@ -161,7 +161,15 @@ def _is_readonly(p):
|
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
class SdkJSONEncoder(JSONEncoder):
|
|
164
|
-
"""A JSON encoder that's capable of serializing datetime objects and bytes.
|
|
164
|
+
"""A JSON encoder that's capable of serializing datetime objects and bytes.
|
|
165
|
+
|
|
166
|
+
:param args: Additional positional arguments passed to the base ``JSONEncoder``.
|
|
167
|
+
:type args: typing.Any
|
|
168
|
+
:keyword exclude_readonly: Whether to exclude readonly properties. Defaults to False.
|
|
169
|
+
:paramtype exclude_readonly: bool
|
|
170
|
+
:keyword format: The format to use for serialization. Defaults to None.
|
|
171
|
+
:paramtype format: typing.Optional[str]
|
|
172
|
+
"""
|
|
165
173
|
|
|
166
174
|
def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs):
|
|
167
175
|
super().__init__(*args, **kwargs)
|
|
@@ -348,6 +356,12 @@ def _deserialize_int_as_str(attr):
|
|
|
348
356
|
return int(attr)
|
|
349
357
|
|
|
350
358
|
|
|
359
|
+
def _deserialize_bool_as_str(attr):
|
|
360
|
+
if isinstance(attr, bool):
|
|
361
|
+
return attr
|
|
362
|
+
return attr.lower() == "true"
|
|
363
|
+
|
|
364
|
+
|
|
351
365
|
_DESERIALIZE_MAPPING = {
|
|
352
366
|
datetime: _deserialize_datetime,
|
|
353
367
|
date: _deserialize_date,
|
|
@@ -375,6 +389,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
|
|
|
375
389
|
def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
|
|
376
390
|
if annotation is int and rf and rf._format == "str":
|
|
377
391
|
return _deserialize_int_as_str
|
|
392
|
+
if annotation is bool and rf and rf._format == "str":
|
|
393
|
+
return _deserialize_bool_as_str
|
|
378
394
|
if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
|
|
379
395
|
return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
|
|
380
396
|
if rf and rf._format:
|
|
@@ -470,21 +486,21 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
470
486
|
|
|
471
487
|
def keys(self) -> typing.KeysView[str]:
|
|
472
488
|
"""
|
|
473
|
-
:returns: a set-like object providing a view on
|
|
489
|
+
:returns: a set-like object providing a view on the mapping's keys
|
|
474
490
|
:rtype: ~typing.KeysView
|
|
475
491
|
"""
|
|
476
492
|
return self._data.keys()
|
|
477
493
|
|
|
478
494
|
def values(self) -> typing.ValuesView[typing.Any]:
|
|
479
495
|
"""
|
|
480
|
-
:returns: an object providing a view on
|
|
496
|
+
:returns: an object providing a view on the mapping's values
|
|
481
497
|
:rtype: ~typing.ValuesView
|
|
482
498
|
"""
|
|
483
499
|
return self._data.values()
|
|
484
500
|
|
|
485
501
|
def items(self) -> typing.ItemsView[str, typing.Any]:
|
|
486
502
|
"""
|
|
487
|
-
:returns: set-like object providing a view on
|
|
503
|
+
:returns: a set-like object providing a view on the mapping's items
|
|
488
504
|
:rtype: ~typing.ItemsView
|
|
489
505
|
"""
|
|
490
506
|
return self._data.items()
|
|
@@ -494,7 +510,7 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
494
510
|
Get the value for key if key is in the dictionary, else default.
|
|
495
511
|
:param str key: The key to look up.
|
|
496
512
|
:param any default: The value to return if key is not in the dictionary. Defaults to None
|
|
497
|
-
:returns:
|
|
513
|
+
:returns: The value for key if key is in the dictionary, else default.
|
|
498
514
|
:rtype: any
|
|
499
515
|
"""
|
|
500
516
|
try:
|
|
@@ -529,19 +545,19 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
529
545
|
Removes and returns some (key, value) pair
|
|
530
546
|
:returns: The (key, value) pair.
|
|
531
547
|
:rtype: tuple
|
|
532
|
-
:raises KeyError: if
|
|
548
|
+
:raises KeyError: if the dictionary is empty.
|
|
533
549
|
"""
|
|
534
550
|
return self._data.popitem()
|
|
535
551
|
|
|
536
552
|
def clear(self) -> None:
|
|
537
553
|
"""
|
|
538
|
-
Remove all items from
|
|
554
|
+
Remove all items from the dictionary.
|
|
539
555
|
"""
|
|
540
556
|
self._data.clear()
|
|
541
557
|
|
|
542
558
|
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=arguments-differ
|
|
543
559
|
"""
|
|
544
|
-
|
|
560
|
+
Update the dictionary from a mapping or an iterable of key-value pairs.
|
|
545
561
|
:param any args: Either a mapping object or an iterable of key-value pairs.
|
|
546
562
|
"""
|
|
547
563
|
self._data.update(*args, **kwargs)
|
|
@@ -554,10 +570,11 @@ class _MyMutableMapping(MutableMapping[str, typing.Any]):
|
|
|
554
570
|
|
|
555
571
|
def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
|
|
556
572
|
"""
|
|
557
|
-
|
|
573
|
+
Return the value for key if key is in the dictionary; otherwise set the key to
|
|
574
|
+
default and return default.
|
|
558
575
|
:param str key: The key to look up.
|
|
559
576
|
:param any default: The value to set if key is not in the dictionary
|
|
560
|
-
:returns:
|
|
577
|
+
:returns: The value for key if key is in the dictionary, else default.
|
|
561
578
|
:rtype: any
|
|
562
579
|
"""
|
|
563
580
|
if default is _UNSET:
|
|
@@ -476,7 +476,11 @@ def _decode_attribute_map_key(key):
|
|
|
476
476
|
|
|
477
477
|
|
|
478
478
|
class Serializer: # pylint: disable=too-many-public-methods
|
|
479
|
-
"""Request object model serializer.
|
|
479
|
+
"""Request object model serializer.
|
|
480
|
+
|
|
481
|
+
:param classes: Mapping of model names to model types, used to resolve models during serialization.
|
|
482
|
+
:type classes: typing.Optional[typing.Mapping[str, type]]
|
|
483
|
+
"""
|
|
480
484
|
|
|
481
485
|
basic_types = {str: "str", int: "int", bool: "bool", float: "float"}
|
|
482
486
|
|
|
@@ -399,6 +399,25 @@ class PreProcessPlugin(YamlUpdatePlugin):
|
|
|
399
399
|
code_model: dict[str, Any],
|
|
400
400
|
body_parameter: dict[str, Any],
|
|
401
401
|
):
|
|
402
|
+
# For a binary `bytes` body (e.g. content type application/octet-stream or a custom
|
|
403
|
+
# binary media type), add an IO overload alongside the `bytes` one. This keeps backward
|
|
404
|
+
# compatibility for services migrating from swagger, whose binary bodies were typed as IO.
|
|
405
|
+
is_multipart = self.is_tsp and has_multi_part_content_type(body_parameter)
|
|
406
|
+
is_binary_body = not has_json_content_type(body_parameter) and not is_multipart
|
|
407
|
+
if (
|
|
408
|
+
body_parameter
|
|
409
|
+
and body_parameter["type"]["type"] == "bytes"
|
|
410
|
+
and is_binary_body
|
|
411
|
+
and self.options["models-mode"] != "typeddict"
|
|
412
|
+
and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
|
|
413
|
+
):
|
|
414
|
+
body_parameter["type"] = {
|
|
415
|
+
"type": "combined",
|
|
416
|
+
"types": [body_parameter["type"], KNOWN_TYPES["binary"]],
|
|
417
|
+
}
|
|
418
|
+
code_model["types"].append(body_parameter["type"])
|
|
419
|
+
return
|
|
420
|
+
|
|
402
421
|
# only add overload for special content type
|
|
403
422
|
if ( # pylint: disable=too-many-boolean-expressions
|
|
404
423
|
body_parameter
|
package/generator/pygen/utils.py
CHANGED
|
@@ -109,7 +109,7 @@ def parse_args(
|
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
def get_body_type_for_description(body_parameter: dict[str, Any]) -> str:
|
|
112
|
-
if body_parameter["type"]["type"]
|
|
112
|
+
if body_parameter["type"]["type"] in ("binary", "bytes"):
|
|
113
113
|
return "binary"
|
|
114
114
|
if body_parameter["type"]["type"] == "string":
|
|
115
115
|
return "string"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typespec/http-client-python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"author": "Microsoft Corporation",
|
|
5
5
|
"description": "TypeSpec emitter for Python SDKs",
|
|
6
6
|
"homepage": "https://typespec.io",
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"ci:generated": "tsx ./eng/scripts/ci/run-tests.ts --generator --env=ci",
|
|
55
55
|
"change:version": "pnpm chronus version --ignore-policies --only @typespec/http-client-python",
|
|
56
56
|
"change:add": "pnpm chronus add",
|
|
57
|
-
"regen-docs": "node ../../packages/tspd/cmd/tspd.js doc . --enable-experimental --output-dir ../../website/src/content/docs/docs/emitters/clients/http-client-python/reference --skip-js"
|
|
57
|
+
"regen-docs": "node ../../packages/tspd/cmd/tspd.js doc . --enable-experimental --output-dir ../../website/src/content/docs/docs/emitters/clients/http-client-python/reference --skip-js",
|
|
58
|
+
"diff-spector-tests": "node ../../eng/emitter-diff/src/cli.ts --emitter python"
|
|
58
59
|
},
|
|
59
60
|
"files": [
|
|
60
61
|
"dist/**",
|
|
@@ -64,20 +65,20 @@
|
|
|
64
65
|
"emitter"
|
|
65
66
|
],
|
|
66
67
|
"peerDependencies": {
|
|
67
|
-
"@azure-tools/typespec-autorest": ">=0.
|
|
68
|
-
"@azure-tools/typespec-azure-core": ">=0.
|
|
69
|
-
"@azure-tools/typespec-azure-resource-manager": ">=0.
|
|
70
|
-
"@azure-tools/typespec-azure-rulesets": ">=0.
|
|
71
|
-
"@azure-tools/typespec-client-generator-core": ">=0.
|
|
72
|
-
"@typespec/compiler": "^1.
|
|
73
|
-
"@typespec/http": "^1.
|
|
74
|
-
"@typespec/openapi": "^1.
|
|
75
|
-
"@typespec/rest": ">=0.
|
|
76
|
-
"@typespec/versioning": ">=0.
|
|
77
|
-
"@typespec/events": ">=0.
|
|
78
|
-
"@typespec/sse": ">=0.
|
|
79
|
-
"@typespec/streams": ">=0.
|
|
80
|
-
"@typespec/xml": ">=0.
|
|
68
|
+
"@azure-tools/typespec-autorest": ">=0.70.0 <1.0.0",
|
|
69
|
+
"@azure-tools/typespec-azure-core": ">=0.70.0 <1.0.0",
|
|
70
|
+
"@azure-tools/typespec-azure-resource-manager": ">=0.70.0 <1.0.0",
|
|
71
|
+
"@azure-tools/typespec-azure-rulesets": ">=0.70.0 <1.0.0",
|
|
72
|
+
"@azure-tools/typespec-client-generator-core": ">=0.70.0 <1.0.0",
|
|
73
|
+
"@typespec/compiler": "^1.14.0",
|
|
74
|
+
"@typespec/http": "^1.14.0",
|
|
75
|
+
"@typespec/openapi": "^1.14.0",
|
|
76
|
+
"@typespec/rest": ">=0.84.0 <1.0.0",
|
|
77
|
+
"@typespec/versioning": ">=0.84.0 <1.0.0",
|
|
78
|
+
"@typespec/events": ">=0.84.0 <1.0.0",
|
|
79
|
+
"@typespec/sse": ">=0.84.0 <1.0.0",
|
|
80
|
+
"@typespec/streams": ">=0.84.0 <1.0.0",
|
|
81
|
+
"@typespec/xml": ">=0.84.0 <1.0.0"
|
|
81
82
|
},
|
|
82
83
|
"playgroundConfig": {
|
|
83
84
|
"assets": [
|
|
@@ -102,24 +103,24 @@
|
|
|
102
103
|
"tsx": "^4.21.0"
|
|
103
104
|
},
|
|
104
105
|
"devDependencies": {
|
|
105
|
-
"@azure-tools/typespec-autorest": "~0.
|
|
106
|
-
"@azure-tools/typespec-azure-core": "~0.
|
|
107
|
-
"@azure-tools/typespec-azure-resource-manager": "~0.
|
|
108
|
-
"@azure-tools/typespec-azure-rulesets": "~0.
|
|
109
|
-
"@azure-tools/typespec-client-generator-core": "~0.
|
|
110
|
-
"@azure-tools/azure-http-specs": "0.1.0-alpha.
|
|
111
|
-
"@typespec/compiler": "^1.
|
|
112
|
-
"@typespec/http": "^1.
|
|
113
|
-
"@typespec/openapi": "^1.
|
|
114
|
-
"@typespec/rest": "~0.
|
|
115
|
-
"@typespec/versioning": "~0.
|
|
116
|
-
"@typespec/events": "~0.
|
|
117
|
-
"@typespec/spector": "0.1.0-alpha.
|
|
118
|
-
"@typespec/spec-api": "0.1.0-alpha.
|
|
119
|
-
"@typespec/sse": "~0.
|
|
120
|
-
"@typespec/streams": "~0.
|
|
121
|
-
"@typespec/xml": "~0.
|
|
122
|
-
"@typespec/http-specs": "0.1.0-alpha.39
|
|
106
|
+
"@azure-tools/typespec-autorest": "~0.70.0",
|
|
107
|
+
"@azure-tools/typespec-azure-core": "~0.70.0",
|
|
108
|
+
"@azure-tools/typespec-azure-resource-manager": "~0.70.0",
|
|
109
|
+
"@azure-tools/typespec-azure-rulesets": "~0.70.0",
|
|
110
|
+
"@azure-tools/typespec-client-generator-core": "~0.70.0",
|
|
111
|
+
"@azure-tools/azure-http-specs": "0.1.0-alpha.43",
|
|
112
|
+
"@typespec/compiler": "^1.14.0",
|
|
113
|
+
"@typespec/http": "^1.14.0",
|
|
114
|
+
"@typespec/openapi": "^1.14.0",
|
|
115
|
+
"@typespec/rest": "~0.84.0",
|
|
116
|
+
"@typespec/versioning": "~0.84.0",
|
|
117
|
+
"@typespec/events": "~0.84.0",
|
|
118
|
+
"@typespec/spector": "0.1.0-alpha.26",
|
|
119
|
+
"@typespec/spec-api": "0.1.0-alpha.15",
|
|
120
|
+
"@typespec/sse": "~0.84.0",
|
|
121
|
+
"@typespec/streams": "~0.84.0",
|
|
122
|
+
"@typespec/xml": "~0.84.0",
|
|
123
|
+
"@typespec/http-specs": "0.1.0-alpha.39",
|
|
123
124
|
"@types/js-yaml": "~4.0.5",
|
|
124
125
|
"@types/node": "~25.0.2",
|
|
125
126
|
"@types/semver": "7.5.8",
|