cliV1 0.0.6__py3-none-any.whl

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 (92) hide show
  1. cliv1-0.0.6.dist-info/METADATA +212 -0
  2. cliv1-0.0.6.dist-info/RECORD +92 -0
  3. cliv1-0.0.6.dist-info/WHEEL +4 -0
  4. cliv1-0.0.6.dist-info/licenses/LICENSE +21 -0
  5. swagger_petstore_open_api_3_1_0/__init__.py +15 -0
  6. swagger_petstore_open_api_3_1_0/apis/__init__.py +5 -0
  7. swagger_petstore_open_api_3_1_0/apis/pet_api.py +922 -0
  8. swagger_petstore_open_api_3_1_0/apis/store.py +432 -0
  9. swagger_petstore_open_api_3_1_0/apis/user_api.py +829 -0
  10. swagger_petstore_open_api_3_1_0/async_client.py +87 -0
  11. swagger_petstore_open_api_3_1_0/auth.py +17 -0
  12. swagger_petstore_open_api_3_1_0/base_client.py +27 -0
  13. swagger_petstore_open_api_3_1_0/client.py +85 -0
  14. swagger_petstore_open_api_3_1_0/core/__init__.py +395 -0
  15. swagger_petstore_open_api_3_1_0/core/_internal/__init__.py +9 -0
  16. swagger_petstore_open_api_3_1_0/core/_internal/content_disposition.py +154 -0
  17. swagger_petstore_open_api_3_1_0/core/_internal/flattening.py +104 -0
  18. swagger_petstore_open_api_3_1_0/core/_internal/headers.py +243 -0
  19. swagger_petstore_open_api_3_1_0/core/_internal/redaction.py +208 -0
  20. swagger_petstore_open_api_3_1_0/core/_internal/sse_parser.py +124 -0
  21. swagger_petstore_open_api_3_1_0/core/_internal/subscripts.py +50 -0
  22. swagger_petstore_open_api_3_1_0/core/_internal/urls.py +172 -0
  23. swagger_petstore_open_api_3_1_0/core/_internal/wire.py +201 -0
  24. swagger_petstore_open_api_3_1_0/core/adapters.py +177 -0
  25. swagger_petstore_open_api_3_1_0/core/auth/__init__.py +70 -0
  26. swagger_petstore_open_api_3_1_0/core/auth/composition.py +175 -0
  27. swagger_petstore_open_api_3_1_0/core/auth/models.py +216 -0
  28. swagger_petstore_open_api_3_1_0/core/auth/protocols.py +103 -0
  29. swagger_petstore_open_api_3_1_0/core/auth/schemes/__init__.py +94 -0
  30. swagger_petstore_open_api_3_1_0/core/auth/schemes/api_key_auth.py +53 -0
  31. swagger_petstore_open_api_3_1_0/core/auth/schemes/basic_auth.py +73 -0
  32. swagger_petstore_open_api_3_1_0/core/auth/schemes/bearer_auth.py +21 -0
  33. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_authorization_code.py +395 -0
  34. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_client_credentials.py +130 -0
  35. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_password.py +145 -0
  36. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_schemes.py +300 -0
  37. swagger_petstore_open_api_3_1_0/core/base_raw_response.py +49 -0
  38. swagger_petstore_open_api_3_1_0/core/bodies.py +590 -0
  39. swagger_petstore_open_api_3_1_0/core/converters/__init__.py +68 -0
  40. swagger_petstore_open_api_3_1_0/core/converters/date_time.py +237 -0
  41. swagger_petstore_open_api_3_1_0/core/converters/encoded_bytes.py +273 -0
  42. swagger_petstore_open_api_3_1_0/core/converters/open_enum.py +35 -0
  43. swagger_petstore_open_api_3_1_0/core/decoding.py +498 -0
  44. swagger_petstore_open_api_3_1_0/core/discriminators.py +129 -0
  45. swagger_petstore_open_api_3_1_0/core/event_streams.py +225 -0
  46. swagger_petstore_open_api_3_1_0/core/exceptions.py +78 -0
  47. swagger_petstore_open_api_3_1_0/core/file_responses.py +337 -0
  48. swagger_petstore_open_api_3_1_0/core/files.py +174 -0
  49. swagger_petstore_open_api_3_1_0/core/httpx2_transport.py +748 -0
  50. swagger_petstore_open_api_3_1_0/core/logging.py +472 -0
  51. swagger_petstore_open_api_3_1_0/core/models.py +143 -0
  52. swagger_petstore_open_api_3_1_0/core/optionality.py +144 -0
  53. swagger_petstore_open_api_3_1_0/core/pagination.py +452 -0
  54. swagger_petstore_open_api_3_1_0/core/paginator.py +55 -0
  55. swagger_petstore_open_api_3_1_0/core/params.py +157 -0
  56. swagger_petstore_open_api_3_1_0/core/raw_client.py +480 -0
  57. swagger_petstore_open_api_3_1_0/core/request_options.py +172 -0
  58. swagger_petstore_open_api_3_1_0/core/results.py +178 -0
  59. swagger_petstore_open_api_3_1_0/core/retries.py +237 -0
  60. swagger_petstore_open_api_3_1_0/core/runtime_env.py +43 -0
  61. swagger_petstore_open_api_3_1_0/core/servers.py +33 -0
  62. swagger_petstore_open_api_3_1_0/core/transport.py +218 -0
  63. swagger_petstore_open_api_3_1_0/errors/__init__.py +48 -0
  64. swagger_petstore_open_api_3_1_0/errors/add_pet_error.py +21 -0
  65. swagger_petstore_open_api_3_1_0/errors/delete_order_error.py +21 -0
  66. swagger_petstore_open_api_3_1_0/errors/delete_pet_error.py +21 -0
  67. swagger_petstore_open_api_3_1_0/errors/delete_user_error.py +21 -0
  68. swagger_petstore_open_api_3_1_0/errors/find_pets_by_status_error.py +21 -0
  69. swagger_petstore_open_api_3_1_0/errors/find_pets_by_tags_error.py +21 -0
  70. swagger_petstore_open_api_3_1_0/errors/get_order_by_id_error.py +21 -0
  71. swagger_petstore_open_api_3_1_0/errors/get_pet_by_id_error.py +21 -0
  72. swagger_petstore_open_api_3_1_0/errors/get_user_by_name_error.py +21 -0
  73. swagger_petstore_open_api_3_1_0/errors/login_user_error.py +21 -0
  74. swagger_petstore_open_api_3_1_0/errors/place_order_error.py +21 -0
  75. swagger_petstore_open_api_3_1_0/errors/update_pet_error.py +21 -0
  76. swagger_petstore_open_api_3_1_0/errors/update_pet_with_form_error.py +21 -0
  77. swagger_petstore_open_api_3_1_0/errors/update_user_error.py +21 -0
  78. swagger_petstore_open_api_3_1_0/errors/upload_file_error.py +21 -0
  79. swagger_petstore_open_api_3_1_0/models/__init__.py +23 -0
  80. swagger_petstore_open_api_3_1_0/models/api_response.py +18 -0
  81. swagger_petstore_open_api_3_1_0/models/category.py +15 -0
  82. swagger_petstore_open_api_3_1_0/models/enums/__init__.py +4 -0
  83. swagger_petstore_open_api_3_1_0/models/enums/order_status.py +17 -0
  84. swagger_petstore_open_api_3_1_0/models/enums/pet_status.py +17 -0
  85. swagger_petstore_open_api_3_1_0/models/order.py +27 -0
  86. swagger_petstore_open_api_3_1_0/models/pet.py +28 -0
  87. swagger_petstore_open_api_3_1_0/models/tag_model.py +15 -0
  88. swagger_petstore_open_api_3_1_0/models/user.py +29 -0
  89. swagger_petstore_open_api_3_1_0/py.typed +0 -0
  90. swagger_petstore_open_api_3_1_0/server/__init__.py +21 -0
  91. swagger_petstore_open_api_3_1_0/server/server.py +17 -0
  92. swagger_petstore_open_api_3_1_0/server/server_config.py +52 -0
