cliV1 0.0.8__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 (87) hide show
  1. cliv1-0.0.8.dist-info/METADATA +168 -0
  2. cliv1-0.0.8.dist-info/RECORD +87 -0
  3. cliv1-0.0.8.dist-info/WHEEL +4 -0
  4. cliv1-0.0.8.dist-info/licenses/LICENSE +21 -0
  5. swagger_petstore_open_api_3_0/__init__.py +15 -0
  6. swagger_petstore_open_api_3_0/apis/__init__.py +5 -0
  7. swagger_petstore_open_api_3_0/apis/pet_api.py +883 -0
  8. swagger_petstore_open_api_3_0/apis/store.py +413 -0
  9. swagger_petstore_open_api_3_0/apis/user_api.py +794 -0
  10. swagger_petstore_open_api_3_0/async_client.py +79 -0
  11. swagger_petstore_open_api_3_0/auth.py +17 -0
  12. swagger_petstore_open_api_3_0/base_client.py +18 -0
  13. swagger_petstore_open_api_3_0/client.py +77 -0
  14. swagger_petstore_open_api_3_0/core/__init__.py +358 -0
  15. swagger_petstore_open_api_3_0/core/_internal/__init__.py +9 -0
  16. swagger_petstore_open_api_3_0/core/_internal/content_disposition.py +154 -0
  17. swagger_petstore_open_api_3_0/core/_internal/flattening.py +139 -0
  18. swagger_petstore_open_api_3_0/core/_internal/headers.py +144 -0
  19. swagger_petstore_open_api_3_0/core/_internal/sse_parser.py +124 -0
  20. swagger_petstore_open_api_3_0/core/_internal/subscripts.py +50 -0
  21. swagger_petstore_open_api_3_0/core/_internal/urls.py +186 -0
  22. swagger_petstore_open_api_3_0/core/_internal/wire.py +198 -0
  23. swagger_petstore_open_api_3_0/core/adapters.py +177 -0
  24. swagger_petstore_open_api_3_0/core/auth/__init__.py +70 -0
  25. swagger_petstore_open_api_3_0/core/auth/composition.py +175 -0
  26. swagger_petstore_open_api_3_0/core/auth/models.py +216 -0
  27. swagger_petstore_open_api_3_0/core/auth/protocols.py +103 -0
  28. swagger_petstore_open_api_3_0/core/auth/schemes/__init__.py +94 -0
  29. swagger_petstore_open_api_3_0/core/auth/schemes/api_key_auth.py +53 -0
  30. swagger_petstore_open_api_3_0/core/auth/schemes/basic_auth.py +73 -0
  31. swagger_petstore_open_api_3_0/core/auth/schemes/bearer_auth.py +21 -0
  32. swagger_petstore_open_api_3_0/core/auth/schemes/oauth2_authorization_code.py +393 -0
  33. swagger_petstore_open_api_3_0/core/auth/schemes/oauth2_client_credentials.py +127 -0
  34. swagger_petstore_open_api_3_0/core/auth/schemes/oauth2_password.py +142 -0
  35. swagger_petstore_open_api_3_0/core/auth/schemes/oauth2_schemes.py +300 -0
  36. swagger_petstore_open_api_3_0/core/base_raw_response.py +49 -0
  37. swagger_petstore_open_api_3_0/core/bodies.py +550 -0
  38. swagger_petstore_open_api_3_0/core/converters/__init__.py +68 -0
  39. swagger_petstore_open_api_3_0/core/converters/date_time.py +237 -0
  40. swagger_petstore_open_api_3_0/core/converters/encoded_bytes.py +273 -0
  41. swagger_petstore_open_api_3_0/core/converters/open_enum.py +35 -0
  42. swagger_petstore_open_api_3_0/core/decoding.py +498 -0
  43. swagger_petstore_open_api_3_0/core/discriminators.py +129 -0
  44. swagger_petstore_open_api_3_0/core/event_streams.py +225 -0
  45. swagger_petstore_open_api_3_0/core/exceptions.py +38 -0
  46. swagger_petstore_open_api_3_0/core/file_responses.py +335 -0
  47. swagger_petstore_open_api_3_0/core/files.py +173 -0
  48. swagger_petstore_open_api_3_0/core/httpx_transport.py +748 -0
  49. swagger_petstore_open_api_3_0/core/models.py +143 -0
  50. swagger_petstore_open_api_3_0/core/optionality.py +130 -0
  51. swagger_petstore_open_api_3_0/core/params.py +157 -0
  52. swagger_petstore_open_api_3_0/core/raw_client.py +338 -0
  53. swagger_petstore_open_api_3_0/core/request_options.py +94 -0
  54. swagger_petstore_open_api_3_0/core/results.py +123 -0
  55. swagger_petstore_open_api_3_0/core/runtime_env.py +43 -0
  56. swagger_petstore_open_api_3_0/core/servers.py +33 -0
  57. swagger_petstore_open_api_3_0/core/transport.py +191 -0
  58. swagger_petstore_open_api_3_0/errors/__init__.py +48 -0
  59. swagger_petstore_open_api_3_0/errors/add_pet_error.py +21 -0
  60. swagger_petstore_open_api_3_0/errors/delete_order_error.py +21 -0
  61. swagger_petstore_open_api_3_0/errors/delete_pet_error.py +21 -0
  62. swagger_petstore_open_api_3_0/errors/delete_user_error.py +21 -0
  63. swagger_petstore_open_api_3_0/errors/find_pets_by_status_error.py +21 -0
  64. swagger_petstore_open_api_3_0/errors/find_pets_by_tags_error.py +21 -0
  65. swagger_petstore_open_api_3_0/errors/get_order_by_id_error.py +21 -0
  66. swagger_petstore_open_api_3_0/errors/get_pet_by_id_error.py +21 -0
  67. swagger_petstore_open_api_3_0/errors/get_user_by_name_error.py +21 -0
  68. swagger_petstore_open_api_3_0/errors/login_user_error.py +21 -0
  69. swagger_petstore_open_api_3_0/errors/place_order_error.py +21 -0
  70. swagger_petstore_open_api_3_0/errors/update_pet_error.py +21 -0
  71. swagger_petstore_open_api_3_0/errors/update_pet_with_form_error.py +21 -0
  72. swagger_petstore_open_api_3_0/errors/update_user_error.py +21 -0
  73. swagger_petstore_open_api_3_0/errors/upload_file_error.py +21 -0
  74. swagger_petstore_open_api_3_0/models/__init__.py +23 -0
  75. swagger_petstore_open_api_3_0/models/api_response.py +18 -0
  76. swagger_petstore_open_api_3_0/models/category.py +15 -0
  77. swagger_petstore_open_api_3_0/models/enums/__init__.py +4 -0
  78. swagger_petstore_open_api_3_0/models/enums/order_status.py +17 -0
  79. swagger_petstore_open_api_3_0/models/enums/pet_status.py +17 -0
  80. swagger_petstore_open_api_3_0/models/order.py +27 -0
  81. swagger_petstore_open_api_3_0/models/pet.py +28 -0
  82. swagger_petstore_open_api_3_0/models/tag_model.py +15 -0
  83. swagger_petstore_open_api_3_0/models/user.py +29 -0
  84. swagger_petstore_open_api_3_0/py.typed +0 -0
  85. swagger_petstore_open_api_3_0/server/__init__.py +21 -0
  86. swagger_petstore_open_api_3_0/server/server.py +17 -0
  87. swagger_petstore_open_api_3_0/server/server_config.py +52 -0
