@typespec/http-client-python 0.35.1 → 0.37.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/README.md +6 -0
- package/dist/emitter/code-model.d.ts +1 -1
- package/dist/emitter/code-model.d.ts.map +1 -1
- package/dist/emitter/code-model.js.map +1 -1
- package/dist/emitter/emitter.d.ts +2 -2
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +24 -3
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/external-process.d.ts +1 -15
- package/dist/emitter/external-process.d.ts.map +1 -1
- package/dist/emitter/external-process.js +1 -17
- package/dist/emitter/external-process.js.map +1 -1
- package/dist/emitter/http.d.ts +45 -2
- package/dist/emitter/http.d.ts.map +1 -1
- package/dist/emitter/http.js +97 -1
- package/dist/emitter/http.js.map +1 -1
- package/dist/emitter/index.d.ts +2 -1
- package/dist/emitter/index.d.ts.map +1 -1
- package/dist/emitter/index.js.map +1 -1
- package/dist/emitter/lib.d.ts +3 -2
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +6 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/node-runner.browser.d.ts +1 -1
- package/dist/emitter/node-runner.browser.d.ts.map +1 -1
- package/dist/emitter/node-runner.browser.js.map +1 -1
- package/dist/emitter/node-runner.d.ts +3 -3
- package/dist/emitter/node-runner.d.ts.map +1 -1
- package/dist/emitter/node-runner.js.map +1 -1
- package/dist/emitter/system-requirements.d.ts.map +1 -1
- package/dist/emitter/system-requirements.js.map +1 -1
- package/dist/emitter/types.d.ts +4 -4
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +15 -3
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts +2 -2
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +1 -1
- package/dist/emitter/utils.js.map +1 -1
- package/dist/emitter/yaml-utils.d.ts +19 -0
- package/dist/emitter/yaml-utils.d.ts.map +1 -0
- package/dist/emitter/yaml-utils.js +22 -0
- package/dist/emitter/yaml-utils.js.map +1 -0
- package/emitter/src/code-model.ts +4 -2
- package/emitter/src/emitter.ts +31 -7
- package/emitter/src/external-process.ts +3 -19
- package/emitter/src/http.ts +159 -5
- package/emitter/src/index.ts +2 -1
- package/emitter/src/lib.ts +11 -6
- package/emitter/src/node-runner.browser.ts +2 -1
- package/emitter/src/node-runner.ts +6 -3
- package/emitter/src/system-requirements.ts +2 -1
- package/emitter/src/types.ts +23 -8
- package/emitter/src/utils.ts +5 -4
- package/emitter/src/yaml-utils.ts +22 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/emitter/test/browser-pyodide.test.ts +25 -0
- package/emitter/test/streaming.test.ts +155 -0
- package/emitter/test/types.test.ts +72 -0
- package/emitter/test/{external-process.test.ts → yaml-utils.test.ts} +2 -2
- package/eng/scripts/ci/dev_requirements.txt +1 -1
- package/eng/scripts/ci/regenerate-common.ts +1 -1
- package/eng/scripts/ci/regenerate.ts +1 -2
- package/eng/scripts/ci/run-tests.ts +2 -1
- package/eng/scripts/ci/run_pylint.py +1 -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/eng/scripts/setup/system-requirements.ts +2 -1
- package/generator/build/lib/pygen/__init__.py +24 -5
- package/generator/build/lib/pygen/codegen/models/__init__.py +3 -1
- package/generator/build/lib/pygen/codegen/models/code_model.py +29 -9
- package/generator/build/lib/pygen/codegen/models/enum_type.py +21 -9
- package/generator/build/lib/pygen/codegen/models/list_type.py +1 -0
- package/generator/build/lib/pygen/codegen/models/model_type.py +1 -3
- package/generator/build/lib/pygen/codegen/models/operation.py +13 -3
- package/generator/build/lib/pygen/codegen/models/parameter.py +3 -1
- package/generator/build/lib/pygen/codegen/models/response.py +152 -8
- package/generator/build/lib/pygen/codegen/models/utils.py +14 -0
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +13 -6
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +219 -69
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +13 -6
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +4 -0
- package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +27 -0
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +4 -1
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +44 -12
- package/generator/build/lib/pygen/codegen/templates/streaming_base.py.jinja2 +767 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +70 -17
- package/generator/build/lib/pygen/utils.py +15 -0
- package/generator/component-detection-pip-report.json +9 -9
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +24 -5
- package/generator/pygen/codegen/models/__init__.py +3 -1
- package/generator/pygen/codegen/models/code_model.py +29 -9
- package/generator/pygen/codegen/models/enum_type.py +21 -9
- package/generator/pygen/codegen/models/list_type.py +1 -0
- package/generator/pygen/codegen/models/model_type.py +1 -3
- package/generator/pygen/codegen/models/operation.py +13 -3
- package/generator/pygen/codegen/models/parameter.py +3 -1
- package/generator/pygen/codegen/models/response.py +152 -8
- package/generator/pygen/codegen/models/utils.py +14 -0
- package/generator/pygen/codegen/serializers/__init__.py +13 -6
- package/generator/pygen/codegen/serializers/builder_serializer.py +219 -69
- package/generator/pygen/codegen/serializers/client_serializer.py +13 -6
- package/generator/pygen/codegen/serializers/general_serializer.py +4 -0
- package/generator/pygen/codegen/serializers/import_serializer.py +27 -0
- package/generator/pygen/codegen/serializers/model_serializer.py +4 -1
- package/generator/pygen/codegen/serializers/types_serializer.py +44 -12
- package/generator/pygen/codegen/templates/streaming_base.py.jinja2 +767 -0
- package/generator/pygen/preprocess/__init__.py +70 -17
- package/generator/pygen/utils.py +15 -0
- package/generator/pygen.egg-info/PKG-INFO +1 -1
- package/generator/pygen.egg-info/SOURCES.txt +1 -0
- package/generator/pygen.egg-info/requires.txt +1 -1
- package/generator/setup.py +1 -1
- package/package.json +33 -33
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
4
4
|
# license information.
|
|
5
5
|
# --------------------------------------------------------------------------
|
|
6
|
+
from dataclasses import dataclass
|
|
6
7
|
from typing import Optional, Any, TYPE_CHECKING, Union
|
|
7
8
|
|
|
8
9
|
from .base import BaseModel
|
|
@@ -17,6 +18,53 @@ from .combined_type import CombinedType
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from .code_model import CodeModel
|
|
19
20
|
|
|
21
|
+
DEFAULT_SSE_EVENT_TYPE = "message"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class StreamingEvent:
|
|
26
|
+
event_type: Optional[str]
|
|
27
|
+
payload_type: BaseType
|
|
28
|
+
payload_content_type: Optional[str]
|
|
29
|
+
is_terminal: bool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_streaming_event_discriminator(
|
|
33
|
+
events: list[StreamingEvent],
|
|
34
|
+
) -> Optional[tuple[str, list[tuple[str, StreamingEvent]]]]:
|
|
35
|
+
discriminator_name: Optional[str] = None
|
|
36
|
+
variants: list[tuple[str, StreamingEvent]] = []
|
|
37
|
+
values: set[str] = set()
|
|
38
|
+
for event in events:
|
|
39
|
+
discriminator_property = getattr(event.payload_type, "discriminator_property", None)
|
|
40
|
+
discriminator_value = getattr(event.payload_type, "discriminator_value", None)
|
|
41
|
+
if discriminator_property is None or discriminator_value is None:
|
|
42
|
+
return None
|
|
43
|
+
if discriminator_name is None:
|
|
44
|
+
discriminator_name = discriminator_property.wire_name
|
|
45
|
+
elif discriminator_property.wire_name != discriminator_name:
|
|
46
|
+
return None
|
|
47
|
+
if discriminator_value in values:
|
|
48
|
+
return None
|
|
49
|
+
values.add(discriminator_value)
|
|
50
|
+
variants.append((discriminator_value, event))
|
|
51
|
+
if discriminator_name is None:
|
|
52
|
+
return None
|
|
53
|
+
return discriminator_name, variants
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _get_terminal_event_names(events: list[StreamingEvent]) -> list[str]:
|
|
57
|
+
unnamed_events = [event for event in events if event.event_type is None]
|
|
58
|
+
terminal_event_names: list[str] = []
|
|
59
|
+
for event in events:
|
|
60
|
+
if not event.is_terminal:
|
|
61
|
+
continue
|
|
62
|
+
if event.event_type is not None:
|
|
63
|
+
terminal_event_names.append(event.event_type)
|
|
64
|
+
elif len(unnamed_events) == 1:
|
|
65
|
+
terminal_event_names.append(DEFAULT_SSE_EVENT_TYPE)
|
|
66
|
+
return list(dict.fromkeys(terminal_event_names))
|
|
67
|
+
|
|
20
68
|
|
|
21
69
|
class ResponseHeader(BaseModel):
|
|
22
70
|
def __init__(
|
|
@@ -58,6 +106,25 @@ class Response(BaseModel):
|
|
|
58
106
|
self.type = type
|
|
59
107
|
self.nullable = yaml_data.get("nullable")
|
|
60
108
|
self.default_content_type = yaml_data.get("defaultContentType")
|
|
109
|
+
streaming = yaml_data.get("streaming")
|
|
110
|
+
self.streaming_kind: Optional[str] = streaming["kind"] if streaming else None
|
|
111
|
+
self.streaming_events: list[StreamingEvent] = []
|
|
112
|
+
self.terminal_event: Optional[str] = streaming.get("terminalEvent") if streaming else None
|
|
113
|
+
# Named / model ``@terminalEvent`` events: deserialized and yielded like any other event,
|
|
114
|
+
# then iteration stops. The bare string-constant sentinel (``terminal_event``)
|
|
115
|
+
# stops WITHOUT yielding and is matched on event ``data`` instead of the event name.
|
|
116
|
+
self.terminal_event_names: list[str] = []
|
|
117
|
+
if streaming:
|
|
118
|
+
self.streaming_events = [
|
|
119
|
+
StreamingEvent(
|
|
120
|
+
event_type=event.get("eventType"),
|
|
121
|
+
payload_type=self.code_model.lookup_type(id(event["payloadType"])),
|
|
122
|
+
payload_content_type=event.get("payloadContentType"),
|
|
123
|
+
is_terminal=event.get("isTerminal", False),
|
|
124
|
+
)
|
|
125
|
+
for event in streaming.get("events", [])
|
|
126
|
+
]
|
|
127
|
+
self.terminal_event_names = _get_terminal_event_names(self.streaming_events)
|
|
61
128
|
|
|
62
129
|
@property
|
|
63
130
|
def result_property(self) -> str:
|
|
@@ -92,12 +159,48 @@ class Response(BaseModel):
|
|
|
92
159
|
)
|
|
93
160
|
return retval
|
|
94
161
|
|
|
162
|
+
@property
|
|
163
|
+
def is_structured_stream(self) -> bool:
|
|
164
|
+
"""Is the response a structured (JSONL / SSE) stream rendered as Stream[T] / AsyncStream[T]."""
|
|
165
|
+
return self.streaming_kind is not None
|
|
166
|
+
|
|
167
|
+
def stream_class_name(self, async_mode: bool) -> str:
|
|
168
|
+
return "AsyncStream" if async_mode else "Stream"
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def stream_item_type(self) -> Optional[BaseType]:
|
|
172
|
+
event_item_types = list(dict.fromkeys(event.payload_type for event in self.streaming_events))
|
|
173
|
+
if len(event_item_types) == 1:
|
|
174
|
+
return event_item_types[0]
|
|
175
|
+
if event_item_types:
|
|
176
|
+
return CombinedType({"type": "combined"}, self.code_model, event_item_types)
|
|
177
|
+
return self.type
|
|
178
|
+
|
|
95
179
|
def serialization_type(self, **kwargs: Any) -> str:
|
|
96
180
|
if self.type:
|
|
97
181
|
return self.type.serialization_type(**kwargs)
|
|
98
182
|
return "None"
|
|
99
183
|
|
|
184
|
+
def stream_item_annotation(self, **kwargs: Any) -> str:
|
|
185
|
+
"""Valid type expression for a structured stream's item type.
|
|
186
|
+
|
|
187
|
+
A named ``CombinedType`` (``@events`` union) renders its ``type_annotation`` as the
|
|
188
|
+
``_unions.<Name>`` alias, which is a module-level variable and therefore rejected by
|
|
189
|
+
pyright/mypy inside ``Stream[...]`` ("Variable not allowed in type expression"). Expand
|
|
190
|
+
the union inline (``Union[Model, ...]`` / the single member) so the annotation is a
|
|
191
|
+
valid type expression.
|
|
192
|
+
"""
|
|
193
|
+
item_type = self.stream_item_type
|
|
194
|
+
if isinstance(item_type, CombinedType):
|
|
195
|
+
return item_type.type_definition(**kwargs)
|
|
196
|
+
return item_type.type_annotation(**kwargs) if item_type else "None"
|
|
197
|
+
|
|
100
198
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
199
|
+
if self.is_structured_stream and self.type:
|
|
200
|
+
kwargs["is_operation_file"] = True
|
|
201
|
+
kwargs["is_response"] = True
|
|
202
|
+
stream_class = self.stream_class_name(kwargs.get("async_mode", False))
|
|
203
|
+
return f"{stream_class}[{self.stream_item_annotation(**kwargs)}]"
|
|
101
204
|
if self.type:
|
|
102
205
|
kwargs["is_operation_file"] = True
|
|
103
206
|
kwargs["is_response"] = True
|
|
@@ -109,30 +212,63 @@ class Response(BaseModel):
|
|
|
109
212
|
|
|
110
213
|
def docstring_text(self, **kwargs: Any) -> str:
|
|
111
214
|
kwargs["is_response"] = True
|
|
215
|
+
if self.is_structured_stream and self.type:
|
|
216
|
+
stream_class = self.stream_class_name(kwargs.get("async_mode", False))
|
|
217
|
+
item_type = self.stream_item_type or self.type
|
|
218
|
+
return f"An instance of {stream_class} that iterates over {item_type.docstring_text(**kwargs)}"
|
|
112
219
|
if self.nullable and self.type:
|
|
113
220
|
return f"{self.type.docstring_text(**kwargs)} or None"
|
|
114
221
|
return self.type.docstring_text(**kwargs) if self.type else "None"
|
|
115
222
|
|
|
116
223
|
def docstring_type(self, **kwargs: Any) -> str:
|
|
117
224
|
kwargs["is_response"] = True
|
|
225
|
+
if self.is_structured_stream and self.type:
|
|
226
|
+
stream_class = self.stream_class_name(kwargs.get("async_mode", False))
|
|
227
|
+
item_type = (self.stream_item_type or self.type).docstring_type(**kwargs)
|
|
228
|
+
return f"~{self.code_model.namespace}.{stream_class}[{item_type}]"
|
|
118
229
|
if self.nullable and self.type:
|
|
119
230
|
return f"{self.type.docstring_type(**kwargs)} or None"
|
|
120
231
|
return self.type.docstring_type(**kwargs) if self.type else "None"
|
|
121
232
|
|
|
122
233
|
def imports(self, **kwargs: Any) -> FileImport:
|
|
123
234
|
file_import = FileImport(self.code_model)
|
|
124
|
-
if self.type
|
|
125
|
-
|
|
235
|
+
item_type = self.stream_item_type if self.is_structured_stream else self.type
|
|
236
|
+
# For a structured stream whose item type is a named ``@events`` union, the annotation
|
|
237
|
+
# is expanded inline (see ``stream_item_annotation``), so import the union member types
|
|
238
|
+
# rather than the ``_unions`` alias.
|
|
239
|
+
if self.is_structured_stream and isinstance(item_type, CombinedType):
|
|
240
|
+
for member in item_type.types:
|
|
241
|
+
file_import.merge(member.imports(**kwargs))
|
|
242
|
+
# ``Union`` is only needed when the inline expansion actually yields a union of
|
|
243
|
+
# 2+ distinct member types (a single member collapses to that member; a union of
|
|
244
|
+
# only literals collapses to a single ``Literal[...]``).
|
|
245
|
+
distinct = list(dict.fromkeys(m.type_annotation(**kwargs) for m in item_type.types))
|
|
246
|
+
all_constant = all(t.type == "constant" for t in item_type.types)
|
|
247
|
+
if len(distinct) > 1 and not all_constant:
|
|
248
|
+
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB)
|
|
249
|
+
elif item_type:
|
|
250
|
+
file_import.merge(item_type.imports(**kwargs))
|
|
251
|
+
if not self.is_structured_stream and isinstance(item_type, CombinedType) and item_type.name:
|
|
252
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
253
|
+
file_import.add_submodule_import(
|
|
254
|
+
self.code_model.get_relative_import_path(serialize_namespace),
|
|
255
|
+
"_unions",
|
|
256
|
+
ImportType.LOCAL,
|
|
257
|
+
TypingSection.TYPING,
|
|
258
|
+
)
|
|
126
259
|
if self.nullable:
|
|
127
260
|
file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
|
|
128
|
-
if
|
|
261
|
+
if self.is_structured_stream:
|
|
262
|
+
stream_class = self.stream_class_name(kwargs.get("async_mode", False))
|
|
129
263
|
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"_unions",
|
|
133
|
-
ImportType.LOCAL,
|
|
134
|
-
TypingSection.TYPING,
|
|
264
|
+
relative_path = self.code_model.get_relative_import_path(
|
|
265
|
+
serialize_namespace, module_name="_utils.streaming_base"
|
|
135
266
|
)
|
|
267
|
+
file_import.add_submodule_import(relative_path, stream_class, ImportType.LOCAL)
|
|
268
|
+
if self.streaming_kind == "sse":
|
|
269
|
+
file_import.add_import("json", ImportType.STDLIB)
|
|
270
|
+
for event in self.streaming_events:
|
|
271
|
+
file_import.merge(event.payload_type.imports(**kwargs))
|
|
136
272
|
return file_import
|
|
137
273
|
|
|
138
274
|
def _get_import_type(self, input_path: str) -> ImportType:
|
|
@@ -143,6 +279,14 @@ class Response(BaseModel):
|
|
|
143
279
|
|
|
144
280
|
@classmethod
|
|
145
281
|
def from_yaml(cls, yaml_data: dict[str, Any], code_model: "CodeModel") -> "Response":
|
|
282
|
+
streaming = yaml_data.get("streaming")
|
|
283
|
+
if streaming:
|
|
284
|
+
return cls(
|
|
285
|
+
yaml_data=yaml_data,
|
|
286
|
+
code_model=code_model,
|
|
287
|
+
headers=[ResponseHeader.from_yaml(header, code_model) for header in yaml_data["headers"]],
|
|
288
|
+
type=code_model.lookup_type(id(streaming["itemType"])),
|
|
289
|
+
)
|
|
146
290
|
type = code_model.lookup_type(id(yaml_data["type"])) if yaml_data.get("type") else None
|
|
147
291
|
# use ByteIteratorType if we are returning a binary type
|
|
148
292
|
default_content_type = yaml_data.get("defaultContentType", "application/json")
|
|
@@ -34,6 +34,20 @@ def add_to_pylint_disable(curr_str: str, entry: str) -> str:
|
|
|
34
34
|
return f" # pylint: disable={entry}"
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def escape_sphinx_field_name(name: str) -> str:
|
|
38
|
+
"""Escape a name used as the target of a Sphinx info field (``:ivar``, ``:vartype``,
|
|
39
|
+
``:keyword``, ``:paramtype``, ``:param``, ``:type``).
|
|
40
|
+
|
|
41
|
+
A name containing ``@`` (e.g. a wire name such as ``@search.facets``) is wrapped in
|
|
42
|
+
double backticks so it is treated as an inline literal. This renders cleanly in Sphinx
|
|
43
|
+
without a leading ``@`` being misinterpreted, and — unlike a ``\\@`` escape — introduces
|
|
44
|
+
no invalid escape sequence into the generated Python docstring.
|
|
45
|
+
"""
|
|
46
|
+
if "@" in name:
|
|
47
|
+
return f"``{name}``"
|
|
48
|
+
return name
|
|
49
|
+
|
|
50
|
+
|
|
37
51
|
class NamespaceType(str, Enum):
|
|
38
52
|
"""Special signal for impports"""
|
|
39
53
|
|
|
@@ -207,10 +207,10 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
207
207
|
self._serialize_and_write_top_level_folder(env=env, namespace=client_namespace)
|
|
208
208
|
|
|
209
209
|
# add models folder if there are models in this namespace
|
|
210
|
-
is_typeddict_mode = self.code_model.
|
|
211
|
-
if (
|
|
212
|
-
self.code_model.
|
|
213
|
-
)
|
|
210
|
+
is_typeddict_mode = self.code_model.generate_typeddict_only
|
|
211
|
+
if (self.code_model.has_non_json_models(client_namespace_type.models) or client_namespace_type.enums) and (
|
|
212
|
+
self.code_model.options["models-mode"] or self.code_model.generate_typeddict_only
|
|
213
|
+
):
|
|
214
214
|
self._serialize_and_write_models_folder(
|
|
215
215
|
env=env,
|
|
216
216
|
namespace=client_namespace,
|
|
@@ -238,7 +238,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
238
238
|
).serialize(),
|
|
239
239
|
)
|
|
240
240
|
|
|
241
|
-
if not self.code_model.options["models-mode"]:
|
|
241
|
+
if not self.code_model.options["models-mode"] and not self.code_model.generate_typeddict_only:
|
|
242
242
|
# keep models file if users ended up just writing a models file
|
|
243
243
|
model_path = generation_path / Path("models.py")
|
|
244
244
|
if self.read_file(model_path):
|
|
@@ -321,7 +321,7 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
321
321
|
# Write the models folder
|
|
322
322
|
models_path = self.code_model.get_generation_dir(namespace) / "models"
|
|
323
323
|
models_mode = self.code_model.options["models-mode"]
|
|
324
|
-
if models_mode
|
|
324
|
+
if models_mode == "dpg" or self.code_model.generate_typeddict_only:
|
|
325
325
|
serializer = DpgModelSerializer
|
|
326
326
|
else:
|
|
327
327
|
serializer = MsrestModelSerializer
|
|
@@ -525,6 +525,13 @@ class JinjaSerializer(ReaderAndWriter):
|
|
|
525
525
|
general_serializer.serialize_model_base_file(),
|
|
526
526
|
)
|
|
527
527
|
|
|
528
|
+
# write _utils/streaming_base.py (vendored Stream/AsyncStream + JSONL/SSE decoders)
|
|
529
|
+
if self.code_model.has_structured_stream:
|
|
530
|
+
self.write_file(
|
|
531
|
+
utils_folder_path / Path("streaming_base.py"),
|
|
532
|
+
general_serializer.serialize_streaming_base_file(),
|
|
533
|
+
)
|
|
534
|
+
|
|
528
535
|
def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None:
|
|
529
536
|
root_dir = self.code_model.get_root_dir()
|
|
530
537
|
generation_dir = self.code_model.get_generation_dir(namespace)
|