@@ -0,0 +1,498 @@
1
+ """Turning wire bytes into typed values: response decoding and error-body mapping.
2
+
3
+ One verb per act, and they are not interchangeable. ``decode`` builds a 2xx payload from a response
4
+ whose body has not been read; ``parse`` is the step inside it, bytes to an untyped Python value for
5
+ the adapter to validate; ``map`` is the error path's, because there the work is selection -- an
6
+ :class:`ErrorMapper` picks the documented schema for a response's status.
7
+
8
+ Every decoder is handed the same unread response and owns it from there: it either reads the body to
9
+ the end and releases the connection, or hands the open connection to the value it returns. There is
10
+ no third option, which is what lets the raw client own no connection and take no branch.
11
+
12
+ The file reads in that order -- the three contracts, then the buffered arm, the error side, the file
13
+ arm, the event-stream arm. Within an arm, sync precedes async, a private step sits immediately above
14
+ what holds it, and an arm's singletons come after the last class they need."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from collections.abc import Callable
19
+ from dataclasses import dataclass
20
+ from typing import Any, Final, Generic, Protocol, TypeVar
21
+
22
+ from pydantic import TypeAdapter
23
+ from typing_extensions import TypeForm
24
+
25
+ from ._internal.subscripts import SubscriptOnly
26
+ from ._internal.wire import parse_json
27
+ from .adapters import adapter_for
28
+ from .event_streams import AsyncEventStream, EventStream
29
+ from .file_responses import AsyncFileResponse, FileResponse
30
+ from .results import RawError
31
+ from .transport import AsyncHttpResponse, HttpResponse
32
+
33
+ T = TypeVar("T")
34
+ T_co = TypeVar("T_co", covariant=True)
35
+ E_co = TypeVar("E_co", covariant=True)
36
+
37
+
38
+ class ResponseDecoder(Protocol[T_co]):
39
+ """Builds a 2xx payload from a response whose body has not been read.
40
+
41
+ The decoder owns that body. It either reads it to the end and releases the connection -- what
42
+ every model, text and no-content decoder does, through :class:`BufferedDecoder` -- or hands the
43
+ still-open connection to the value it returns, which becomes responsible for releasing it
44
+ (:class:`FileResponse`). There is no third option: a 2xx response leaves ``execute`` owned by
45
+ exactly one object, and a decoder that neither read nor handed over would strand a pool
46
+ connection."""
47
+
48
+ def decode(self, response: HttpResponse) -> T_co: ...
49
+
50
+
51
+ class AsyncResponseDecoder(Protocol[T_co]):
52
+ """The twin of :class:`ResponseDecoder`, over the async response.
53
+
54
+ ``decode`` is an ``async def`` here rather than returning ``T | Awaitable[T]``. That union
55
+ spelling exists so one *static* auth scheme can satisfy both flavours; nothing about reading a
56
+ body is static, so there is nothing for it to buy -- the same reasoning that settled
57
+ :meth:`AsyncTokenSource.fetch`. The cost is an ``await`` on :data:`async_file_decoder` that
58
+ never suspends, which is the honest price of one signature per flavour."""
59
+
60
+ async def decode(self, response: AsyncHttpResponse) -> T_co: ...
61
+
62
+
63
+ class ErrorMapper(Protocol[E_co]):
64
+ """Maps a non-2xx response to the operation's typed error body -- a union of the
65
+ documented error schemas, or ``RawError`` for an unmapped status.
66
+
67
+ Takes the two facts a mapper uses and no envelope around them: the status it switches on, and
68
+ the body it decodes. Both are values by the time this runs -- the raw client has read the body
69
+ and released the connection before calling -- so there is nothing here that could hold a
70
+ connection open, and nothing to ask whether it has been read yet."""
71
+
72
+ def map(self, status_code: int, content: bytes) -> E_co: ...
73
+
74
+
75
+ @dataclass(frozen=True, slots=True)
76
+ class BufferedDecoder(Generic[T]):
77
+ """Reads a 2xx body to the end, releases the connection, then decodes what it read.
78
+
79
+ The bridge between a seam that hands every decoder an unread response and the decoders whose
80
+ payload is a value rather than a connection. ``decode_body`` is the step that turns the buffered
81
+ response into that value -- a JSON parse, a text parse, or nothing at all.
82
+
83
+ Written once per flavour instead of once per format, which is what lets the three steps below
84
+ stay plain ``bytes -> T`` functions: they are the same callables a generated error mapper reaches
85
+ through ``decode_json``, so one parse serves the success path and the error path alike.
86
+
87
+ The body is read even where the payload ignores it, as :data:`empty_response`'s does: an unread
88
+ body cannot be returned to the pool, only abandoned. The ``finally`` is what makes a failed read
89
+ release rather than leak, and the parse runs *after* it, so nothing holds a socket while pydantic
90
+ works and a rejected payload cannot strand a connection either."""
91
+
92
+ decode_body: Callable[[bytes], T]
93
+
94
+ def decode(self, response: HttpResponse) -> T:
95
+ try:
96
+ content = response.read()
97
+ finally:
98
+ response.close()
99
+ return self.decode_body(content)
100
+
101
+
102
+ @dataclass(frozen=True, slots=True)
103
+ class AsyncBufferedDecoder(Generic[T]):
104
+ """The twin of :class:`BufferedDecoder`, over the async response."""
105
+
106
+ decode_body: Callable[[bytes], T]
107
+
108
+ async def decode(self, response: AsyncHttpResponse) -> T:
109
+ try:
110
+ content = await response.aread()
111
+ finally:
112
+ await response.aclose()
113
+ return self.decode_body(content)
114
+
115
+
116
+ def _decode_utf8(content: bytes) -> str:
117
+ """Read a buffered body as UTF-8 text, refusing a byte that is not.
118
+
119
+ ``strict``: on the payload path an undecodable byte is corruption, and the caller is owed the
120
+ failure rather than a U+FFFD standing in for it. A diagnostic reader replaces instead -- see
121
+ :meth:`RawError.text`. The charset is fixed at UTF-8 here; were a response charset ever
122
+ spec-derived the way a request's is, this is the argument it would become.
123
+
124
+ The plain-text counterpart of :func:`parse_json`, and the only other step a :class:`BodyDecoder`
125
+ is built over. Private, because nothing outside this module reads a body as text:
126
+ :meth:`RawError.text` has its own, replacing posture.
127
+
128
+ Args:
129
+ content: The buffered body.
130
+
131
+ Returns:
132
+ The body as text.
133
+
134
+ Raises:
135
+ UnicodeDecodeError: If the body is not UTF-8."""
136
+ return content.decode("utf-8", errors="strict")
137
+
138
+
139
+ @dataclass(frozen=True, slots=True)
140
+ class BodyDecoder(Generic[T]):
141
+ """Reads a buffered body as a Python value, then validates it through ``adapter``.
142
+
143
+ The decode *step*, not a decoder for the seam: :class:`BufferedDecoder` wraps it for an endpoint,
144
+ and ``decode_json`` hands the same bound method to a generated error mapper.
145
+
146
+ ``parse`` is the whole difference between a JSON body and a plain-text one -- ``parse_json``
147
+ for the first, :func:`_decode_utf8` for the second. It is the *format*, and a format is data:
148
+ both produce the same ``BodyDecoder[T]``, so nothing above this class asks which it holds. What
149
+ may never become data is the *shape* a factory returns -- see :class:`_BufferedDecoderFactory`.
150
+
151
+ Its result is deliberately untyped. What a body parses to is the adapter's to judge and ``T`` is
152
+ solved from ``adapter`` alone: typing it ``str`` would refuse ``parse_json``, and typing it ``T``
153
+ would claim a parse had already validated."""
154
+
155
+ parse: Callable[[bytes], Any]
156
+ adapter: TypeAdapter[T]
157
+
158
+ def decode(self, content: bytes) -> T:
159
+ return self.adapter.validate_python(self.parse(content))
160
+
161
+
162
+ @dataclass(frozen=True, slots=True)
163
+ class _BufferedDecoderFactory(SubscriptOnly):
164
+ """``json_decoder[T]`` / ``text_decoder[T]`` -- build a decoder that reads a buffered body.
165
+
166
+ One class for two formats, because both produce the same thing: a ``ResponseDecoder[T]`` whose
167
+ ``T`` is the subscript. The format is ``parse``, a field, and ``json_decoder`` / ``text_decoder``
168
+ below are the whole difference between them. Its adapter comes from :func:`adapter_for`, the one
169
+ cache the request path (``json_body``, ``param``) draws from too.
170
+
171
+ The line a field may not cross is the *return shape*. ``ResponseDecoder[T]``,
172
+ ``AsyncResponseDecoder[T]`` and ``Callable[[bytes], T]`` each depend on the subscripted type, and
173
+ Python has no higher-kinded types, so no one class can produce all three -- which is the precision
174
+ that makes ``json_decoder[int]`` an error where ``ApiResult[str, ...]`` is declared. One class per
175
+ shape is the floor, and this is one of the three.
176
+
177
+ The subscript is a ``TypeForm`` (PEP 747) rather than a ``type[T]``, so a runtime union alias
178
+ (``Person``, which is ``Employee | Boss``), an ``Annotated`` wire-format alias and a
179
+ ``list[...]`` wrapper are each as acceptable as a concrete class and ``T`` stays precise for all
180
+ of them. That precision is what makes an endpoint's declared payload type checkable against what
181
+ it decodes. A ``type[T]``-plus-``object`` overload pair cannot: given a declared type the precise
182
+ overload does not fit, inference falls through to the ``object`` one, yields ``Any``, and the
183
+ disagreement has nowhere left to surface. And because the factory declares no ``__call__``, the
184
+ type cannot be omitted (``[operator]``); the one inherited from :class:`SubscriptOnly` is
185
+ checker-invisible and only guides."""
186
+
187
+ parse: Callable[[bytes], Any]
188
+ factory_name: str
189
+ spelling: str
190
+
191
+ def __getitem__(self, declared: TypeForm[T]) -> ResponseDecoder[T]:
192
+ return BufferedDecoder(BodyDecoder(self.parse, adapter_for(declared)).decode)
193
+
194
+
195
+ @dataclass(frozen=True, slots=True)
196
+ class _AsyncBufferedDecoderFactory(SubscriptOnly):
197
+ """``async_json_decoder[T]`` / ``async_text_decoder[T]`` -- the async twins; see the sync one.
198
+
199
+ A separate class rather than a third field, and that is the rule stated there read the other way
200
+ round: an argument position is contravariant, so an ``AsyncResponseDecoder[T]`` can never satisfy
201
+ the sync seam nor the reverse, and the flavour is exactly what may not become data. Which is what
202
+ makes this a build-time distinction rather than a coroutine nobody awaited."""
203
+
204
+ parse: Callable[[bytes], Any]
205
+ factory_name: str
206
+ spelling: str
207
+
208
+ def __getitem__(self, declared: TypeForm[T]) -> AsyncResponseDecoder[T]:
209
+ return AsyncBufferedDecoder(BodyDecoder(self.parse, adapter_for(declared)).decode)
210
+
211
+
212
+ json_decoder: Final = _BufferedDecoderFactory(
213
+ parse_json, "json_decoder", "decoder=json_decoder[T], e.g. json_decoder[Person]"
214
+ )
215
+ text_decoder: Final = _BufferedDecoderFactory(
216
+ _decode_utf8, "text_decoder", "decoder=text_decoder[T], e.g. text_decoder[int]"
217
+ )
218
+ async_json_decoder: Final = _AsyncBufferedDecoderFactory(
219
+ parse_json, "async_json_decoder", "decoder=async_json_decoder[T], e.g. async_json_decoder[Person]"
220
+ )
221
+ async_text_decoder: Final = _AsyncBufferedDecoderFactory(
222
+ _decode_utf8, "async_text_decoder", "decoder=async_text_decoder[T], e.g. async_text_decoder[int]"
223
+ )
224
+
225
+
226
+ class _DecodeJsonFactory(SubscriptOnly):
227
+ """``decode_json[T](content)`` -- decode a JSON body as the subscripted type, now.
228
+
229
+ The result is typed as the subscripted type whatever shape it takes -- a concrete class, a
230
+ union such as ``Accountant | Manager``, an ``Annotated`` alias -- so a generated error mapper
231
+ returns its documented body union directly, with no ``cast`` standing in for what the type
232
+ system now carries itself.
233
+
234
+ Its format is written in the body rather than passed as a field, unlike the two buffered
235
+ factories: this shape has one other member, ``decode_text``, and that one is callable bare.
236
+ Parametrising a class for effectively one instance would hand ``decode_text`` a ``factory_name``
237
+ and a ``spelling`` it can never raise."""
238
+
239
+ factory_name = "decode_json"
240
+ spelling = "decode_json[T](content), e.g. decode_json[ProblemDetails](content)"
241
+
242
+ def __getitem__(self, declared: TypeForm[T]) -> Callable[[bytes], T]:
243
+ return BodyDecoder(parse_json, adapter_for(declared)).decode
244
+
245
+
246
+ class _DecodeTextFactory:
247
+ """``decode_text[T](content)`` -- decode a plain-text body, now; see ``decode_json``.
248
+
249
+ Also callable bare -- ``decode_text(content)`` -- which is the ``str`` default the retired
250
+ two-member overload existed to carry: the default lives on ``__call__`` and a named type can
251
+ only take the subscript path, so a mismatch has nowhere to fall through to."""
252
+
253
+ def __getitem__(self, declared: TypeForm[T]) -> Callable[[bytes], T]:
254
+ return BodyDecoder(_decode_utf8, adapter_for(declared)).decode
255
+
256
+ def __call__(self, content: bytes) -> str:
257
+ return self[str](content)
258
+
259
+
260
+ decode_json: Final = _DecodeJsonFactory()
261
+ decode_text: Final = _DecodeTextFactory()
262
+
263
+
264
+ def _discard_body(content: bytes) -> None:
265
+ """Discard a buffered body, for an operation whose 2xx declares none.
266
+
267
+ The third decode step, beside :func:`parse_json` and :func:`_decode_utf8`, and the only one that
268
+ reads nothing: what arrived is never inspected, so correctness does not rest on the body being
269
+ empty in fact. It is read all the same, one layer up in :class:`BufferedDecoder`, because an
270
+ unread body has to abandon its connection rather than return it to the pool.
271
+
272
+ Private, like :func:`_decode_utf8`: what an operation names is the singleton below, never this.
273
+
274
+ Args:
275
+ content: The buffered body, whatever arrived in it.
276
+
277
+ Returns:
278
+ ``None``, always."""
279
+ return None
280
+
281
+
282
+ empty_response: Final[ResponseDecoder[None]] = BufferedDecoder(_discard_body)
283
+ """Decoder for an operation whose 2xx body is empty, and the success-side twin of
284
+ :data:`raw_error_response`.
285
+
286
+ It is the reason ``decoder`` is a required argument rather than an optional one: an operation that
287
+ returns no content says so by naming this, instead of by leaving the argument out. Presence is
288
+ auditable where absence is not -- a missing ``decoder=`` could be a no-content operation or a
289
+ generator that dropped the line, and only one of those should compile.
290
+
291
+ Naming it is also what keeps the payload type honest. ``T`` is solved from this argument, so the
292
+ operation can declare ``ApiResult[None, E]`` and nothing else. Were the argument omitted instead,
293
+ ``T`` would be solved from the declared type, and an operation that parses nothing could claim any
294
+ payload it liked and hand back ``None`` at runtime -- the last place a declared type could go
295
+ unstated, now that each factory makes its own subscript mandatory.
296
+
297
+ Not subscripted, and not a factory: there is no type to name. ``json_decoder[None]`` would still
298
+ parse the body and raise on an empty one, which is the opposite of what this does.
299
+
300
+ Carries nothing SDK-specific, so it lives here rather than in the generated error layer, and is
301
+ shared by every such operation."""
302
+
303
+ async_empty_response: Final[AsyncResponseDecoder[None]] = AsyncBufferedDecoder(_discard_body)
304
+ """The async twin of :data:`empty_response`; a sync decoder on the async seam is a build failure."""
305
+
306
+
307
+ @dataclass(frozen=True, slots=True)
308
+ class RawErrorResponse:
309
+ """Error mapper for an operation that declares no error schemas.
310
+
311
+ Deliberately has **no** ``match`` on the status code: the absence is the
312
+ signal. Where an operation's spec documents error responses, its own mapper
313
+ in the generated error layer selects a schema per status; where it documents
314
+ none there is nothing to select, so every non-2xx response maps to the same
315
+ :class:`RawError` -- status plus the undecoded body, decoded on demand.
316
+
317
+ Carries nothing SDK-specific, so it lives here rather than in the generated
318
+ error layer, and is shared by every such operation."""
319
+
320
+ def map(self, status_code: int, content: bytes) -> RawError:
321
+ return RawError(status_code, content)
322
+
323
+
324
+ raw_error_response: Final[ErrorMapper[RawError]] = RawErrorResponse()
325
+
326
+
327
+ @dataclass(frozen=True, slots=True)
328
+ class FileDecoder:
329
+ """Decoder for an operation whose 2xx body is a file, handing it the open connection.
330
+
331
+ Not subscripted, and not a factory: a file bypasses the model layer, so there is no adapter to
332
+ name -- the same absence that leaves ``file_part`` unsubscripted on the request side.
333
+
334
+ Naming it is what keeps the payload solved from an *argument* rather than from the seam's own
335
+ return type, which is what lets a second streamed payload shape join ``execute`` instead of
336
+ forking it."""
337
+
338
+ def decode(self, response: HttpResponse) -> FileResponse:
339
+ return FileResponse(response)
340
+
341
+
342
+ @dataclass(frozen=True, slots=True)
343
+ class AsyncFileDecoder:
344
+ """Decoder for an operation whose 2xx body is a file; the twin of :class:`FileDecoder`.
345
+
346
+ ``async def`` with nothing awaited: the payload is ready, and the keyword is what makes this
347
+ decoder unusable at the sync seam."""
348
+
349
+ async def decode(self, response: AsyncHttpResponse) -> AsyncFileResponse:
350
+ return AsyncFileResponse(response)
351
+
352
+
353
+ file_decoder: Final[ResponseDecoder[FileResponse]] = FileDecoder()
354
+ async_file_decoder: Final[AsyncResponseDecoder[AsyncFileResponse]] = AsyncFileDecoder()
355
+
356
+
357
+ @dataclass(frozen=True, slots=True)
358
+ class EventStreamDecoder(Generic[T]):
359
+ """Response decoder for an operation whose 2xx body is an event stream.
360
+
361
+ Hands the open connection to the :class:`EventStream` it builds, exactly as :class:`FileDecoder`
362
+ does with its file: the body is the payload, so nothing is read here.
363
+
364
+ ``decode_data`` is how each event's ``data`` becomes a ``T`` -- the adapter's JSON entry point or
365
+ its plain-value one, chosen by the factory that built this -- and ``sentinel`` is the ``data``
366
+ value that ends the stream, ``None`` where the operation declares none. The format is which
367
+ callable was handed in, never a flag read at runtime."""
368
+
369
+ decode_data: Callable[[str], T]
370
+ sentinel: str | None
371
+
372
+ def decode(self, response: HttpResponse) -> EventStream[T]:
373
+ return EventStream(response, self.decode_data, self.sentinel)
374
+
375
+
376
+ @dataclass(frozen=True, slots=True)
377
+ class AsyncEventStreamDecoder(Generic[T]):
378
+ """The twin of :class:`EventStreamDecoder`, over the async response.
379
+
380
+ ``async def`` with nothing awaited, as :class:`AsyncFileDecoder` is: the payload is ready, and the
381
+ keyword is what makes this decoder unusable at the sync seam."""
382
+
383
+ decode_data: Callable[[str], T]
384
+ sentinel: str | None
385
+
386
+ async def decode(self, response: AsyncHttpResponse) -> AsyncEventStream[T]:
387
+ return AsyncEventStream(response, self.decode_data, self.sentinel)
388
+
389
+
390
+ @dataclass(frozen=True, slots=True)
391
+ class EventStreamDecoderBuilder(Generic[T]):
392
+ """What ``json_event_decoder[T]`` hands back: the type resolved, the sentinel still to name.
393
+
394
+ Calling it -- ``(sentinel="[DONE]")``, or ``()`` for an operation declaring none -- yields the
395
+ decoder. It is not itself one: it has no ``decode``, so ``decoder=json_event_decoder[T]`` with the
396
+ call left off is an ``[arg-type]`` build failure rather than a stream that never ends. The
397
+ sentinel is keyword-only, so a positional one is a build failure too."""
398
+
399
+ decode_data: Callable[[str], T]
400
+
401
+ def __call__(self, *, sentinel: str | None = None) -> ResponseDecoder[EventStream[T]]:
402
+ return EventStreamDecoder(self.decode_data, sentinel)
403
+
404
+
405
+ @dataclass(frozen=True, slots=True)
406
+ class AsyncEventStreamDecoderBuilder(Generic[T]):
407
+ """The twin of :class:`EventStreamDecoderBuilder`, yielding the async decoder."""
408
+
409
+ decode_data: Callable[[str], T]
410
+
411
+ def __call__(self, *, sentinel: str | None = None) -> AsyncResponseDecoder[AsyncEventStream[T]]:
412
+ return AsyncEventStreamDecoder(self.decode_data, sentinel)
413
+
414
+
415
+ def _validates_json(adapter: TypeAdapter[Any]) -> Callable[[str], Any]:
416
+ """An event's ``data`` parsed and validated in one pass, for an event stream carrying JSON.
417
+
418
+ The adapter's own JSON entry point rather than a parse followed by a validation: one pass, no
419
+ intermediate Python object, on a path that runs once per event rather than once per response.
420
+ That is why the two event factories name an entry point where the two buffered ones name a
421
+ ``parse`` -- the step they share comes *after* the adapter there, and *is* the adapter here.
422
+
423
+ Args:
424
+ adapter: The adapter for the subscripted event type.
425
+
426
+ Returns:
427
+ A callable turning one event's ``data`` into that type."""
428
+ return adapter.validate_json
429
+
430
+
431
+ def _validates_value(adapter: TypeAdapter[Any]) -> Callable[[str], Any]:
432
+ """An event's ``data`` validated as the value it already is, for an event stream carrying text.
433
+
434
+ ``[str]`` passes the string through and ``[int]`` coerces it, exactly as ``text_decoder[int]``
435
+ would; see :func:`_validates_json`.
436
+
437
+ Args:
438
+ adapter: The adapter for the subscripted event type.
439
+
440
+ Returns:
441
+ A callable turning one event's ``data`` into that type."""
442
+ return adapter.validate_python
443
+
444
+
445
+ @dataclass(frozen=True, slots=True)
446
+ class _EventDecoderFactory(SubscriptOnly):
447
+ """``json_event_decoder[T](sentinel=...)`` / ``text_event_decoder[T](...)`` -- an event stream.
448
+
449
+ One class for two formats, for the reason :class:`_BufferedDecoderFactory` gives: both produce
450
+ the same thing, an :class:`EventStreamDecoderBuilder` whose ``T`` is the subscript. ``read_data``
451
+ is the whole difference between them, and it may be a field because it carries no type of its
452
+ own; only the return *shape* may not.
453
+
454
+ The subscript is a ``TypeForm``, exactly as ``json_decoder``'s is, so a union alias such as a
455
+ discriminated event union routes each event through its ``WireDiscriminator``. What comes back is
456
+ a *builder* rather than a decoder, because an event stream carries one spec fact the type cannot:
457
+ the ``data`` value that ends it. And because the factory declares no ``__call__``, the type cannot
458
+ be omitted (``[operator]``); the one inherited from :class:`SubscriptOnly` is checker-invisible
459
+ and only guides."""
460
+
461
+ read_data: Callable[[TypeAdapter[Any]], Callable[[str], Any]]
462
+ factory_name: str
463
+ spelling: str
464
+
465
+ def __getitem__(self, declared: TypeForm[T]) -> EventStreamDecoderBuilder[T]:
466
+ return EventStreamDecoderBuilder(self.read_data(adapter_for(declared)))
467
+
468
+
469
+ @dataclass(frozen=True, slots=True)
470
+ class _AsyncEventDecoderFactory(SubscriptOnly):
471
+ """The async twins; see the sync one.
472
+
473
+ A separate class rather than a third field, and that is the rule stated there read the other way
474
+ round: an :class:`AsyncEventStreamDecoderBuilder` builds a decoder the sync seam cannot take, so
475
+ the flavour is exactly what may not become data."""
476
+
477
+ read_data: Callable[[TypeAdapter[Any]], Callable[[str], Any]]
478
+ factory_name: str
479
+ spelling: str
480
+
481
+ def __getitem__(self, declared: TypeForm[T]) -> AsyncEventStreamDecoderBuilder[T]:
482
+ return AsyncEventStreamDecoderBuilder(self.read_data(adapter_for(declared)))
483
+
484
+
485
+ json_event_decoder: Final = _EventDecoderFactory(
486
+ _validates_json,
487
+ "json_event_decoder",
488
+ "decoder=json_event_decoder[T](), e.g. json_event_decoder[Chunk](sentinel='[DONE]')",
489
+ )
490
+ text_event_decoder: Final = _EventDecoderFactory(
491
+ _validates_value, "text_event_decoder", "decoder=text_event_decoder[T](), e.g. text_event_decoder[str]()"
492
+ )
493
+ async_json_event_decoder: Final = _AsyncEventDecoderFactory(
494
+ _validates_json, "async_json_event_decoder", "decoder=async_json_event_decoder[T]()"
495
+ )
496
+ async_text_event_decoder: Final = _AsyncEventDecoderFactory(
497
+ _validates_value, "async_text_event_decoder", "decoder=async_text_event_decoder[T]()"
498
+ )
@@ -0,0 +1,129 @@
1
+ """A discriminated union whose tag the variants need not carry.
2
+
3
+ OpenAPI puts ``discriminator`` on the ``oneOf``/``anyOf`` node rather than on the schemas it
4
+ selects, so the property name and the value-to-schema mapping are the union's facts. A schema that
5
+ does not pin that property itself can appear in two unions under two different property names and
6
+ two different tag values, and a model carrying one union's tag in a field could never serve the
7
+ other.
8
+
9
+ :class:`WireDiscriminator` routes on that tag without requiring the variants to carry it. Each arm
10
+ carries pydantic's ``Tag`` with the mapping value the spec gave it; the metadata names the wire
11
+ property. **The union supplies the tag exactly where the variant has no field for it** -- removing
12
+ the key before such a variant validates, and writing it back when such a variant is dumped -- so one
13
+ model round-trips through every union that references it, and a variant's field set stays exactly its
14
+ own schema's.
15
+
16
+ A variant whose schema *does* declare the property keeps that field, and the union leaves it alone in
17
+ both directions: the key reaches the field, and the field dumps it back. The field is read through
18
+ its alias, so the tag may arrive under the wire spelling or under the field's own name.
19
+
20
+ Several mapping values may select the same variant. Each value is its own arm, so each routes; the
21
+ first one the union lists is the spelling a model instance dumps through, since a model carries no
22
+ tag of its own to disambiguate them.
23
+
24
+ **Every discriminated union names this one metadata, whatever its arms declare.** Pydantic's own
25
+ ``Field(discriminator=...)`` can serve only the narrowest case -- every arm pinning the property to a
26
+ constant equal to its own mapping value, under one shared field name, one value per arm -- and it
27
+ requires a ``Literal``, refusing a plain ``str``. Reserving it for that case would make the emitting
28
+ template resolve every arm's schema and compare it against the mapping before it could choose a
29
+ shape, and a wrong comparison routes on a value the spec never named. One shape, decided from the
30
+ union node alone, is the trade."""
31
+
32
+ from __future__ import annotations
33
+
34
+ from dataclasses import dataclass
35
+ from typing import Any, get_args
36
+
37
+ from pydantic import GetCoreSchemaHandler, Tag
38
+ from pydantic_core import core_schema
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class WireDiscriminator:
43
+ """``Annotated`` metadata turning a union of ``Tag``-marked arms into a wire-tagged union.
44
+
45
+ Args:
46
+ property_name: The wire key the tag is read from and written to -- the spec's
47
+ ``discriminator.propertyName`` in its wire spelling, not a Python field name, because the
48
+ tag is read before any variant's alias machinery runs.
49
+ companion_key: The key the dict companions declare the tag under, given wherever it differs
50
+ from ``property_name`` and some variant needs the union to declare it -- the member
51
+ spelling of the wire key, sanitized where that key is a Python keyword or no identifier
52
+ at all. Read alongside ``property_name``; never written on dump, since only the wire
53
+ spelling belongs on the wire."""
54
+
55
+ property_name: str
56
+ companion_key: str | None = None
57
+
58
+ def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
59
+ """Build the tagged union, supplying the tag for the arms that declare no field for it.
60
+
61
+ Args:
62
+ source: The union of ``Annotated[Variant, Tag(...)]`` arms this metadata annotates.
63
+ handler: Pydantic's schema generator, called once per arm.
64
+
65
+ Returns:
66
+ A tagged-union schema keyed by the variants' declared tags.
67
+
68
+ Raises:
69
+ TypeError: If an arm carries no ``Tag``, leaving its mapping value undeclared."""
70
+ property_name = self.property_name
71
+ arms: list[tuple[type[Any], str]] = []
72
+ spellings: dict[type[Any], frozenset[str]] = {}
73
+ for arm in get_args(source):
74
+ variant, *metadata = get_args(arm)
75
+ tag = next((item.tag for item in metadata if isinstance(item, Tag)), None)
76
+ if tag is None:
77
+ raise TypeError(f"{variant!r} carries no Tag, so WireDiscriminator has no mapping value for it")
78
+ arms.append((variant, tag))
79
+ if variant not in spellings:
80
+ spellings[variant] = frozenset(
81
+ name for name, field in variant.model_fields.items() if property_name in (name, field.alias)
82
+ )
83
+
84
+ # The value a model instance dumps through, for a variant several mapping values select. It
85
+ # carries no tag of its own, so the union's first listing is the one deterministic answer.
86
+ canonical: dict[type[Any], str] = {}
87
+ for variant, tag in arms:
88
+ canonical.setdefault(variant, tag)
89
+
90
+ declared = {name for names in spellings.values() for name in names}
91
+ substitute = {self.companion_key} if self.companion_key is not None else set[str]()
92
+ keys = [property_name, *sorted((declared | substitute) - {property_name})]
93
+
94
+ def read_tag(value: Any) -> str | None:
95
+ if not isinstance(value, dict):
96
+ return canonical.get(type(value))
97
+ for key in keys:
98
+ tag_ = value.get(key)
99
+ if isinstance(tag_, str):
100
+ return tag_
101
+ return None
102
+
103
+ # pydantic-core names the reader in both union-tag errors, so this reads ``using petKind()``
104
+ # rather than ``using read_tag()`` -- the wire key the spec declares, which is what a caller
105
+ # reading the message is looking for.
106
+ read_tag.__name__ = property_name
107
+
108
+ def choice(variant_: type[Any], tag_: str) -> core_schema.CoreSchema:
109
+ schema = handler.generate_schema(variant_)
110
+ if spellings[variant_]:
111
+ return schema
112
+ foreign = frozenset(keys)
113
+
114
+ def without_tag(value: Any, validate: core_schema.ValidatorFunctionWrapHandler) -> Any:
115
+ if isinstance(value, dict) and not foreign.isdisjoint(value):
116
+ return validate({key: item for key, item in value.items() if key not in foreign})
117
+ return validate(value)
118
+
119
+ def with_tag(value: Any, serialize: core_schema.SerializerFunctionWrapHandler) -> Any:
120
+ dumped = serialize(value)
121
+ if isinstance(dumped, dict):
122
+ return {**dumped, property_name: tag_}
123
+ return dumped
124
+
125
+ return core_schema.no_info_wrap_validator_function(
126
+ without_tag, schema, serialization=core_schema.wrap_serializer_function_ser_schema(with_tag)
127
+ )
128
+
129
+ return core_schema.tagged_union_schema({tag: choice(variant, tag) for variant, tag in arms}, read_tag)