smithy-core 0.0.1__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.
- smithy_core/__init__.py +141 -0
- smithy_core/aio/__init__.py +2 -0
- smithy_core/aio/endpoints.py +21 -0
- smithy_core/aio/eventstream.py +233 -0
- smithy_core/aio/interfaces/__init__.py +200 -0
- smithy_core/aio/interfaces/eventstream.py +69 -0
- smithy_core/aio/interfaces/identity.py +22 -0
- smithy_core/aio/types.py +425 -0
- smithy_core/aio/utils.py +60 -0
- smithy_core/codecs.py +63 -0
- smithy_core/deserializers.py +282 -0
- smithy_core/documents.py +692 -0
- smithy_core/endpoints.py +91 -0
- smithy_core/exceptions.py +39 -0
- smithy_core/identity.py +31 -0
- smithy_core/interceptors.py +659 -0
- smithy_core/interfaces/__init__.py +228 -0
- smithy_core/interfaces/exceptions.py +13 -0
- smithy_core/interfaces/identity.py +35 -0
- smithy_core/interfaces/retries.py +119 -0
- smithy_core/prelude.py +120 -0
- smithy_core/py.typed +0 -0
- smithy_core/retries.py +249 -0
- smithy_core/rfc3986.py +120 -0
- smithy_core/schemas.py +320 -0
- smithy_core/serializers.py +444 -0
- smithy_core/shapes.py +127 -0
- smithy_core/traits.py +316 -0
- smithy_core/types.py +287 -0
- smithy_core/utils.py +262 -0
- smithy_core-0.0.1.dist-info/METADATA +6 -0
- smithy_core-0.0.1.dist-info/RECORD +34 -0
- smithy_core-0.0.1.dist-info/WHEEL +4 -0
- smithy_core-0.0.1.dist-info/licenses/NOTICE +1 -0
smithy_core/__init__.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from urllib.parse import urlunparse
|
|
8
|
+
|
|
9
|
+
from . import interfaces, rfc3986
|
|
10
|
+
from .exceptions import SmithyException
|
|
11
|
+
|
|
12
|
+
__version__: str = importlib.metadata.version("smithy-core")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HostType(Enum):
|
|
16
|
+
"""Enumeration of possible host types."""
|
|
17
|
+
|
|
18
|
+
IPv6 = "IPv6"
|
|
19
|
+
"""Host is an IPv6 address."""
|
|
20
|
+
|
|
21
|
+
IPv4 = "IPv4"
|
|
22
|
+
"""Host is an IPv4 address."""
|
|
23
|
+
|
|
24
|
+
DOMAIN = "DOMAIN"
|
|
25
|
+
"""Host type is a domain name."""
|
|
26
|
+
|
|
27
|
+
UNKNOWN = "UNKNOWN"
|
|
28
|
+
"""Host type is unknown."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(kw_only=True, frozen=True)
|
|
32
|
+
class URI(interfaces.URI):
|
|
33
|
+
"""Universal Resource Identifier, target location for a :py:class:`HTTPRequest`."""
|
|
34
|
+
|
|
35
|
+
scheme: str = "https"
|
|
36
|
+
"""For example ``http`` or ``https``."""
|
|
37
|
+
|
|
38
|
+
username: str | None = None
|
|
39
|
+
"""Username part of the userinfo URI component."""
|
|
40
|
+
|
|
41
|
+
password: str | None = None
|
|
42
|
+
"""Password part of the userinfo URI component."""
|
|
43
|
+
|
|
44
|
+
host: str
|
|
45
|
+
"""The hostname, for example ``amazonaws.com``."""
|
|
46
|
+
|
|
47
|
+
port: int | None = None
|
|
48
|
+
"""An explicit port number."""
|
|
49
|
+
|
|
50
|
+
path: str | None = None
|
|
51
|
+
"""Path component of the URI."""
|
|
52
|
+
|
|
53
|
+
query: str | None = None
|
|
54
|
+
"""Query component of the URI as string."""
|
|
55
|
+
|
|
56
|
+
fragment: str | None = None
|
|
57
|
+
"""Part of the URI specification, but may not be transmitted by a client."""
|
|
58
|
+
|
|
59
|
+
def __post_init__(self) -> None:
|
|
60
|
+
"""Validate host component."""
|
|
61
|
+
if not rfc3986.HOST_MATCHER.match(self.host) and not rfc3986.IPv6_MATCHER.match(
|
|
62
|
+
f"[{self.host}]"
|
|
63
|
+
):
|
|
64
|
+
raise SmithyException(f"Invalid host: {self.host}")
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def netloc(self) -> str:
|
|
68
|
+
"""Construct netloc string in format ``{username}:{password}@{host}:{port}``
|
|
69
|
+
|
|
70
|
+
``username``, ``password``, and ``port`` are only included if set. ``password``
|
|
71
|
+
is ignored, unless ``username`` is also set. Add square brackets around the host
|
|
72
|
+
if it is a valid IPv6 endpoint URI per :rfc:`3986#section-3.2.2`.
|
|
73
|
+
"""
|
|
74
|
+
return self._netloc
|
|
75
|
+
|
|
76
|
+
# cached_property does NOT behave like property, it actually allows for setting.
|
|
77
|
+
# Therefore we need a layer of indirection.
|
|
78
|
+
@cached_property
|
|
79
|
+
def _netloc(self) -> str:
|
|
80
|
+
if self.username is not None:
|
|
81
|
+
password = "" if self.password is None else f":{self.password}"
|
|
82
|
+
userinfo = f"{self.username}{password}@"
|
|
83
|
+
else:
|
|
84
|
+
userinfo = ""
|
|
85
|
+
|
|
86
|
+
if self.port is not None:
|
|
87
|
+
port = f":{self.port}"
|
|
88
|
+
else:
|
|
89
|
+
port = ""
|
|
90
|
+
|
|
91
|
+
if self.host_type == HostType.IPv6:
|
|
92
|
+
host = f"[{self.host}]"
|
|
93
|
+
else:
|
|
94
|
+
host = self.host
|
|
95
|
+
|
|
96
|
+
return f"{userinfo}{host}{port}"
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def host_type(self) -> HostType:
|
|
100
|
+
"""Return the type of host."""
|
|
101
|
+
return self._host_type
|
|
102
|
+
|
|
103
|
+
@cached_property
|
|
104
|
+
def _host_type(self) -> HostType:
|
|
105
|
+
if rfc3986.IPv6_MATCHER.match(f"[{self.host}]"):
|
|
106
|
+
return HostType.IPv6
|
|
107
|
+
if rfc3986.IPv4_MATCHER.match(self.host):
|
|
108
|
+
return HostType.IPv4
|
|
109
|
+
if rfc3986.HOST_MATCHER.match(self.host):
|
|
110
|
+
return HostType.DOMAIN
|
|
111
|
+
return HostType.UNKNOWN
|
|
112
|
+
|
|
113
|
+
def build(self) -> str:
|
|
114
|
+
"""Construct URI string representation.
|
|
115
|
+
|
|
116
|
+
Validate host. Returns a string of the form
|
|
117
|
+
``{scheme}://{username}:{password}@{host}:{port}{path}?{query}#{fragment}``
|
|
118
|
+
"""
|
|
119
|
+
components = (
|
|
120
|
+
self.scheme,
|
|
121
|
+
self.netloc,
|
|
122
|
+
self.path or "",
|
|
123
|
+
"", # params
|
|
124
|
+
self.query,
|
|
125
|
+
self.fragment,
|
|
126
|
+
)
|
|
127
|
+
return urlunparse(components)
|
|
128
|
+
|
|
129
|
+
def __eq__(self, other: object) -> bool:
|
|
130
|
+
if not isinstance(other, URI):
|
|
131
|
+
return False
|
|
132
|
+
return (
|
|
133
|
+
self.scheme == other.scheme
|
|
134
|
+
and self.host == other.host
|
|
135
|
+
and self.port == other.port
|
|
136
|
+
and self.path == other.path
|
|
137
|
+
and self.query == other.query
|
|
138
|
+
and self.username == other.username
|
|
139
|
+
and self.password == other.password
|
|
140
|
+
and self.fragment == other.fragment
|
|
141
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..endpoints import Endpoint, EndpointResolverParams, resolve_static_uri
|
|
6
|
+
from ..exceptions import EndpointResolutionError
|
|
7
|
+
from ..interfaces import Endpoint as _Endpoint
|
|
8
|
+
from .interfaces import EndpointResolver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StaticEndpointResolver(EndpointResolver):
|
|
12
|
+
"""A basic endpoint resolver that forwards a static URI."""
|
|
13
|
+
|
|
14
|
+
async def resolve_endpoint(self, params: EndpointResolverParams[Any]) -> _Endpoint:
|
|
15
|
+
static_uri = resolve_static_uri(params)
|
|
16
|
+
if static_uri is None:
|
|
17
|
+
raise EndpointResolutionError(
|
|
18
|
+
"Unable to resolve endpoint: endpoint_uri is required"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return Endpoint(uri=static_uri)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from asyncio import Future
|
|
4
|
+
from typing import Any, Self
|
|
5
|
+
|
|
6
|
+
from ..deserializers import DeserializeableShape
|
|
7
|
+
from ..serializers import SerializeableShape
|
|
8
|
+
from .interfaces.eventstream import EventPublisher, EventReceiver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DuplexEventStream[
|
|
12
|
+
IE: SerializeableShape,
|
|
13
|
+
OE: DeserializeableShape,
|
|
14
|
+
O: DeserializeableShape,
|
|
15
|
+
]:
|
|
16
|
+
"""An event stream that both sends and receives messages.
|
|
17
|
+
|
|
18
|
+
To ensure that streams are closed upon exiting, this class may be used as an async
|
|
19
|
+
context manager.
|
|
20
|
+
|
|
21
|
+
.. code-block:: python
|
|
22
|
+
|
|
23
|
+
async def main():
|
|
24
|
+
client = ChatClient()
|
|
25
|
+
input = StreamMessagesInput(chat_room="aws-python-sdk", username="hunter7")
|
|
26
|
+
|
|
27
|
+
async with client.stream_messages(input=input) as stream:
|
|
28
|
+
stream.input_stream.send(
|
|
29
|
+
MessageStreamMessage("Chat logger starting up.")
|
|
30
|
+
)
|
|
31
|
+
response_task = asyncio.create_task(handle_output(stream))
|
|
32
|
+
stream.input_stream.send(MessageStreamMessage("Chat logger active."))
|
|
33
|
+
await response_handler
|
|
34
|
+
|
|
35
|
+
async def handle_output(stream: EventStream) -> None:
|
|
36
|
+
_, output_stream = await stream.await_output()
|
|
37
|
+
async for event in output_stream:
|
|
38
|
+
match event:
|
|
39
|
+
case MessageStreamMessage():
|
|
40
|
+
print(event.value)
|
|
41
|
+
case MessageStreamShutdown():
|
|
42
|
+
return
|
|
43
|
+
case _:
|
|
44
|
+
stream.input_stream.send(
|
|
45
|
+
MessageStreamMessage(
|
|
46
|
+
"Unknown message type received. Shutting down."
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
return
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
input_stream: EventPublisher[IE]
|
|
53
|
+
"""An event stream that sends events to the service."""
|
|
54
|
+
|
|
55
|
+
output_stream: EventReceiver[OE] | None = None
|
|
56
|
+
"""An event stream that receives events from the service.
|
|
57
|
+
|
|
58
|
+
This value may be None until ``await_output`` has been called.
|
|
59
|
+
|
|
60
|
+
This value will also be None if the operation has no output stream.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
output: O | None = None
|
|
64
|
+
"""The initial response from the service.
|
|
65
|
+
|
|
66
|
+
This value may be None until ``await_output`` has been called.
|
|
67
|
+
|
|
68
|
+
This may include context necessary to interpret output events or prepare
|
|
69
|
+
input events. It will always be available before any events.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
*,
|
|
75
|
+
input_stream: EventPublisher[IE],
|
|
76
|
+
output_future: Future[tuple[O, EventReceiver[OE]]],
|
|
77
|
+
) -> None:
|
|
78
|
+
self.input_stream = input_stream
|
|
79
|
+
self._output_future = output_future
|
|
80
|
+
|
|
81
|
+
async def await_output(self) -> tuple[O, EventReceiver[OE]]:
|
|
82
|
+
"""Await the operation's output.
|
|
83
|
+
|
|
84
|
+
The EventStream will be returned as soon as the input stream is ready to
|
|
85
|
+
receive events, which may be before the initial response has been received
|
|
86
|
+
and the service is ready to send events.
|
|
87
|
+
|
|
88
|
+
Awaiting this method will wait until the initial response was received and the
|
|
89
|
+
service is ready to send events. The initial response and output stream will
|
|
90
|
+
be returned by this operation and also cached in ``response`` and
|
|
91
|
+
``output_stream``, respectively.
|
|
92
|
+
|
|
93
|
+
The default implementation of this method performs the caching behavior,
|
|
94
|
+
delegating to the abstract ``_await_output`` method to actually retrieve the
|
|
95
|
+
initial response and output stream.
|
|
96
|
+
|
|
97
|
+
:returns: A tuple containing the initial response and output stream. If the
|
|
98
|
+
operation has no output stream, the second value will be None.
|
|
99
|
+
"""
|
|
100
|
+
self.output, self.output_stream = await self._output_future
|
|
101
|
+
return self.output, self.output_stream
|
|
102
|
+
|
|
103
|
+
async def close(self) -> None:
|
|
104
|
+
"""Closes the event stream.
|
|
105
|
+
|
|
106
|
+
This closes both the input and output streams.
|
|
107
|
+
"""
|
|
108
|
+
if self.output_stream is None:
|
|
109
|
+
_, self.output_stream = await self.await_output()
|
|
110
|
+
|
|
111
|
+
await self.input_stream.close()
|
|
112
|
+
await self.output_stream.close()
|
|
113
|
+
|
|
114
|
+
async def __aenter__(self) -> Self:
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any):
|
|
118
|
+
await self.close()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class InputEventStream[IE: SerializeableShape, O]:
|
|
122
|
+
"""An event stream that streams messages to the service.
|
|
123
|
+
|
|
124
|
+
To ensure that streams are closed upon exiting, this class may be used as an async
|
|
125
|
+
context manager.
|
|
126
|
+
|
|
127
|
+
.. code-block:: python
|
|
128
|
+
|
|
129
|
+
async def main():
|
|
130
|
+
client = ChatClient()
|
|
131
|
+
input = PublishMessagesInput(chat_room="aws-python-sdk", username="hunter7")
|
|
132
|
+
|
|
133
|
+
async with client.publish_messages(input=input) as stream:
|
|
134
|
+
stream.input_stream.send(
|
|
135
|
+
MessageStreamMessage("High severity ticket alert!")
|
|
136
|
+
)
|
|
137
|
+
await stream.await_output()
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
input_stream: EventPublisher[IE]
|
|
141
|
+
"""An event stream that sends events to the service."""
|
|
142
|
+
|
|
143
|
+
output: O | None = None
|
|
144
|
+
"""The initial response from the service.
|
|
145
|
+
|
|
146
|
+
This value may be None until ``await_output`` has been called.
|
|
147
|
+
|
|
148
|
+
This may include context necessary to interpret output events or prepare
|
|
149
|
+
input events. It will always be available before any events.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def __init__(
|
|
153
|
+
self,
|
|
154
|
+
*,
|
|
155
|
+
input_stream: EventPublisher[IE],
|
|
156
|
+
output_future: Future[O],
|
|
157
|
+
) -> None:
|
|
158
|
+
self.input_stream = input_stream
|
|
159
|
+
self._output_future = output_future
|
|
160
|
+
|
|
161
|
+
async def await_output(self) -> O:
|
|
162
|
+
"""Await the operation's initial response.
|
|
163
|
+
|
|
164
|
+
The EventStream will be returned as soon as the input stream is ready to receive
|
|
165
|
+
events, which may be before the initial response has been received and the
|
|
166
|
+
service is ready to send events.
|
|
167
|
+
|
|
168
|
+
Awaiting this method will wait until the initial response was received.
|
|
169
|
+
|
|
170
|
+
:returns: The service's initial response.
|
|
171
|
+
"""
|
|
172
|
+
if self.output is None:
|
|
173
|
+
self.output = await self._output_future
|
|
174
|
+
return self.output
|
|
175
|
+
|
|
176
|
+
async def close(self) -> None:
|
|
177
|
+
"""Closes the event stream."""
|
|
178
|
+
await self.input_stream.close()
|
|
179
|
+
|
|
180
|
+
async def __aenter__(self) -> Self:
|
|
181
|
+
return self
|
|
182
|
+
|
|
183
|
+
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any):
|
|
184
|
+
await self.close()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class OutputEventStream[OE: DeserializeableShape, O: DeserializeableShape]:
|
|
188
|
+
"""An event stream that streams messages from the service.
|
|
189
|
+
|
|
190
|
+
To ensure that streams are closed upon exiting, this class may be used as an async
|
|
191
|
+
context manager.
|
|
192
|
+
|
|
193
|
+
.. code-block:: python
|
|
194
|
+
|
|
195
|
+
async def main():
|
|
196
|
+
client = ChatClient()
|
|
197
|
+
input = ReceiveMessagesInput(chat_room="aws-python-sdk")
|
|
198
|
+
|
|
199
|
+
async with client.receive_messages(input=input) as stream:
|
|
200
|
+
async for event in stream.output_stream:
|
|
201
|
+
match event:
|
|
202
|
+
case MessageStreamMessage():
|
|
203
|
+
print(event.value)
|
|
204
|
+
case _:
|
|
205
|
+
return
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
output_stream: EventReceiver[OE]
|
|
209
|
+
"""An event stream that receives events from the service.
|
|
210
|
+
|
|
211
|
+
This value will also be None if the operation has no output stream.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
output: O
|
|
215
|
+
"""The initial response from the service.
|
|
216
|
+
|
|
217
|
+
This may include context necessary to interpret output events or prepare input
|
|
218
|
+
events. It will always be available before any events.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
def __init__(self, output_stream: EventReceiver[OE], output: O) -> None:
|
|
222
|
+
self.output_stream = output_stream
|
|
223
|
+
self.output = output
|
|
224
|
+
|
|
225
|
+
async def close(self) -> None:
|
|
226
|
+
"""Closes the event stream."""
|
|
227
|
+
await self.output_stream.close()
|
|
228
|
+
|
|
229
|
+
async def __aenter__(self) -> Self:
|
|
230
|
+
return self
|
|
231
|
+
|
|
232
|
+
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any):
|
|
233
|
+
await self.close()
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from collections.abc import AsyncIterable, Callable
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
from ...documents import TypeRegistry
|
|
7
|
+
from ...endpoints import EndpointResolverParams
|
|
8
|
+
from ...exceptions import UnsupportedStreamException
|
|
9
|
+
from ...interfaces import Endpoint, TypedProperties, URI
|
|
10
|
+
from ...interfaces import StreamingBlob as SyncStreamingBlob
|
|
11
|
+
from .eventstream import EventPublisher, EventReceiver
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ...deserializers import DeserializeableShape, ShapeDeserializer
|
|
15
|
+
from ...schemas import APIOperation
|
|
16
|
+
from ...serializers import SerializeableShape
|
|
17
|
+
from ...shapes import ShapeID
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@runtime_checkable
|
|
21
|
+
class AsyncByteStream(Protocol):
|
|
22
|
+
"""A file-like object with an async read method."""
|
|
23
|
+
|
|
24
|
+
async def read(self, size: int = -1) -> bytes: ...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@runtime_checkable
|
|
28
|
+
class AsyncWriter(Protocol):
|
|
29
|
+
"""An object with an async write method."""
|
|
30
|
+
|
|
31
|
+
async def write(self, data: bytes) -> None: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# A union of all acceptable streaming blob types. Deserialized payloads will
|
|
35
|
+
# always return a ByteStream, or AsyncByteStream if async is enabled.
|
|
36
|
+
type StreamingBlob = SyncStreamingBlob | AsyncByteStream | AsyncIterable[bytes]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Request(Protocol):
|
|
40
|
+
"""Protocol-agnostic representation of a request."""
|
|
41
|
+
|
|
42
|
+
destination: URI
|
|
43
|
+
"""The URI where the request should be sent to."""
|
|
44
|
+
|
|
45
|
+
body: StreamingBlob = b""
|
|
46
|
+
"""The request payload."""
|
|
47
|
+
|
|
48
|
+
async def consume_body_async(self) -> bytes:
|
|
49
|
+
"""Iterate over request body and return as bytes."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def consume_body(self) -> bytes:
|
|
53
|
+
"""Iterate over request body and return as bytes."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Response(Protocol):
|
|
58
|
+
"""Protocol-agnostic representation of a response."""
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def body(self) -> StreamingBlob:
|
|
62
|
+
"""The response payload as iterable of chunks of bytes."""
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
async def consume_body_async(self) -> bytes:
|
|
66
|
+
"""Iterate over response body and return as bytes."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
def consume_body(self) -> bytes:
|
|
70
|
+
"""Iterate over request body and return as bytes."""
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class EndpointResolver(Protocol):
|
|
75
|
+
"""Resolves an operation's endpoint based given parameters."""
|
|
76
|
+
|
|
77
|
+
async def resolve_endpoint(self, params: EndpointResolverParams[Any]) -> Endpoint:
|
|
78
|
+
"""Resolve an endpoint for the given operation.
|
|
79
|
+
|
|
80
|
+
:param params: The parameters available to resolve the endpoint.
|
|
81
|
+
"""
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ClientTransport[I: Request, O: Response](Protocol):
|
|
86
|
+
"""Protocol-agnostic representation of a client tranport (e.g. an HTTP client)."""
|
|
87
|
+
|
|
88
|
+
async def send(self, request: I) -> O:
|
|
89
|
+
"""Send a request over the transport and receive the response."""
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ClientProtocol[I: Request, O: Response](Protocol):
|
|
94
|
+
"""A protocol used by a client to communicate with a server."""
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def id(self) -> "ShapeID":
|
|
98
|
+
"""The ID of the protocol."""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
def serialize_request[
|
|
102
|
+
OperationInput: "SerializeableShape",
|
|
103
|
+
OperationOutput: "DeserializeableShape",
|
|
104
|
+
](
|
|
105
|
+
self,
|
|
106
|
+
*,
|
|
107
|
+
operation: "APIOperation[OperationInput, OperationOutput]",
|
|
108
|
+
input: OperationInput,
|
|
109
|
+
endpoint: URI,
|
|
110
|
+
context: TypedProperties,
|
|
111
|
+
) -> I:
|
|
112
|
+
"""Serialize an operation input into a transport request.
|
|
113
|
+
|
|
114
|
+
:param operation: The operation whose request is being serialized.
|
|
115
|
+
:param input: The input shape to be serialized.
|
|
116
|
+
:param endpoint: The base endpoint to serialize.
|
|
117
|
+
:param context: A context bag for the request.
|
|
118
|
+
"""
|
|
119
|
+
...
|
|
120
|
+
|
|
121
|
+
def set_service_endpoint(
|
|
122
|
+
self,
|
|
123
|
+
*,
|
|
124
|
+
request: I,
|
|
125
|
+
endpoint: Endpoint,
|
|
126
|
+
) -> I:
|
|
127
|
+
"""Update the endpoint of a transport request.
|
|
128
|
+
|
|
129
|
+
:param request: The request whose endpoint should be updated.
|
|
130
|
+
:param endpoint: The endpoint to set on the request.
|
|
131
|
+
"""
|
|
132
|
+
...
|
|
133
|
+
|
|
134
|
+
async def deserialize_response[
|
|
135
|
+
OperationInput: "SerializeableShape",
|
|
136
|
+
OperationOutput: "DeserializeableShape",
|
|
137
|
+
](
|
|
138
|
+
self,
|
|
139
|
+
*,
|
|
140
|
+
operation: "APIOperation[OperationInput, OperationOutput]",
|
|
141
|
+
request: I,
|
|
142
|
+
response: O,
|
|
143
|
+
error_registry: TypeRegistry,
|
|
144
|
+
context: TypedProperties,
|
|
145
|
+
) -> OperationOutput:
|
|
146
|
+
"""Deserializes the output from the tranport response or throws an exception.
|
|
147
|
+
|
|
148
|
+
:param operation: The operation whose response is being deserialized.
|
|
149
|
+
:param request: The transport request that was sent for this response.
|
|
150
|
+
:param response: The response to deserialize.
|
|
151
|
+
:param error_registry: A TypeRegistry used to deserialize errors.
|
|
152
|
+
:param context: A context bag for the request.
|
|
153
|
+
"""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
def create_event_publisher[
|
|
157
|
+
OperationInput: "SerializeableShape",
|
|
158
|
+
OperationOutput: "DeserializeableShape",
|
|
159
|
+
Event: "SerializeableShape",
|
|
160
|
+
](
|
|
161
|
+
self,
|
|
162
|
+
*,
|
|
163
|
+
operation: "APIOperation[OperationInput, OperationOutput]",
|
|
164
|
+
request: I,
|
|
165
|
+
event_type: type[Event],
|
|
166
|
+
context: TypedProperties,
|
|
167
|
+
) -> EventPublisher[Event]:
|
|
168
|
+
"""Creates an event publisher for a protocol event stream.
|
|
169
|
+
|
|
170
|
+
:param operation: The event stream operation.
|
|
171
|
+
:param request: The transport request that was sent for this stream.
|
|
172
|
+
:param event_type: The type of event to publish.
|
|
173
|
+
:param context: A context bag for the request.
|
|
174
|
+
"""
|
|
175
|
+
raise UnsupportedStreamException()
|
|
176
|
+
|
|
177
|
+
def create_event_receiver[
|
|
178
|
+
OperationInput: "SerializeableShape",
|
|
179
|
+
OperationOutput: "DeserializeableShape",
|
|
180
|
+
Event: "DeserializeableShape",
|
|
181
|
+
](
|
|
182
|
+
self,
|
|
183
|
+
*,
|
|
184
|
+
operation: "APIOperation[OperationInput, OperationOutput]",
|
|
185
|
+
request: I,
|
|
186
|
+
response: O,
|
|
187
|
+
event_type: type[Event],
|
|
188
|
+
event_deserializer: Callable[["ShapeDeserializer"], Event],
|
|
189
|
+
context: TypedProperties,
|
|
190
|
+
) -> EventReceiver[Event]:
|
|
191
|
+
"""Creates an event receiver for a protocol event stream.
|
|
192
|
+
|
|
193
|
+
:param operation: The event stream operation.
|
|
194
|
+
:param request: The transport request that was sent for this stream.
|
|
195
|
+
:param response: The transport response that was received for this stream.
|
|
196
|
+
:param event_type: The type of event to publish.
|
|
197
|
+
:param event_deserializer: The deserializer to be used to deserialize events.
|
|
198
|
+
:param context: A context bag for the request.
|
|
199
|
+
"""
|
|
200
|
+
raise UnsupportedStreamException()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from typing import Any, Protocol, Self
|
|
4
|
+
|
|
5
|
+
from ...deserializers import DeserializeableShape
|
|
6
|
+
from ...serializers import SerializeableShape
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EventPublisher[E: SerializeableShape](Protocol):
|
|
10
|
+
"""Asynchronously sends events to a service.
|
|
11
|
+
|
|
12
|
+
This may be used as a context manager to ensure the stream is closed before exiting.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
async def send(self, event: E) -> None:
|
|
16
|
+
"""Sends an event to the service.
|
|
17
|
+
|
|
18
|
+
:param event: The event to send.
|
|
19
|
+
"""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
async def close(self) -> None:
|
|
23
|
+
"""Closes the event stream."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
async def __aenter__(self) -> Self:
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any):
|
|
30
|
+
await self.close()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EventReceiver[E: DeserializeableShape](Protocol):
|
|
34
|
+
"""Asynchronously receives events from a service.
|
|
35
|
+
|
|
36
|
+
Events may be received via the ``receive`` method or by using this class as
|
|
37
|
+
an async iterable.
|
|
38
|
+
|
|
39
|
+
This may also be used as a context manager to ensure the stream is closed before
|
|
40
|
+
exiting.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
async def receive(self) -> E | None:
|
|
44
|
+
"""Receive a single event from the service.
|
|
45
|
+
|
|
46
|
+
:returns: An event or None. None indicates that no more events will be sent by
|
|
47
|
+
the service.
|
|
48
|
+
"""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
async def close(self) -> None:
|
|
52
|
+
"""Closes the event stream."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
async def __anext__(self) -> E:
|
|
56
|
+
result = await self.receive()
|
|
57
|
+
if result is None:
|
|
58
|
+
await self.close()
|
|
59
|
+
raise StopAsyncIteration
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
def __aiter__(self) -> Self:
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
async def __enter__(self) -> Self:
|
|
66
|
+
return self
|
|
67
|
+
|
|
68
|
+
async def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any):
|
|
69
|
+
await self.close()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from ...interfaces.identity import IdentityPropertiesType_contra, IdentityType_cov
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IdentityResolver(Protocol[IdentityType_cov, IdentityPropertiesType_contra]):
|
|
9
|
+
"""Used to load a user's `Identity` from a given source.
|
|
10
|
+
|
|
11
|
+
Each `Identity` may have one or more resolver implementations.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
async def get_identity(
|
|
15
|
+
self, *, identity_properties: IdentityPropertiesType_contra
|
|
16
|
+
) -> IdentityType_cov:
|
|
17
|
+
"""Load the user's identity from this resolver.
|
|
18
|
+
|
|
19
|
+
:param identity_properties: Properties used to help determine the identity to
|
|
20
|
+
return.
|
|
21
|
+
"""
|
|
22
|
+
...
|