pydantic-openapi-generator 2.4.0__tar.gz → 2.5.0__tar.gz
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.
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/PKG-INFO +1 -1
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/pyproject.toml +1 -1
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/client_generator.py +108 -3
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/async_client_httpx_pydantic_2.jinja2 +64 -22
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/sync_client_httpx_pydantic_2.jinja2 +64 -22
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/models.py +10 -1
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/.gitignore +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/LICENSE +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/README.md +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/__init__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/__main__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/common.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/__init__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/base_parameter.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/class_var_parameter.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/function_parameter.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/generator_config.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/kwarg_parameter.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/loader.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/parameter_source.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/parameter_union.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/config/utils.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/generate_data.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/__init__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/__init__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/common.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/exception_generator.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/generator.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/jinja_config.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/model_generator.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/alias_union.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/discriminator_enum.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/enum.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/http_exception.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/models.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/language_converters/python/templates/models_pydantic_2.jinja2 +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/parsers/__init__.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/parsers/openapi_30.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/parsers/openapi_31.py +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/py.typed +0 -0
- {pydantic_openapi_generator-2.4.0 → pydantic_openapi_generator-2.5.0}/src/pydantic_openapi_generator/version_detector.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pydantic-openapi-generator
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Openapi Python Generator
|
|
5
5
|
Project-URL: Homepage, https://github.com/mattcoulter7/pydantic-openapi-generator
|
|
6
6
|
Project-URL: Repository, https://github.com/mattcoulter7/pydantic-openapi-generator
|
|
@@ -58,6 +58,7 @@ from pydantic_openapi_generator.models import (
|
|
|
58
58
|
Model,
|
|
59
59
|
OpReturnType,
|
|
60
60
|
RequestBodyDefinition,
|
|
61
|
+
ResponseContentHandler,
|
|
61
62
|
ResponseVariant,
|
|
62
63
|
ServiceOperation,
|
|
63
64
|
TypeConversion,
|
|
@@ -154,6 +155,71 @@ def operation_is_sse(op: Operation) -> bool:
|
|
|
154
155
|
return False
|
|
155
156
|
|
|
156
157
|
|
|
158
|
+
def _sse_data_schema_from_media_type(media_type: Any) -> Any:
|
|
159
|
+
media_type_schema = getattr(media_type, "media_type_schema", None)
|
|
160
|
+
if media_type_schema is None:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
if is_reference_type(media_type_schema):
|
|
164
|
+
return media_type_schema
|
|
165
|
+
|
|
166
|
+
if not is_schema_type(media_type_schema):
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
properties = getattr(media_type_schema, "properties", None)
|
|
170
|
+
if isinstance(properties, dict) and "data" in properties:
|
|
171
|
+
return properties["data"]
|
|
172
|
+
|
|
173
|
+
schema_type = getattr(media_type_schema, "type", None)
|
|
174
|
+
if schema_type != "object" and str(schema_type) != "DataType.OBJECT":
|
|
175
|
+
return media_type_schema
|
|
176
|
+
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def generate_sse_data_handler(operation: Operation) -> Optional[ResponseContentHandler]:
|
|
181
|
+
if not getattr(operation, "responses", None):
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
for status_code, response in operation.responses.items():
|
|
185
|
+
try:
|
|
186
|
+
if not str(status_code).startswith("2"):
|
|
187
|
+
continue
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger = logging.getLogger(__name__)
|
|
190
|
+
logger.debug("Skipping response status key; conversion failed", exc_info=e)
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
if not is_response_type(response):
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
content = getattr(response, "content", None)
|
|
197
|
+
if not isinstance(content, dict):
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
media_type = content.get("text/event-stream")
|
|
201
|
+
if not is_media_type(media_type):
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
data_schema = _sse_data_schema_from_media_type(media_type)
|
|
205
|
+
if data_schema is None:
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
variant = _response_variant_from_schema(int(status_code), "application/json", data_schema)
|
|
209
|
+
if variant.type is None or variant.body_kind == "empty":
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
return ResponseContentHandler(
|
|
213
|
+
content_type="text/event-stream",
|
|
214
|
+
type=variant.type,
|
|
215
|
+
complex_type=variant.complex_type,
|
|
216
|
+
list_type=variant.list_type,
|
|
217
|
+
body_kind=variant.body_kind, # type: ignore[arg-type]
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
|
|
157
223
|
HTTP_OPERATIONS = ["get", "post", "put", "delete", "options", "head", "patch", "trace"]
|
|
158
224
|
|
|
159
225
|
|
|
@@ -620,9 +686,47 @@ def _return_type_hint(variants: List[ResponseVariant]) -> str:
|
|
|
620
686
|
return hints[0] if len(hints) == 1 else " | ".join(hints)
|
|
621
687
|
|
|
622
688
|
|
|
689
|
+
def _variant_deserialization_key(variant: ResponseVariant) -> tuple:
|
|
690
|
+
return (
|
|
691
|
+
variant.body_kind,
|
|
692
|
+
variant.type.converted_type if variant.type is not None else None,
|
|
693
|
+
variant.complex_type,
|
|
694
|
+
variant.list_type,
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def _unambiguous_content_handlers(
|
|
699
|
+
variants: List[ResponseVariant],
|
|
700
|
+
) -> List[ResponseContentHandler]:
|
|
701
|
+
variants_by_content_type: Dict[str, List[ResponseVariant]] = {}
|
|
702
|
+
for variant in variants:
|
|
703
|
+
if variant.content_type is None or variant.body_kind == "empty":
|
|
704
|
+
continue
|
|
705
|
+
variants_by_content_type.setdefault(variant.content_type.lower(), []).append(variant)
|
|
706
|
+
|
|
707
|
+
handlers: List[ResponseContentHandler] = []
|
|
708
|
+
for content_type, content_variants in variants_by_content_type.items():
|
|
709
|
+
keys = {_variant_deserialization_key(variant) for variant in content_variants}
|
|
710
|
+
if len(keys) != 1:
|
|
711
|
+
continue
|
|
712
|
+
|
|
713
|
+
variant = content_variants[0]
|
|
714
|
+
handlers.append(
|
|
715
|
+
ResponseContentHandler(
|
|
716
|
+
content_type=content_type,
|
|
717
|
+
type=variant.type,
|
|
718
|
+
complex_type=variant.complex_type,
|
|
719
|
+
list_type=variant.list_type,
|
|
720
|
+
body_kind=variant.body_kind, # type: ignore[arg-type]
|
|
721
|
+
)
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
return handlers
|
|
725
|
+
|
|
726
|
+
|
|
623
727
|
def generate_return_type(operation: Operation) -> OpReturnType:
|
|
624
728
|
if operation.responses is None:
|
|
625
|
-
return OpReturnType(type=None, status_code=200, complex_type=False
|
|
729
|
+
return OpReturnType(type=None, status_code=200, complex_type=False)
|
|
626
730
|
|
|
627
731
|
good_responses: List[Tuple[int, Union[Response, Reference]]] = [
|
|
628
732
|
(int(status_code), response)
|
|
@@ -630,7 +734,7 @@ def generate_return_type(operation: Operation) -> OpReturnType:
|
|
|
630
734
|
if status_code.startswith("2")
|
|
631
735
|
]
|
|
632
736
|
if len(good_responses) == 0:
|
|
633
|
-
return OpReturnType(type=None, status_code=200, complex_type=False
|
|
737
|
+
return OpReturnType(type=None, status_code=200, complex_type=False)
|
|
634
738
|
|
|
635
739
|
variants: List[ResponseVariant] = []
|
|
636
740
|
for status_code, response in good_responses:
|
|
@@ -643,8 +747,8 @@ def generate_return_type(operation: Operation) -> OpReturnType:
|
|
|
643
747
|
complex_type=first_variant.complex_type,
|
|
644
748
|
list_type=first_variant.list_type,
|
|
645
749
|
variants=variants,
|
|
646
|
-
accepted_status_codes=sorted({status_code for status_code, _ in good_responses}),
|
|
647
750
|
accept_content_types=list(dict.fromkeys(v.content_type for v in variants if v.content_type is not None)),
|
|
751
|
+
unambiguous_content_handlers=_unambiguous_content_handlers(variants),
|
|
648
752
|
return_type_hint=_return_type_hint(variants),
|
|
649
753
|
)
|
|
650
754
|
|
|
@@ -732,6 +836,7 @@ def generate_clients(
|
|
|
732
836
|
path_name=path_name,
|
|
733
837
|
method=http_operation,
|
|
734
838
|
is_sse=operation_is_sse(op),
|
|
839
|
+
sse_data_handler=generate_sse_data_handler(op),
|
|
735
840
|
use_orjson=common.get_use_orjson(),
|
|
736
841
|
)
|
|
737
842
|
|
|
@@ -10,7 +10,6 @@ import httpx
|
|
|
10
10
|
from pydantic import BaseModel, SecretStr, TypeAdapter
|
|
11
11
|
|
|
12
12
|
from ..models import *
|
|
13
|
-
from ..exceptions import HTTPException
|
|
14
13
|
|
|
15
14
|
{% set _base_url = servers[0].url if servers|length > 0 else "/" %}
|
|
16
15
|
|
|
@@ -54,7 +53,7 @@ class AsyncClient(BaseModel):
|
|
|
54
53
|
{% endfor %}
|
|
55
54
|
{% for op in operations %}
|
|
56
55
|
async def {{ op.operation_id }}(self{% if op.params %}, {{ op.params }}{% endif %}) -> {%- if op.is_sse -%}
|
|
57
|
-
AsyncGenerator[str | dict[str, Any], None]
|
|
56
|
+
AsyncGenerator[str | {% if op.sse_data_handler and op.sse_data_handler.type %}{% if op.sse_data_handler.list_type %}list[{{ op.sse_data_handler.list_type }}]{% else %}{{ op.sse_data_handler.type.converted_type }}{% endif %}{% else %}dict[str, Any]{% endif %}, None]
|
|
58
57
|
{%- else -%}
|
|
59
58
|
{{ op.return_type.return_type_hint or "None" }}
|
|
60
59
|
{%- endif -%}:
|
|
@@ -121,11 +120,7 @@ AsyncGenerator[str | dict[str, Any], None]
|
|
|
121
120
|
{% endif %}
|
|
122
121
|
{% endif %}
|
|
123
122
|
) as response:
|
|
124
|
-
|
|
125
|
-
raise HTTPException(
|
|
126
|
-
response.status_code,
|
|
127
|
-
f"{{ op.operation_id }} failed with status code: {response.status_code}",
|
|
128
|
-
)
|
|
123
|
+
response.raise_for_status()
|
|
129
124
|
|
|
130
125
|
async for line in response.aiter_lines():
|
|
131
126
|
if not line:
|
|
@@ -137,11 +132,16 @@ AsyncGenerator[str | dict[str, Any], None]
|
|
|
137
132
|
if payload == "[DONE]":
|
|
138
133
|
break
|
|
139
134
|
try:
|
|
135
|
+
{% if op.sse_data_handler and op.sse_data_handler.type %}
|
|
136
|
+
obj = json.loads(payload)
|
|
137
|
+
yield TypeAdapter({% if op.sse_data_handler.list_type %}list[{{ op.sse_data_handler.list_type }}]{% else %}{{ op.sse_data_handler.type.converted_type }}{% endif %}).validate_python(obj)
|
|
138
|
+
{% else %}
|
|
140
139
|
obj = json.loads(payload)
|
|
141
140
|
if isinstance(obj, dict):
|
|
142
141
|
yield obj
|
|
143
142
|
else:
|
|
144
143
|
yield payload
|
|
144
|
+
{% endif %}
|
|
145
145
|
except Exception:
|
|
146
146
|
yield payload
|
|
147
147
|
else:
|
|
@@ -169,23 +169,60 @@ AsyncGenerator[str | dict[str, Any], None]
|
|
|
169
169
|
{% endif %}
|
|
170
170
|
)
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
response.raise_for_status()
|
|
173
|
+
|
|
174
|
+
{% set empty_variants = op.return_type.variants | selectattr("body_kind", "equalto", "empty") | list %}
|
|
175
|
+
{% if empty_variants|length > 0 %}
|
|
176
|
+
if not response.content:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
{% endif %}
|
|
180
|
+
{% if op.return_type.unambiguous_content_handlers|length == 1 %}
|
|
181
|
+
{% set handler = op.return_type.unambiguous_content_handlers[0] %}
|
|
182
|
+
{% if handler.body_kind == "binary" %}
|
|
183
|
+
return response.content
|
|
184
|
+
{% elif handler.body_kind == "text" %}
|
|
185
|
+
return response.text
|
|
186
|
+
{% elif handler.type is not none and handler.type.converted_type is not none %}
|
|
187
|
+
{% if handler.list_type is none %}
|
|
188
|
+
return TypeAdapter({{ handler.type.converted_type }}).validate_python(response.json())
|
|
189
|
+
{% else %}
|
|
190
|
+
return TypeAdapter(list[{{ handler.list_type }}]).validate_python(response.json())
|
|
191
|
+
{% endif %}
|
|
192
|
+
{% else %}
|
|
193
|
+
return response.json()
|
|
194
|
+
{% endif %}
|
|
195
|
+
{% else %}
|
|
196
|
+
if not response.content:
|
|
197
|
+
return None
|
|
177
198
|
|
|
178
199
|
_response_content_type = response.headers.get("content-type", "").split(";", 1)[0].lower()
|
|
200
|
+
{% set unambiguous_content_types = op.return_type.unambiguous_content_handlers | map(attribute="content_type") | list %}
|
|
201
|
+
{% for handler in op.return_type.unambiguous_content_handlers %}
|
|
202
|
+
if _response_content_type == "{{ handler.content_type }}":
|
|
203
|
+
{% if handler.body_kind == "binary" %}
|
|
204
|
+
return response.content
|
|
205
|
+
{% elif handler.body_kind == "text" %}
|
|
206
|
+
return response.text
|
|
207
|
+
{% elif handler.type is not none and handler.type.converted_type is not none %}
|
|
208
|
+
{% if handler.list_type is none %}
|
|
209
|
+
return TypeAdapter({{ handler.type.converted_type }}).validate_python(response.json())
|
|
210
|
+
{% else %}
|
|
211
|
+
return TypeAdapter(list[{{ handler.list_type }}]).validate_python(response.json())
|
|
212
|
+
{% endif %}
|
|
213
|
+
{% else %}
|
|
214
|
+
return response.json()
|
|
215
|
+
{% endif %}
|
|
216
|
+
|
|
217
|
+
{% endfor %}
|
|
179
218
|
{% for variant in op.return_type.variants %}
|
|
180
|
-
{%
|
|
181
|
-
if response.status_code == {{ variant.status_code }}
|
|
182
|
-
{% if variant.body_kind == "
|
|
183
|
-
return None
|
|
184
|
-
{% elif variant.body_kind == "binary" %}
|
|
219
|
+
{% if variant.content_type and variant.body_kind != "empty" and variant.content_type not in unambiguous_content_types %}
|
|
220
|
+
if response.status_code == {{ variant.status_code }} and _response_content_type == "{{ variant.content_type }}":
|
|
221
|
+
{% if variant.body_kind == "binary" %}
|
|
185
222
|
return response.content
|
|
186
223
|
{% elif variant.body_kind == "text" %}
|
|
187
224
|
return response.text
|
|
188
|
-
{% elif variant.
|
|
225
|
+
{% elif variant.type is not none and variant.type.converted_type is not none %}
|
|
189
226
|
{% if variant.list_type is none %}
|
|
190
227
|
return TypeAdapter({{ variant.type.converted_type }}).validate_python(response.json())
|
|
191
228
|
{% else %}
|
|
@@ -195,11 +232,16 @@ AsyncGenerator[str | dict[str, Any], None]
|
|
|
195
232
|
return response.json()
|
|
196
233
|
{% endif %}
|
|
197
234
|
|
|
235
|
+
{% endif %}
|
|
198
236
|
{% endfor %}
|
|
199
|
-
|
|
200
|
-
response.
|
|
201
|
-
|
|
202
|
-
)
|
|
237
|
+
if _response_content_type == "application/json" or _response_content_type.endswith("+json"):
|
|
238
|
+
return response.json()
|
|
239
|
+
|
|
240
|
+
if _response_content_type.startswith("text/"):
|
|
241
|
+
return response.text
|
|
242
|
+
|
|
243
|
+
return response.content
|
|
244
|
+
{% endif %}
|
|
203
245
|
{% endif %}
|
|
204
246
|
|
|
205
247
|
{% endfor %}
|
|
@@ -10,7 +10,6 @@ import httpx
|
|
|
10
10
|
from pydantic import BaseModel, SecretStr, TypeAdapter
|
|
11
11
|
|
|
12
12
|
from ..models import *
|
|
13
|
-
from ..exceptions import HTTPException
|
|
14
13
|
|
|
15
14
|
{% set _base_url = servers[0].url if servers|length > 0 else "/" %}
|
|
16
15
|
|
|
@@ -54,7 +53,7 @@ class SyncClient(BaseModel):
|
|
|
54
53
|
{% endfor %}
|
|
55
54
|
{% for op in operations %}
|
|
56
55
|
def {{ op.operation_id }}(self{% if op.params %}, {{ op.params }}{% endif %}) -> {%- if op.is_sse -%}
|
|
57
|
-
Generator[str | dict[str, Any], None, None]
|
|
56
|
+
Generator[str | {% if op.sse_data_handler and op.sse_data_handler.type %}{% if op.sse_data_handler.list_type %}list[{{ op.sse_data_handler.list_type }}]{% else %}{{ op.sse_data_handler.type.converted_type }}{% endif %}{% else %}dict[str, Any]{% endif %}, None, None]
|
|
58
57
|
{%- else -%}
|
|
59
58
|
{{ op.return_type.return_type_hint or "None" }}
|
|
60
59
|
{%- endif -%}:
|
|
@@ -121,11 +120,7 @@ Generator[str | dict[str, Any], None, None]
|
|
|
121
120
|
{% endif %}
|
|
122
121
|
{% endif %}
|
|
123
122
|
) as response:
|
|
124
|
-
|
|
125
|
-
raise HTTPException(
|
|
126
|
-
response.status_code,
|
|
127
|
-
f"{{ op.operation_id }} failed with status code: {response.status_code}",
|
|
128
|
-
)
|
|
123
|
+
response.raise_for_status()
|
|
129
124
|
|
|
130
125
|
for line in response.iter_lines():
|
|
131
126
|
if not line:
|
|
@@ -137,11 +132,16 @@ Generator[str | dict[str, Any], None, None]
|
|
|
137
132
|
if payload == "[DONE]":
|
|
138
133
|
break
|
|
139
134
|
try:
|
|
135
|
+
{% if op.sse_data_handler and op.sse_data_handler.type %}
|
|
136
|
+
obj = json.loads(payload)
|
|
137
|
+
yield TypeAdapter({% if op.sse_data_handler.list_type %}list[{{ op.sse_data_handler.list_type }}]{% else %}{{ op.sse_data_handler.type.converted_type }}{% endif %}).validate_python(obj)
|
|
138
|
+
{% else %}
|
|
140
139
|
obj = json.loads(payload)
|
|
141
140
|
if isinstance(obj, dict):
|
|
142
141
|
yield obj
|
|
143
142
|
else:
|
|
144
143
|
yield payload
|
|
144
|
+
{% endif %}
|
|
145
145
|
except Exception:
|
|
146
146
|
yield payload
|
|
147
147
|
else:
|
|
@@ -168,23 +168,60 @@ Generator[str | dict[str, Any], None, None]
|
|
|
168
168
|
{% endif %}
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
response.raise_for_status()
|
|
172
|
+
|
|
173
|
+
{% set empty_variants = op.return_type.variants | selectattr("body_kind", "equalto", "empty") | list %}
|
|
174
|
+
{% if empty_variants|length > 0 %}
|
|
175
|
+
if not response.content:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
{% endif %}
|
|
179
|
+
{% if op.return_type.unambiguous_content_handlers|length == 1 %}
|
|
180
|
+
{% set handler = op.return_type.unambiguous_content_handlers[0] %}
|
|
181
|
+
{% if handler.body_kind == "binary" %}
|
|
182
|
+
return response.content
|
|
183
|
+
{% elif handler.body_kind == "text" %}
|
|
184
|
+
return response.text
|
|
185
|
+
{% elif handler.type is not none and handler.type.converted_type is not none %}
|
|
186
|
+
{% if handler.list_type is none %}
|
|
187
|
+
return TypeAdapter({{ handler.type.converted_type }}).validate_python(response.json())
|
|
188
|
+
{% else %}
|
|
189
|
+
return TypeAdapter(list[{{ handler.list_type }}]).validate_python(response.json())
|
|
190
|
+
{% endif %}
|
|
191
|
+
{% else %}
|
|
192
|
+
return response.json()
|
|
193
|
+
{% endif %}
|
|
194
|
+
{% else %}
|
|
195
|
+
if not response.content:
|
|
196
|
+
return None
|
|
176
197
|
|
|
177
198
|
_response_content_type = response.headers.get("content-type", "").split(";", 1)[0].lower()
|
|
199
|
+
{% set unambiguous_content_types = op.return_type.unambiguous_content_handlers | map(attribute="content_type") | list %}
|
|
200
|
+
{% for handler in op.return_type.unambiguous_content_handlers %}
|
|
201
|
+
if _response_content_type == "{{ handler.content_type }}":
|
|
202
|
+
{% if handler.body_kind == "binary" %}
|
|
203
|
+
return response.content
|
|
204
|
+
{% elif handler.body_kind == "text" %}
|
|
205
|
+
return response.text
|
|
206
|
+
{% elif handler.type is not none and handler.type.converted_type is not none %}
|
|
207
|
+
{% if handler.list_type is none %}
|
|
208
|
+
return TypeAdapter({{ handler.type.converted_type }}).validate_python(response.json())
|
|
209
|
+
{% else %}
|
|
210
|
+
return TypeAdapter(list[{{ handler.list_type }}]).validate_python(response.json())
|
|
211
|
+
{% endif %}
|
|
212
|
+
{% else %}
|
|
213
|
+
return response.json()
|
|
214
|
+
{% endif %}
|
|
215
|
+
|
|
216
|
+
{% endfor %}
|
|
178
217
|
{% for variant in op.return_type.variants %}
|
|
179
|
-
{%
|
|
180
|
-
if response.status_code == {{ variant.status_code }}
|
|
181
|
-
{% if variant.body_kind == "
|
|
182
|
-
return None
|
|
183
|
-
{% elif variant.body_kind == "binary" %}
|
|
218
|
+
{% if variant.content_type and variant.body_kind != "empty" and variant.content_type not in unambiguous_content_types %}
|
|
219
|
+
if response.status_code == {{ variant.status_code }} and _response_content_type == "{{ variant.content_type }}":
|
|
220
|
+
{% if variant.body_kind == "binary" %}
|
|
184
221
|
return response.content
|
|
185
222
|
{% elif variant.body_kind == "text" %}
|
|
186
223
|
return response.text
|
|
187
|
-
{% elif variant.
|
|
224
|
+
{% elif variant.type is not none and variant.type.converted_type is not none %}
|
|
188
225
|
{% if variant.list_type is none %}
|
|
189
226
|
return TypeAdapter({{ variant.type.converted_type }}).validate_python(response.json())
|
|
190
227
|
{% else %}
|
|
@@ -194,11 +231,16 @@ Generator[str | dict[str, Any], None, None]
|
|
|
194
231
|
return response.json()
|
|
195
232
|
{% endif %}
|
|
196
233
|
|
|
234
|
+
{% endif %}
|
|
197
235
|
{% endfor %}
|
|
198
|
-
|
|
199
|
-
response.
|
|
200
|
-
|
|
201
|
-
)
|
|
236
|
+
if _response_content_type == "application/json" or _response_content_type.endswith("+json"):
|
|
237
|
+
return response.json()
|
|
238
|
+
|
|
239
|
+
if _response_content_type.startswith("text/"):
|
|
240
|
+
return response.text
|
|
241
|
+
|
|
242
|
+
return response.content
|
|
243
|
+
{% endif %}
|
|
202
244
|
{% endif %}
|
|
203
245
|
|
|
204
246
|
{% endfor %}
|
|
@@ -48,8 +48,8 @@ class OpReturnType(BaseModel):
|
|
|
48
48
|
complex_type: bool = False
|
|
49
49
|
list_type: Optional[str] = None
|
|
50
50
|
variants: List["ResponseVariant"] = Field(default_factory=list)
|
|
51
|
-
accepted_status_codes: List[int] = Field(default_factory=list)
|
|
52
51
|
accept_content_types: List[str] = Field(default_factory=list)
|
|
52
|
+
unambiguous_content_handlers: List["ResponseContentHandler"] = Field(default_factory=list)
|
|
53
53
|
return_type_hint: Optional[str] = None
|
|
54
54
|
|
|
55
55
|
|
|
@@ -62,6 +62,14 @@ class ResponseVariant(BaseModel):
|
|
|
62
62
|
body_kind: Literal["empty", "json", "text", "binary"] = "empty"
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class ResponseContentHandler(BaseModel):
|
|
66
|
+
content_type: str
|
|
67
|
+
type: Optional[TypeConversion] = None
|
|
68
|
+
complex_type: bool = False
|
|
69
|
+
list_type: Optional[str] = None
|
|
70
|
+
body_kind: Literal["json", "text", "binary"]
|
|
71
|
+
|
|
72
|
+
|
|
65
73
|
class RequestBodyDefinition(BaseModel):
|
|
66
74
|
content_type: Optional[str] = None
|
|
67
75
|
encoding: Literal["json", "form", "multipart", "binary", "text"]
|
|
@@ -102,6 +110,7 @@ class ServiceOperation(BaseModel):
|
|
|
102
110
|
request_body: Optional[RequestBodyDefinition] = None
|
|
103
111
|
method: str
|
|
104
112
|
is_sse: bool = False
|
|
113
|
+
sse_data_handler: Optional[ResponseContentHandler] = None
|
|
105
114
|
use_orjson: bool = False
|
|
106
115
|
|
|
107
116
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|