@@ -0,0 +1,143 @@
1
+ """The base model every domain model subclasses."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any, Literal
7
+
8
+ from pydantic import BaseModel, ConfigDict
9
+
10
+ from .optionality import strip_unset
11
+
12
+
13
+ class SdkBaseModel(BaseModel):
14
+ """Base class for every domain model.
15
+
16
+ Immutable (``frozen``), **preserves** unknown JSON fields rather than dropping
17
+ them (``extra="allow"``), accepts either the Python field name or its wire alias
18
+ on input, and serializes declared fields back under the wire alias. Attribute
19
+ docstrings on fields are lifted into their schema ``description``.
20
+
21
+ A field the server adds after this SDK was generated survives on the instance --
22
+ readable via ``model_extra``, or by attribute access unless the key collides with
23
+ the model API -- and is re-emitted under the key it arrived with, at every nesting
24
+ level. Preservation is deliberately symmetric: an unknown key a caller supplies is
25
+ likewise sent, so a model received from the server can be echoed back without
26
+ silently discarding server-side state. See
27
+ ``docs/designs/unknown-field-preservation.md``.
28
+
29
+ ``frozen`` blocks rebinding an attribute; it does not deep-freeze values. The
30
+ contents of a ``list``/``dict`` field and the ``model_extra`` mapping remain
31
+ mutable in place.
32
+
33
+ :meth:`to_dict` and :meth:`to_json` are the ergonomic front door to serialization;
34
+ ``model_dump``/``model_dump_json`` remain available for the full option set. See
35
+ ``docs/designs/model-serialization-helpers.md``."""
36
+
37
+ model_config = ConfigDict(
38
+ frozen=True,
39
+ extra="allow",
40
+ validate_by_name=True,
41
+ validate_by_alias=True,
42
+ serialize_by_alias=True,
43
+ use_attribute_docstrings=True,
44
+ )
45
+
46
+ def to_dict(
47
+ self,
48
+ *,
49
+ mode: Literal["json", "python"] = "json",
50
+ by_alias: bool = True,
51
+ exclude_unset: bool = False,
52
+ exclude_defaults: bool = False,
53
+ exclude_none: bool = False,
54
+ ) -> dict[str, Any]:
55
+ """Return this model as a dictionary in the shape the API exchanges.
56
+
57
+ Defaults produce JSON-safe values under the wire alias of every field, so the
58
+ result is ``json.dumps``-able, equals ``json.loads(self.to_json())``, and
59
+ validates back into an equal model -- including through a discriminated-union
60
+ alias. Unknown fields the server sent are included (see
61
+ ``docs/designs/unknown-field-preservation.md``).
62
+
63
+ This is a convenience wrapper over ``model_dump``; reach for that directly when
64
+ you need the options this deliberately does not surface (``include``/``exclude``,
65
+ ``context``, ``round_trip``, ``warnings``, ``serialize_as_any``, ``fallback``).
66
+
67
+ A field typed ``OptionalNullable[...]`` (e.g. an omit-vs-null field) that the caller
68
+ never touched is always omitted here, even though plain ``model_dump`` shows it as
69
+ ``null`` -- this is the one documented exception to "exactly the underlying dump".
70
+ See ``docs/designs/optional-nullable-fields.md``.
71
+
72
+ Args:
73
+ mode: ``"json"`` coerces values to JSON-safe types (``datetime`` becomes a
74
+ string); ``"python"`` keeps the Python objects.
75
+ by_alias: Emit each field under its wire alias rather than its Python name.
76
+ The base config already serializes by alias, so this restates the
77
+ contract; pass ``False`` for Python-name keys.
78
+ exclude_unset: Omit fields that were never explicitly set, narrowing the
79
+ output to what the server actually sent. **Opt-in, not the default**:
80
+ a variant that pins its tag in its own schema carries it as a
81
+ *defaulted* field, so on a locally constructed model this drops the tag
82
+ and the result no longer validates against the union alias.
83
+ exclude_defaults: Omit fields still holding their default value.
84
+ exclude_none: Omit fields whose value is ``None``. The round-trip-safe way
85
+ to suppress nulls -- it keeps defaulted discriminators.
86
+
87
+ Returns:
88
+ The model's fields keyed by wire alias, plus any unknown fields it preserved."""
89
+ return strip_unset(
90
+ self,
91
+ self.model_dump(
92
+ mode=mode,
93
+ by_alias=by_alias,
94
+ exclude_unset=exclude_unset,
95
+ exclude_defaults=exclude_defaults,
96
+ exclude_none=exclude_none,
97
+ ),
98
+ )
99
+
100
+ def to_json(
101
+ self,
102
+ *,
103
+ indent: int | None = 2,
104
+ by_alias: bool = True,
105
+ exclude_unset: bool = False,
106
+ exclude_defaults: bool = False,
107
+ exclude_none: bool = False,
108
+ ) -> str:
109
+ """Return this model as a JSON string in the shape the API exchanges.
110
+
111
+ Indented by default for logs and debugging, and otherwise identical in content
112
+ to :meth:`to_dict` -- ``json.loads(self.to_json()) == self.to_dict()``.
113
+
114
+ This is a convenience wrapper over ``model_dump``/``model_dump_json``; reach for
115
+ those directly when you need the options this deliberately does not surface
116
+ (``include``/``exclude``, ``context``, ``round_trip``, ``warnings``,
117
+ ``ensure_ascii``, ``serialize_as_any``, ``fallback``).
118
+
119
+ Built on :meth:`to_dict` (rather than delegating to ``model_dump_json`` directly)
120
+ so the pair can never drift apart -- including the ``OptionalNullable`` omission
121
+ documented there.
122
+
123
+ Args:
124
+ indent: Spaces of indentation; ``None`` emits compact JSON.
125
+ by_alias: Emit each field under its wire alias rather than its Python name.
126
+ exclude_unset: Omit fields that were never explicitly set. Carries the same
127
+ discriminator caveat as :meth:`to_dict`.
128
+ exclude_defaults: Omit fields still holding their default value.
129
+ exclude_none: Omit fields whose value is ``None``.
130
+
131
+ Returns:
132
+ The JSON text the API exchanges, indented unless ``indent`` is ``None``."""
133
+ return json.dumps(
134
+ self.to_dict(
135
+ mode="json",
136
+ by_alias=by_alias,
137
+ exclude_unset=exclude_unset,
138
+ exclude_defaults=exclude_defaults,
139
+ exclude_none=exclude_none,
140
+ ),
141
+ indent=indent,
142
+ ensure_ascii=False,
143
+ )
@@ -0,0 +1,130 @@
1
+ """The unset sentinel and the states it enables: omitted vs ``null`` vs a value.
2
+
3
+ :data:`UNSET` marks a field the caller never touched and :func:`strip_unset` is where "omitted" is
4
+ recovered at dump time, by dropping any key whose live value is still the sentinel. Two annotations
5
+ admit it, one per spelling a spec can declare: :data:`Optional` for a property that may be omitted,
6
+ :data:`OptionalNullable` for one that may be omitted *or* explicitly ``null``. See
7
+ ``docs/designs/optional-nullable-fields.md``.
8
+
9
+ :data:`Optional` is deliberately **not** ``typing.Optional``. Here it means ``T | UnsetType`` --
10
+ present or absent, with no ``None`` arm -- where ``typing.Optional[T]`` means ``T | None``. They
11
+ differ by exactly the arm that matters: a property the spec does not declare nullable must not be
12
+ able to reach the wire as ``null``, so passing ``None`` to one of these fields is a type error
13
+ rather than a value that serializes. Nothing in a generated SDK imports ``typing.Optional`` -- unions
14
+ are spelled with PEP 604 ``|`` -- so the two never appear in the same module."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any, ClassVar, Final, Literal, TypeVar, cast, final
19
+
20
+ from pydantic import BaseModel, GetCoreSchemaHandler
21
+ from pydantic_core import core_schema
22
+ from typing_extensions import TypeAliasType
23
+
24
+
25
+ @final
26
+ class UnsetType:
27
+ """Marks a field the caller never touched, distinct from one set to ``None``.
28
+
29
+ Only ever seen as a field's default -- never constructed or passed by a caller. Dumps
30
+ as plain ``None`` through pydantic's normal fast path (see :func:`strip_unset` for
31
+ where the tri-state is actually recovered). See
32
+ ``docs/designs/optional-nullable-fields.md``.
33
+
34
+ A singleton, and identity-stable through every channel that could duplicate it: ``UnsetType()``
35
+ returns the existing instance, ``copy`` and ``deepcopy`` return it unchanged, and unpickling
36
+ reduces back through the constructor. Without that, ``model_copy(deep=True)`` would deep-copy
37
+ the sentinel into a *second* object, and ``field is UNSET`` -- the natural way to test for
38
+ it -- would start returning ``False`` on the copy. :func:`strip_unset` uses ``isinstance`` and
39
+ so is immune either way, but callers should not have to know that. ``final`` is the same
40
+ guarantee stated statically: a subclass would inherit ``_instance`` already set, so its
41
+ constructor would hand back a ``UnsetType`` rather than an instance of itself."""
42
+
43
+ _instance: ClassVar[UnsetType | None] = None
44
+
45
+ def __new__(cls) -> UnsetType:
46
+ if cls._instance is None:
47
+ cls._instance = super().__new__(cls)
48
+ return cls._instance
49
+
50
+ def __bool__(self) -> Literal[False]:
51
+ return False
52
+
53
+ def __repr__(self) -> str:
54
+ return "UNSET"
55
+
56
+ def __copy__(self) -> UnsetType:
57
+ return self
58
+
59
+ def __deepcopy__(self, memo: dict[int, Any]) -> UnsetType:
60
+ return self
61
+
62
+ def __reduce__(self) -> tuple[type[UnsetType], tuple[()]]:
63
+ # Protocols >= 2 reduce via ``cls.__new__(cls)`` and reach the guard above by luck of
64
+ # protocol; 0 and 1 reconstruct through ``copyreg._reconstructor``, which calls
65
+ # ``object.__new__`` and bypasses ``__new__`` entirely -- measured, ``restored is UNSET``
66
+ # was ``False`` at both. Reducing to a plain call makes every protocol uniform.
67
+ return (UnsetType, ())
68
+
69
+ @classmethod
70
+ def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
71
+ return core_schema.is_instance_schema(
72
+ cls, serialization=core_schema.plain_serializer_function_ser_schema(lambda _: None)
73
+ )
74
+
75
+
76
+ UNSET: Final = UnsetType()
77
+
78
+ T = TypeVar("T")
79
+ Optional = TypeAliasType("Optional", T | UnsetType, type_params=(T,))
80
+ OptionalNullable = TypeAliasType("OptionalNullable", T | None | UnsetType, type_params=(T,))
81
+
82
+ DumpedT = TypeVar("DumpedT")
83
+
84
+
85
+ def strip_unset(value: object, dumped: DumpedT) -> DumpedT:
86
+ """Recursively drop any dumped key whose live field value is :class:`UnsetType`.
87
+
88
+ Walks ``value`` (the validated model, list or dict, pre-dump) alongside ``dumped`` (its
89
+ ``mode="json"`` dump) so a field that dumped as ``None`` only because it defaulted to
90
+ ``UNSET`` is omitted, while one explicitly set to ``None`` stays ``null``.
91
+
92
+ The mapping branch pairs the two by *key* rather than by position, because ``mode="json"``
93
+ renders a non-``str`` key as text (``{1: "x"}`` dumps as ``{"1": "x"}``): an entry the dump
94
+ renamed keeps its dumped form instead of misaligning the walk. ``list`` and ``dict`` are the
95
+ whole container set on purpose -- an array and a map are what a spec can express, so they are
96
+ the only shapes an emitted declared type nests, and they are the same two ``adapters.py``
97
+ descends. A ``tuple`` or ``set`` passes through like any other value.
98
+
99
+ Shape-preserving: a dict in, a dict out; a list in, a list out. The three ``cast`` calls
100
+ are that invariant, which mypy cannot verify through the rebuild -- they keep the
101
+ imprecision here rather than returning ``Any`` to every caller.
102
+
103
+ Args:
104
+ value: The validated model, list or dict, before dumping -- the side that still
105
+ holds the sentinel.
106
+ dumped: That same value's ``mode="json"`` dump, walked alongside it.
107
+
108
+ Returns:
109
+ ``dumped`` with every never-touched key removed, in the shape it arrived in."""
110
+ if isinstance(value, BaseModel):
111
+ result = dict(cast(Any, dumped))
112
+ for name, field in type(value).model_fields.items():
113
+ # ``dumped`` may be keyed by wire alias (the default) or by Python name
114
+ # (``to_dict(by_alias=False)``); prefer whichever spelling is actually there.
115
+ key = field.alias if field.alias is not None and field.alias in result else name
116
+ if key not in result:
117
+ continue
118
+ attr = getattr(value, name)
119
+ if isinstance(attr, UnsetType):
120
+ del result[key]
121
+ else:
122
+ result[key] = strip_unset(attr, result[key])
123
+ return cast(DumpedT, result)
124
+ if isinstance(value, list) and isinstance(dumped, list):
125
+ stripped = [strip_unset(item, d) for item, d in zip(value, dumped, strict=True)]
126
+ return cast(DumpedT, stripped)
127
+ if isinstance(value, dict) and isinstance(dumped, dict):
128
+ stripped_values = {key: strip_unset(value[key], item) if key in value else item for key, item in dumped.items()}
129
+ return cast(DumpedT, stripped_values)
130
+ return dumped
@@ -0,0 +1,157 @@
1
+ """Request parameters and the unresolved URL they are attached to.
2
+
3
+ A :class:`Param` is one query, header, path or form parameter: a key, the value the endpoint
4
+ received, and the adapter for the type it was declared as. The declared type decides how the value
5
+ reaches the wire -- a temporal alias carries its own serializer, so ``RFC1123DateTime`` and
6
+ ``UnixSecondsDateTime`` render the same ``datetime`` as a string and as an int -- and is what
7
+ dict-shaped input is validated against. How a *collection* explodes into keys is the one choice a
8
+ type cannot express, so it stays a field (:class:`SerializationFormat`)."""
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Sequence
13
+ from dataclasses import dataclass
14
+ from enum import Enum
15
+ from typing import Any, Final, Generic, TypeVar
16
+
17
+ from pydantic import TypeAdapter
18
+ from typing_extensions import TypeForm
19
+
20
+ from ._internal.subscripts import SubscriptOnly
21
+ from .adapters import adapter_for, validation_target
22
+
23
+ T = TypeVar("T")
24
+
25
+
26
+ class SerializationFormat(Enum):
27
+ """How a multi-valued *query or form* parameter explodes into keys.
28
+
29
+ Only those two destinations can honour it, because only they can repeat or subscript a key. A
30
+ path array folds into one comma-separated segment and a header array into one comma-separated
31
+ field value -- OpenAPI's default ``style: simple`` for both, and RFC 9110's own rule for a
32
+ repeated header -- so a path or header :class:`Param` carries this field and nothing reads it.
33
+ That is the price of one ``Param`` and one :func:`param` factory serving every location a
34
+ parameter can occupy, and it is a price rather than a defect: the alternative is a parameter
35
+ type per destination, and with it a generator branch deciding which to emit -- the conditional
36
+ emission ADR-0012 removed.
37
+
38
+ The remaining path and header styles -- ``label``, ``matrix``, ``explode: true`` -- are not
39
+ modelled, and cannot be from here: nothing below this module carries ``style`` or ``explode``,
40
+ so the default is the whole of what there is to render."""
41
+
42
+ INDEXED = "indexed"
43
+ """Example: variableName[0]=value1"""
44
+
45
+ UNINDEXED = "unindexed"
46
+ """Example: variableName[]=value1"""
47
+
48
+ PLAIN = "plain"
49
+ """Example: variableName=value1&variableName=value2"""
50
+
51
+ CSV = "csv"
52
+ """Example: variableName=value1,value2"""
53
+
54
+ TSV = "tsv"
55
+ r"""Example: variableName=value1\tvalue2"""
56
+
57
+ PSV = "psv"
58
+ """Example: variableName=value1|value2"""
59
+
60
+
61
+ @dataclass(frozen=True, slots=True)
62
+ class Param(Generic[T]):
63
+ """One request parameter, not yet flattened onto the wire.
64
+
65
+ ``T`` is the endpoint's declared type in full, dict-shaped companion included -- exactly what
66
+ ``param[...]`` accepts -- while the adapter targets :func:`validation_target` of it, which is
67
+ what the value is validated *into*. ``value`` is typed ``object`` all the same, deliberately:
68
+ every consumer holds a ``Param[Any]``, where a ``T``-typed field would read as ``Any`` and be
69
+ checked as nothing, so ``object`` leaves the adapter as the only route to the value. The
70
+ *resolved* adapter is held rather than the type expression it came from, so the one cache
71
+ lookup happens when the parameter is built rather than on every flatten.
72
+
73
+ Unlike a request body, a parameter is deliberately *not* serialized when it is built: query,
74
+ header and path parameters are merged per key with the API-wide ``global_*`` sets while the
75
+ request is being built, so they have to stay inspectable until then. ``wire_value``
76
+ (``_internal/wire.py``) performs the same validate-then-dump through this ``adapter`` at that
77
+ point, for every destination; each destination then decides only how the result is arranged --
78
+ into query pairs, path segments, or one header value.
79
+
80
+ Construct it through ``param[...]``, whose subscript is where ``T`` is bound.
81
+
82
+ Two parameters compare equal when key, value and format match *and* they share an adapter.
83
+ Adapters are cached per type by :func:`adapter_for`, so that holds for any cacheable type; an
84
+ unhashable type form bypasses the cache and would make two otherwise-identical parameters
85
+ unequal. Nothing depends on it -- resolution keys on ``key`` alone."""
86
+
87
+ key: str
88
+ value: object
89
+ adapter: TypeAdapter[T]
90
+ serialization_format: SerializationFormat = SerializationFormat.INDEXED
91
+
92
+
93
+ @dataclass(frozen=True, slots=True)
94
+ class _DeclaredParam(Generic[T]):
95
+ """A parameter's declared type, bound to its adapter; call it with the key and value.
96
+
97
+ Built by ``param[T]``; resolving the adapter there is what lets the parameter hold a typed
98
+ ``TypeAdapter[T]``."""
99
+
100
+ adapter: TypeAdapter[T]
101
+
102
+ def __call__(
103
+ self,
104
+ key: str,
105
+ value: T,
106
+ *,
107
+ serialization_format: SerializationFormat = SerializationFormat.INDEXED,
108
+ ) -> Param[T]:
109
+ return Param(key, value, self.adapter, serialization_format)
110
+
111
+
112
+ class _ParamFactory(SubscriptOnly):
113
+ """``param[T](key, value)`` -- name a parameter's declared type in the subscript.
114
+
115
+ Subscripted for the reason ``json_body`` documents in full: ``T`` is solved from the
116
+ subscript alone, before the value is compared against it, so ``param[bool]("array",
117
+ employee)`` is a build failure where a one-call ``param("array", employee, bool)`` would
118
+ have inference absorb the disagreement. And because the factory declares no ``__call__``,
119
+ *omitting* the declared type is a build failure too: ``param("array", True)`` is rejected
120
+ statically (``[operator]``); the runtime ``__call__`` inherited from :class:`SubscriptOnly` is
121
+ invisible to type checkers and exists only to turn that same mistake into a guided ``TypeError``.
122
+
123
+ The subscript is a ``TypeForm``, exactly as in ``json_body[...]``, so a runtime union alias
124
+ (``Person``, which is ``Employee | Boss``) binds ``T`` as precisely as a concrete class
125
+ does. It is the endpoint's declared type in full, companion included --
126
+ ``param[Person | PersonDict]`` -- and :func:`validation_target` keeps the companion out of
127
+ the adapter."""
128
+
129
+ factory_name = "param"
130
+ spelling = 'param[T](key, value), e.g. param[bool]("array", True)'
131
+
132
+ def __getitem__(self, declared: TypeForm[T]) -> _DeclaredParam[T]:
133
+ return _DeclaredParam(adapter_for(validation_target(declared)))
134
+
135
+
136
+ param: Final = _ParamFactory()
137
+
138
+
139
+ @dataclass(frozen=True, slots=True)
140
+ class UrlTemplate:
141
+ """An unresolved endpoint URL.
142
+
143
+ Holds a (possibly templated) base URL, the endpoint-relative path, and the
144
+ server variables that fill the base URL's ``{placeholder}`` tokens. The base
145
+ URL and the path are expanded separately when the request is built.
146
+
147
+ A server variable is *configuration* rather than a spec-declared parameter --
148
+ it is resolved from ``ServerConfig`` before any request exists -- but it fills
149
+ a placeholder exactly as a path parameter does, so it is carried as a
150
+ :class:`Param` and rendered by the same function. Naming its declared type is
151
+ what retires the hand ``.value`` unwrap an enum-typed variable used to need,
152
+ and what makes forgetting one a type error rather than a base URL reading
153
+ ``Region.EU``."""
154
+
155
+ base_url: str
156
+ path: str
157
+ variables: Sequence[Param[Any]] = ()