capo-lambda-web 0.2.0__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.
- capo_lambda_web/__init__.py +71 -0
- capo_lambda_web/_async.py +25 -0
- capo_lambda_web/_auth/_identity.py +16 -0
- capo_lambda_web/_auth/_providers.py +886 -0
- capo_lambda_web/_auth/_signers.py +121 -0
- capo_lambda_web/_auth/_sigv4.py +579 -0
- capo_lambda_web/_auth/_zapros_handler.py +80 -0
- capo_lambda_web/_body.py +221 -0
- capo_lambda_web/_iter.py +113 -0
- capo_lambda_web/_operations/lambda_web/create_web_function.py +184 -0
- capo_lambda_web/_operations/lambda_web/create_web_function_endpoint.py +194 -0
- capo_lambda_web/_operations/lambda_web/create_web_function_revision.py +188 -0
- capo_lambda_web/_operations/lambda_web/delete_resource_policy.py +147 -0
- capo_lambda_web/_operations/lambda_web/delete_web_function.py +145 -0
- capo_lambda_web/_operations/lambda_web/delete_web_function_endpoint.py +149 -0
- capo_lambda_web/_operations/lambda_web/delete_web_function_revision.py +149 -0
- capo_lambda_web/_operations/lambda_web/get_resource_policy.py +165 -0
- capo_lambda_web/_operations/lambda_web/get_web_account_settings.py +155 -0
- capo_lambda_web/_operations/lambda_web/get_web_function.py +171 -0
- capo_lambda_web/_operations/lambda_web/get_web_function_endpoint.py +180 -0
- capo_lambda_web/_operations/lambda_web/get_web_function_revision.py +174 -0
- capo_lambda_web/_operations/lambda_web/list_tags.py +164 -0
- capo_lambda_web/_operations/lambda_web/list_web_function_endpoints.py +176 -0
- capo_lambda_web/_operations/lambda_web/list_web_function_revisions.py +176 -0
- capo_lambda_web/_operations/lambda_web/list_web_functions.py +168 -0
- capo_lambda_web/_operations/lambda_web/put_resource_policy.py +179 -0
- capo_lambda_web/_operations/lambda_web/tag_resource.py +155 -0
- capo_lambda_web/_operations/lambda_web/untag_resource.py +148 -0
- capo_lambda_web/_operations/lambda_web/update_web_function_endpoint.py +196 -0
- capo_lambda_web/_pagination.py +21 -0
- capo_lambda_web/_protocol/__init__.py +1 -0
- capo_lambda_web/_protocol/errors.py +93 -0
- capo_lambda_web/_protocol/eventstream.py +240 -0
- capo_lambda_web/_protocol/serialize.py +47 -0
- capo_lambda_web/_protocol/xml.py +33 -0
- capo_lambda_web/_resources/lambda_web/web_function.py +477 -0
- capo_lambda_web/_rule_engine/__init__.py +0 -0
- capo_lambda_web/_rule_engine/_aws_partition.py +160 -0
- capo_lambda_web/_rule_engine/_endpoint_rule_set.py +135 -0
- capo_lambda_web/_rule_engine/_endpoint_runtime.py +389 -0
- capo_lambda_web/_services/_aws_config.py +160 -0
- capo_lambda_web/_services/_pipeline.py +264 -0
- capo_lambda_web/_services/async_lambda_web.py +1394 -0
- capo_lambda_web/_services/lambda_web.py +1374 -0
- capo_lambda_web/errors/__init__.py +36 -0
- capo_lambda_web/errors/_base.py +94 -0
- capo_lambda_web/errors/access_denied_exception.py +48 -0
- capo_lambda_web/errors/conflict_exception.py +58 -0
- capo_lambda_web/errors/internal_server_exception.py +47 -0
- capo_lambda_web/errors/resource_not_found_exception.py +60 -0
- capo_lambda_web/errors/service_quota_exceeded_exception.py +74 -0
- capo_lambda_web/errors/throttling_exception.py +60 -0
- capo_lambda_web/errors/validation_exception.py +46 -0
- capo_lambda_web/py.typed +0 -0
- capo_lambda_web/types/_prelude/blob.py +12 -0
- capo_lambda_web/types/_prelude/timestamp.py +17 -0
- capo_lambda_web/types/account_quotas.py +47 -0
- capo_lambda_web/types/account_usage.py +26 -0
- capo_lambda_web/types/application_log_level.py +22 -0
- capo_lambda_web/types/auth_type.py +18 -0
- capo_lambda_web/types/auto_deployment_mode.py +18 -0
- capo_lambda_web/types/build_config.py +55 -0
- capo_lambda_web/types/code_config.py +37 -0
- capo_lambda_web/types/create_web_function_endpoint_request.py +164 -0
- capo_lambda_web/types/create_web_function_endpoint_response.py +291 -0
- capo_lambda_web/types/create_web_function_request.py +72 -0
- capo_lambda_web/types/create_web_function_response.py +144 -0
- capo_lambda_web/types/create_web_function_revision_request.py +76 -0
- capo_lambda_web/types/create_web_function_revision_response.py +157 -0
- capo_lambda_web/types/date_time.py +20 -0
- capo_lambda_web/types/delete_resource_policy_request.py +29 -0
- capo_lambda_web/types/delete_web_function_endpoint_request.py +27 -0
- capo_lambda_web/types/delete_web_function_request.py +24 -0
- capo_lambda_web/types/delete_web_function_revision_request.py +27 -0
- capo_lambda_web/types/description.py +5 -0
- capo_lambda_web/types/domain_name.py +5 -0
- capo_lambda_web/types/endpoint_arn.py +5 -0
- capo_lambda_web/types/endpoint_config.py +134 -0
- capo_lambda_web/types/endpoint_name.py +5 -0
- capo_lambda_web/types/endpoint_state.py +20 -0
- capo_lambda_web/types/endpoint_type.py +19 -0
- capo_lambda_web/types/endpoint_update_status.py +19 -0
- capo_lambda_web/types/environment_variables.py +22 -0
- capo_lambda_web/types/filter.py +46 -0
- capo_lambda_web/types/filter_list.py +29 -0
- capo_lambda_web/types/filter_value_list.py +14 -0
- capo_lambda_web/types/function_arn.py +5 -0
- capo_lambda_web/types/function_endpoint_summary.py +249 -0
- capo_lambda_web/types/function_endpoint_summary_list.py +33 -0
- capo_lambda_web/types/function_name.py +5 -0
- capo_lambda_web/types/function_revision_summary.py +83 -0
- capo_lambda_web/types/function_revision_summary_list.py +33 -0
- capo_lambda_web/types/function_state.py +20 -0
- capo_lambda_web/types/function_summary.py +91 -0
- capo_lambda_web/types/function_summary_list.py +31 -0
- capo_lambda_web/types/get_resource_policy_request.py +24 -0
- capo_lambda_web/types/get_resource_policy_response.py +39 -0
- capo_lambda_web/types/get_web_account_settings_request.py +18 -0
- capo_lambda_web/types/get_web_account_settings_response.py +59 -0
- capo_lambda_web/types/get_web_function_endpoint_request.py +27 -0
- capo_lambda_web/types/get_web_function_endpoint_response.py +285 -0
- capo_lambda_web/types/get_web_function_request.py +24 -0
- capo_lambda_web/types/get_web_function_response.py +91 -0
- capo_lambda_web/types/get_web_function_revision_request.py +27 -0
- capo_lambda_web/types/get_web_function_revision_response.py +155 -0
- capo_lambda_web/types/kms_key_arn.py +5 -0
- capo_lambda_web/types/list_tags_request.py +24 -0
- capo_lambda_web/types/list_tags_response.py +32 -0
- capo_lambda_web/types/list_web_function_endpoints_request.py +54 -0
- capo_lambda_web/types/list_web_function_endpoints_response.py +52 -0
- capo_lambda_web/types/list_web_function_revisions_request.py +54 -0
- capo_lambda_web/types/list_web_function_revisions_response.py +52 -0
- capo_lambda_web/types/list_web_functions_request.py +51 -0
- capo_lambda_web/types/list_web_functions_response.py +46 -0
- capo_lambda_web/types/logging_config.py +67 -0
- capo_lambda_web/types/max_results.py +5 -0
- capo_lambda_web/types/next_token.py +5 -0
- capo_lambda_web/types/policy_revision_id.py +5 -0
- capo_lambda_web/types/put_resource_policy_request.py +43 -0
- capo_lambda_web/types/put_resource_policy_response.py +39 -0
- capo_lambda_web/types/region.py +5 -0
- capo_lambda_web/types/region_list.py +17 -0
- capo_lambda_web/types/regional_endpoint.py +140 -0
- capo_lambda_web/types/regional_endpoints.py +33 -0
- capo_lambda_web/types/resource_arn.py +5 -0
- capo_lambda_web/types/resource_policy.py +5 -0
- capo_lambda_web/types/revision_arn.py +5 -0
- capo_lambda_web/types/revision_config.py +69 -0
- capo_lambda_web/types/revision_error.py +40 -0
- capo_lambda_web/types/revision_errors.py +29 -0
- capo_lambda_web/types/revision_id.py +5 -0
- capo_lambda_web/types/revision_state.py +19 -0
- capo_lambda_web/types/revision_weight.py +38 -0
- capo_lambda_web/types/revision_weight_list.py +31 -0
- capo_lambda_web/types/role_arn.py +5 -0
- capo_lambda_web/types/runtime_config.py +26 -0
- capo_lambda_web/types/s3_object.py +39 -0
- capo_lambda_web/types/scaling_config.py +23 -0
- capo_lambda_web/types/service_config.py +87 -0
- capo_lambda_web/types/system_log_level.py +19 -0
- capo_lambda_web/types/tag_key.py +5 -0
- capo_lambda_web/types/tag_key_list.py +17 -0
- capo_lambda_web/types/tag_resource_request.py +38 -0
- capo_lambda_web/types/tags.py +25 -0
- capo_lambda_web/types/telemetry_config.py +36 -0
- capo_lambda_web/types/throttle_config.py +23 -0
- capo_lambda_web/types/untag_resource_request.py +27 -0
- capo_lambda_web/types/update_web_function_endpoint_request.py +121 -0
- capo_lambda_web/types/update_web_function_endpoint_response.py +291 -0
- capo_lambda_web-0.2.0.dist-info/METADATA +104 -0
- capo_lambda_web-0.2.0.dist-info/RECORD +154 -0
- capo_lambda_web-0.2.0.dist-info/WHEEL +5 -0
- capo_lambda_web-0.2.0.dist-info/licenses/LICENSE +21 -0
- capo_lambda_web-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import Optional, cast
|
|
2
|
+
|
|
3
|
+
from zapros import (
|
|
4
|
+
AsyncBaseHandler,
|
|
5
|
+
AsyncBaseMiddleware,
|
|
6
|
+
AsyncSyncMismatchError,
|
|
7
|
+
BaseHandler,
|
|
8
|
+
BaseMiddleware,
|
|
9
|
+
Request,
|
|
10
|
+
)
|
|
11
|
+
from zapros._models import Response
|
|
12
|
+
|
|
13
|
+
from ._signers import Signer
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def ensure_async_handler(
|
|
17
|
+
handler: AsyncBaseHandler | BaseHandler,
|
|
18
|
+
) -> AsyncBaseHandler:
|
|
19
|
+
if isinstance(handler, AsyncBaseHandler):
|
|
20
|
+
return handler
|
|
21
|
+
raise AsyncSyncMismatchError(
|
|
22
|
+
"Handler was expected to be an AsyncBaseHandler, but it is not."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def ensure_sync_handler(
|
|
27
|
+
handler: AsyncBaseHandler | BaseHandler,
|
|
28
|
+
) -> BaseHandler:
|
|
29
|
+
if isinstance(handler, BaseHandler):
|
|
30
|
+
return handler
|
|
31
|
+
raise AsyncSyncMismatchError(
|
|
32
|
+
"Handler was expected to be a BaseHandler, but it is not."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def strip_accept_encoding(request: Request) -> Request:
|
|
37
|
+
"""Drop the ``Accept-Encoding`` zapros adds to every request.
|
|
38
|
+
|
|
39
|
+
Like the AWS SDKs, the client never asks for transport compression: a
|
|
40
|
+
response body must be the service's actual bytes so ``Content-Length``,
|
|
41
|
+
checksums and ``Range`` stay coherent and a ``Content-Encoding`` on it is
|
|
42
|
+
always stored metadata (an S3 object's), never something to undo. Removing
|
|
43
|
+
the header before signing also keeps it out of the canonical request, where
|
|
44
|
+
a proxy rewriting it in flight would break the signature.
|
|
45
|
+
"""
|
|
46
|
+
if "accept-encoding" in request.headers:
|
|
47
|
+
del request.headers["Accept-Encoding"]
|
|
48
|
+
return request
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AuthMiddleware(BaseMiddleware, AsyncBaseMiddleware):
|
|
52
|
+
"""Sign the outgoing request using the Signer placed in ``context["signer"]``.
|
|
53
|
+
|
|
54
|
+
The generated ``get_signer`` in each operation module resolves the
|
|
55
|
+
effective auth scheme to a concrete :class:`Signer` and threads it
|
|
56
|
+
through ``request.context`` before dispatch. This middleware does the
|
|
57
|
+
actual signing; ``None`` means the operation opted out via
|
|
58
|
+
``@optionalAuth`` and the request passes through unsigned. Either way
|
|
59
|
+
``Accept-Encoding`` is stripped first (see :func:`strip_accept_encoding`).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, next_handler: BaseHandler | AsyncBaseHandler) -> None:
|
|
63
|
+
self.next = cast(BaseHandler, next_handler)
|
|
64
|
+
self.async_next = cast(AsyncBaseHandler, next_handler)
|
|
65
|
+
|
|
66
|
+
def handle(self, request: Request) -> Response:
|
|
67
|
+
next_handler = ensure_sync_handler(self.next)
|
|
68
|
+
request = strip_accept_encoding(request)
|
|
69
|
+
signer = cast(Optional[Signer], request.context.get("signer"))
|
|
70
|
+
if signer is not None:
|
|
71
|
+
request = signer.sign(request)
|
|
72
|
+
return next_handler.handle(request)
|
|
73
|
+
|
|
74
|
+
async def ahandle(self, request: Request) -> Response:
|
|
75
|
+
next_handler = ensure_async_handler(self.async_next)
|
|
76
|
+
request = strip_accept_encoding(request)
|
|
77
|
+
signer = cast(Optional[Signer], request.context.get("signer"))
|
|
78
|
+
if signer is not None:
|
|
79
|
+
request = await signer.asign(request)
|
|
80
|
+
return await next_handler.ahandle(request)
|
capo_lambda_web/_body.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Request body abstraction for streaming operations.
|
|
2
|
+
|
|
3
|
+
Hand-written, not regenerated.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from collections.abc import AsyncIterator, Callable, Iterator
|
|
10
|
+
from contextlib import (
|
|
11
|
+
AbstractAsyncContextManager,
|
|
12
|
+
AbstractContextManager,
|
|
13
|
+
asynccontextmanager,
|
|
14
|
+
contextmanager,
|
|
15
|
+
)
|
|
16
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, Union, cast
|
|
17
|
+
|
|
18
|
+
from ._iter import AnyIterator
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
import anyio
|
|
22
|
+
else:
|
|
23
|
+
try:
|
|
24
|
+
import anyio
|
|
25
|
+
except ImportError:
|
|
26
|
+
anyio = None
|
|
27
|
+
|
|
28
|
+
_CHUNK_SIZE = 64 * 1024
|
|
29
|
+
|
|
30
|
+
TStream = TypeVar("TStream", Iterator[bytes], AsyncIterator[bytes])
|
|
31
|
+
|
|
32
|
+
# The opener yields the fresh stream together with its length, or ``None`` when
|
|
33
|
+
# no stream could be produced (an exhausted or non-rewindable source, or logic
|
|
34
|
+
# that decides dynamically it cannot reopen). Pairing the length with each open
|
|
35
|
+
# lets it change between rebuilds instead of being fixed for the whole body.
|
|
36
|
+
_ContextManager = Union[
|
|
37
|
+
AbstractContextManager[Union[tuple[TStream, int], None]],
|
|
38
|
+
AbstractAsyncContextManager[Union[tuple[TStream, int], None]],
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Body(AnyIterator[bytes], Generic[TStream]):
|
|
43
|
+
"""A replayable request body whose length travels with each open.
|
|
44
|
+
|
|
45
|
+
A plain ``Iterator[bytes]`` can be sent only once, so a failed request
|
|
46
|
+
cannot be retried. A ``Body`` instead holds an *opener* — a context manager
|
|
47
|
+
that opens a fresh stream over the same source — and rebuilds the stream
|
|
48
|
+
before every attempt, closing whatever was left of the previous one.
|
|
49
|
+
|
|
50
|
+
``TStream`` is ``Iterator[bytes]`` for bodies built with :meth:`from_path`
|
|
51
|
+
and ``AsyncIterator[bytes]`` for :meth:`async_from_path`; the former use
|
|
52
|
+
``rebuild()``/``close()``, the latter ``arebuild()``/``aclose()``.
|
|
53
|
+
``build_request`` rebuilds before every attempt.
|
|
54
|
+
|
|
55
|
+
Each open produces ``(stream, length)`` together, so the length is not bound
|
|
56
|
+
to the whole body and may differ from one rebuild to the next. A rebuild is
|
|
57
|
+
also not guaranteed to succeed: the source may be exhausted or
|
|
58
|
+
non-rewindable, so the opener can yield ``None`` and :meth:`rebuild` then
|
|
59
|
+
returns ``None`` instead of a pair. Callers check the return value::
|
|
60
|
+
|
|
61
|
+
for attempt in range(max_attempts):
|
|
62
|
+
opened = body.rebuild()
|
|
63
|
+
if opened is None:
|
|
64
|
+
raise RuntimeError("body could not be rebuilt")
|
|
65
|
+
stream, length = opened
|
|
66
|
+
send(stream, length)
|
|
67
|
+
|
|
68
|
+
``length`` also holds the current stream's length as an attribute (``None``
|
|
69
|
+
before the first rebuild). Iterating a ``Body`` reads its current stream.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, opener: Callable[[], _ContextManager[TStream]]) -> None:
|
|
73
|
+
self._opener = opener
|
|
74
|
+
self._cm: _ContextManager[TStream] | None = None
|
|
75
|
+
self._stream: TStream | None = None
|
|
76
|
+
self.length: int | None = None
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_path(
|
|
80
|
+
cls, path: str | os.PathLike[str], chunk_size: int = _CHUNK_SIZE
|
|
81
|
+
) -> Body[Iterator[bytes]]:
|
|
82
|
+
"""A body that reads ``path`` in ``chunk_size`` chunks, reopening it (and re-measuring it) on every rebuild."""
|
|
83
|
+
|
|
84
|
+
@contextmanager
|
|
85
|
+
def open_() -> Iterator[tuple[Iterator[bytes], int]]:
|
|
86
|
+
with open(path, "rb") as file:
|
|
87
|
+
length = os.fstat(file.fileno()).st_size
|
|
88
|
+
yield iter(lambda: file.read(chunk_size), b""), length
|
|
89
|
+
|
|
90
|
+
return Body(open_)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def async_from_path(
|
|
94
|
+
cls, path: str | os.PathLike[str], chunk_size: int = _CHUNK_SIZE
|
|
95
|
+
) -> Body[AsyncIterator[bytes]]:
|
|
96
|
+
"""The async counterpart of :meth:`from_path`; needs ``anyio`` installed."""
|
|
97
|
+
if anyio is None:
|
|
98
|
+
dist = (__package__ or "capo").replace("_", "-")
|
|
99
|
+
raise RuntimeError(
|
|
100
|
+
"Body.async_from_path() requires the anyio package; "
|
|
101
|
+
f"reinstall with the anyio feature enabled: {dist}[anyio]"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@asynccontextmanager
|
|
105
|
+
async def open_() -> AsyncIterator[tuple[AsyncIterator[bytes], int]]:
|
|
106
|
+
async with await anyio.open_file(path, "rb") as file:
|
|
107
|
+
length = os.fstat(file.wrapped.fileno()).st_size
|
|
108
|
+
|
|
109
|
+
async def chunks() -> AsyncIterator[bytes]:
|
|
110
|
+
while chunk := await file.read(chunk_size):
|
|
111
|
+
yield chunk
|
|
112
|
+
|
|
113
|
+
yield chunks(), length
|
|
114
|
+
|
|
115
|
+
return Body(open_)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def stream(self) -> TStream | None:
|
|
119
|
+
"""The current open stream, or ``None`` if none is open (before the first
|
|
120
|
+
rebuild, or after a close).
|
|
121
|
+
|
|
122
|
+
``build_request`` reuses a non-``None`` stream instead of rebuilding, so
|
|
123
|
+
the retry layer stays in control of when a fresh stream is created.
|
|
124
|
+
"""
|
|
125
|
+
return self._stream
|
|
126
|
+
|
|
127
|
+
def rebuild(self: Body[Iterator[bytes]]) -> tuple[Iterator[bytes], int] | None:
|
|
128
|
+
"""Reopen the stream, closing any previous one, and return ``(stream, length)``.
|
|
129
|
+
|
|
130
|
+
``None`` if the source could not be reopened.
|
|
131
|
+
"""
|
|
132
|
+
self.close()
|
|
133
|
+
cm = self._opener()
|
|
134
|
+
if not isinstance(cm, AbstractContextManager):
|
|
135
|
+
raise TypeError(
|
|
136
|
+
"this Body has an async opener; use it with the async client (arebuild/aclose)"
|
|
137
|
+
)
|
|
138
|
+
cm = cast(AbstractContextManager["tuple[Iterator[bytes], int] | None"], cm)
|
|
139
|
+
opened = cm.__enter__()
|
|
140
|
+
if opened is None:
|
|
141
|
+
cm.__exit__(None, None, None)
|
|
142
|
+
return None
|
|
143
|
+
stream, length = opened
|
|
144
|
+
self._stream, self.length, self._cm = stream, length, cm
|
|
145
|
+
return stream, length
|
|
146
|
+
|
|
147
|
+
async def arebuild(
|
|
148
|
+
self: Body[AsyncIterator[bytes]],
|
|
149
|
+
) -> tuple[AsyncIterator[bytes], int] | None:
|
|
150
|
+
"""Async :meth:`rebuild`, returning ``(stream, length)`` or ``None``."""
|
|
151
|
+
await self.aclose()
|
|
152
|
+
cm = self._opener()
|
|
153
|
+
if not isinstance(cm, AbstractAsyncContextManager):
|
|
154
|
+
raise TypeError(
|
|
155
|
+
"this Body has a sync opener; use it with the sync client (rebuild/close)"
|
|
156
|
+
)
|
|
157
|
+
cm = cast(
|
|
158
|
+
AbstractAsyncContextManager["tuple[AsyncIterator[bytes], int] | None"], cm
|
|
159
|
+
)
|
|
160
|
+
opened = await cm.__aenter__()
|
|
161
|
+
if opened is None:
|
|
162
|
+
await cm.__aexit__(None, None, None)
|
|
163
|
+
return None
|
|
164
|
+
stream, length = opened
|
|
165
|
+
self._stream, self.length, self._cm = stream, length, cm
|
|
166
|
+
return stream, length
|
|
167
|
+
|
|
168
|
+
def close(self: Body[Iterator[bytes]]) -> None:
|
|
169
|
+
cm, self._cm, self._stream, self.length = self._cm, None, None, None
|
|
170
|
+
if cm is not None:
|
|
171
|
+
cast(AbstractContextManager[object], cm).__exit__(None, None, None)
|
|
172
|
+
|
|
173
|
+
async def aclose(self: Body[AsyncIterator[bytes]]) -> None:
|
|
174
|
+
cm, self._cm, self._stream, self.length = self._cm, None, None, None
|
|
175
|
+
if cm is not None:
|
|
176
|
+
await cast(AbstractAsyncContextManager[object], cm).__aexit__(
|
|
177
|
+
None, None, None
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def __next__(self) -> bytes:
|
|
181
|
+
if self._stream is None:
|
|
182
|
+
raise RuntimeError("body has no open stream; call rebuild() first")
|
|
183
|
+
return next(cast(Iterator[bytes], self._stream))
|
|
184
|
+
|
|
185
|
+
async def __anext__(self) -> bytes:
|
|
186
|
+
if self._stream is None:
|
|
187
|
+
raise RuntimeError("body has no open stream; call rebuild() first")
|
|
188
|
+
return await cast(AsyncIterator[bytes], self._stream).__anext__()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@contextmanager
|
|
192
|
+
def closing_bodies(inp: object) -> Iterator[None]:
|
|
193
|
+
"""Close every :class:`Body` in a request input when the operation finishes.
|
|
194
|
+
|
|
195
|
+
The operation method wraps its whole run (request build across retries, plus
|
|
196
|
+
response handling) in this, so a ``Body``'s stream is released once — and only
|
|
197
|
+
once — the operation is done. Non-``Body`` values (bytes, plain iterators,
|
|
198
|
+
``StaticAnyIterator``) are left untouched.
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
yield
|
|
202
|
+
finally:
|
|
203
|
+
if isinstance(inp, dict):
|
|
204
|
+
for value in inp.values():
|
|
205
|
+
if isinstance(value, Body):
|
|
206
|
+
cast("Body[Iterator[bytes]]", value).close()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@asynccontextmanager
|
|
210
|
+
async def aclosing_bodies(inp: object) -> AsyncIterator[None]:
|
|
211
|
+
"""Async :func:`closing_bodies`, awaiting :meth:`Body.aclose`."""
|
|
212
|
+
try:
|
|
213
|
+
yield
|
|
214
|
+
finally:
|
|
215
|
+
if isinstance(inp, dict):
|
|
216
|
+
for value in inp.values():
|
|
217
|
+
if isinstance(value, Body):
|
|
218
|
+
await cast("Body[AsyncIterator[bytes]]", value).aclose()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
__all__ = ["Body", "aclosing_bodies", "closing_bodies"]
|
capo_lambda_web/_iter.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Iterator helpers used by generated streaming operations.
|
|
2
|
+
|
|
3
|
+
Hand-written, not regenerated.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import AsyncIterable, AsyncIterator, Callable, Iterable, Iterator
|
|
9
|
+
from typing import Generic, TypeVar, cast
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T")
|
|
12
|
+
U = TypeVar("U")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AnyIterator(AsyncIterator[T], Iterator[T], Generic[T]):
|
|
16
|
+
"""A type that is both a synchronous and asynchronous iterator.
|
|
17
|
+
|
|
18
|
+
Streaming output fields use this so the same TypedDict type works for
|
|
19
|
+
both sync and async operation variants.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StaticAnyIterator(AnyIterator[T], Generic[T]):
|
|
26
|
+
"""An :class:`AnyIterator` backed by static, in-memory content.
|
|
27
|
+
|
|
28
|
+
Used when streaming output is produced from already-materialized bytes
|
|
29
|
+
rather than a live stream. The ``content`` is yielded once and remains
|
|
30
|
+
accessible as a public attribute.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, content: T) -> None:
|
|
34
|
+
self.content = content
|
|
35
|
+
self._consumed = False
|
|
36
|
+
|
|
37
|
+
def __next__(self) -> T:
|
|
38
|
+
if self._consumed:
|
|
39
|
+
raise StopIteration
|
|
40
|
+
self._consumed = True
|
|
41
|
+
return self.content
|
|
42
|
+
|
|
43
|
+
async def __anext__(self) -> T:
|
|
44
|
+
if self._consumed:
|
|
45
|
+
raise StopAsyncIteration
|
|
46
|
+
self._consumed = True
|
|
47
|
+
return self.content
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def map_sync_iterator(iterable: Iterable[T], fn: Callable[[T], U]) -> Iterator[U]:
|
|
51
|
+
"""Map ``fn`` over a synchronous iterable, yielding results lazily."""
|
|
52
|
+
for item in iterable:
|
|
53
|
+
yield fn(item)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def map_async_iterator(
|
|
57
|
+
async_iterable: AsyncIterable[T], fn: Callable[[T], U]
|
|
58
|
+
) -> AsyncIterator[U]:
|
|
59
|
+
"""Map ``fn`` over an asynchronous iterable, yielding results lazily."""
|
|
60
|
+
async for item in async_iterable:
|
|
61
|
+
yield fn(item)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def chain_sync_iterator(*iterables: Iterable[T]) -> Iterator[T]:
|
|
65
|
+
"""Yield items from each iterable in order."""
|
|
66
|
+
for iterable in iterables:
|
|
67
|
+
yield from iterable
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def chain_async_iterator(
|
|
71
|
+
prefix: Iterable[T], rest: AsyncIterable[T]
|
|
72
|
+
) -> AsyncIterator[T]:
|
|
73
|
+
"""Yield the synchronous ``prefix`` items, then the async ``rest`` items."""
|
|
74
|
+
for item in prefix:
|
|
75
|
+
yield item
|
|
76
|
+
async for item in rest:
|
|
77
|
+
yield item
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def ensure_async_iterator(value: AsyncIterator[T] | T) -> AnyIterator[T]:
|
|
81
|
+
"""Return ``value`` as an :class:`AnyIterator`.
|
|
82
|
+
|
|
83
|
+
An async iterator is returned unchanged; a single value is wrapped in a
|
|
84
|
+
fresh single-item async iterator.
|
|
85
|
+
"""
|
|
86
|
+
if isinstance(value, AsyncIterator):
|
|
87
|
+
return cast(AnyIterator[T], value)
|
|
88
|
+
|
|
89
|
+
return StaticAnyIterator(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def ensure_sync_iterator(value: Iterator[T] | T) -> AnyIterator[T]:
|
|
93
|
+
"""Return ``value`` as an :class:`AnyIterator`.
|
|
94
|
+
|
|
95
|
+
A sync iterator is returned unchanged; a single value is wrapped in a
|
|
96
|
+
fresh single-item sync iterator.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(value, Iterator):
|
|
99
|
+
return cast(AnyIterator[T], value)
|
|
100
|
+
|
|
101
|
+
return StaticAnyIterator(value)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
__all__ = [
|
|
105
|
+
"AnyIterator",
|
|
106
|
+
"StaticAnyIterator",
|
|
107
|
+
"chain_async_iterator",
|
|
108
|
+
"chain_sync_iterator",
|
|
109
|
+
"ensure_async_iterator",
|
|
110
|
+
"ensure_sync_iterator",
|
|
111
|
+
"map_async_iterator",
|
|
112
|
+
"map_sync_iterator",
|
|
113
|
+
]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Generated from Smithy shape ``com.amazonaws.lambdaweb#CreateWebFunction``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import zapros
|
|
9
|
+
from typing_extensions import Never
|
|
10
|
+
|
|
11
|
+
import capo_lambda_web._auth._signers
|
|
12
|
+
import capo_lambda_web._auth._sigv4
|
|
13
|
+
import capo_lambda_web._protocol.eventstream
|
|
14
|
+
import capo_lambda_web.errors.access_denied_exception
|
|
15
|
+
import capo_lambda_web.errors.conflict_exception
|
|
16
|
+
import capo_lambda_web.errors.internal_server_exception
|
|
17
|
+
import capo_lambda_web.errors.resource_not_found_exception
|
|
18
|
+
import capo_lambda_web.errors.service_quota_exceeded_exception
|
|
19
|
+
import capo_lambda_web.errors.throttling_exception
|
|
20
|
+
import capo_lambda_web.errors.validation_exception
|
|
21
|
+
import capo_lambda_web.types.create_web_function_request
|
|
22
|
+
import capo_lambda_web.types.create_web_function_response
|
|
23
|
+
import capo_lambda_web.types.date_time
|
|
24
|
+
import capo_lambda_web.types.endpoint_config
|
|
25
|
+
import capo_lambda_web.types.function_endpoint_summary
|
|
26
|
+
import capo_lambda_web.types.function_revision_summary
|
|
27
|
+
import capo_lambda_web.types.function_state
|
|
28
|
+
import capo_lambda_web.types.revision_config
|
|
29
|
+
import capo_lambda_web.types.tags
|
|
30
|
+
from capo_lambda_web._protocol.errors import parse_error_metadata_json
|
|
31
|
+
from capo_lambda_web._rule_engine._endpoint_rule_set import EndpointParams, resolve
|
|
32
|
+
from capo_lambda_web._services._pipeline import (
|
|
33
|
+
AsyncOperationOptions,
|
|
34
|
+
OperationOptions,
|
|
35
|
+
raise_error,
|
|
36
|
+
)
|
|
37
|
+
from capo_lambda_web.errors import UnknownServiceError
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def handle_error(response: zapros.Response) -> Never:
|
|
41
|
+
data = json.loads(response.read())
|
|
42
|
+
code, message = parse_error_metadata_json(response, data)
|
|
43
|
+
match code:
|
|
44
|
+
case "AccessDeniedException":
|
|
45
|
+
raise capo_lambda_web.errors.access_denied_exception.AccessDeniedException.from_json(
|
|
46
|
+
data, message
|
|
47
|
+
)
|
|
48
|
+
case "ConflictException":
|
|
49
|
+
raise capo_lambda_web.errors.conflict_exception.ConflictException.from_json(
|
|
50
|
+
data, message
|
|
51
|
+
)
|
|
52
|
+
case "InternalServerException":
|
|
53
|
+
raise capo_lambda_web.errors.internal_server_exception.InternalServerException.from_json(
|
|
54
|
+
data, message
|
|
55
|
+
)
|
|
56
|
+
case "ResourceNotFoundException":
|
|
57
|
+
raise capo_lambda_web.errors.resource_not_found_exception.ResourceNotFoundException.from_json(
|
|
58
|
+
data, message
|
|
59
|
+
)
|
|
60
|
+
case "ServiceQuotaExceededException":
|
|
61
|
+
raise capo_lambda_web.errors.service_quota_exceeded_exception.ServiceQuotaExceededException.from_json(
|
|
62
|
+
data, message
|
|
63
|
+
)
|
|
64
|
+
case "ThrottlingException":
|
|
65
|
+
raise capo_lambda_web.errors.throttling_exception.ThrottlingException.from_json(
|
|
66
|
+
data, message
|
|
67
|
+
)
|
|
68
|
+
case "ValidationException":
|
|
69
|
+
raise capo_lambda_web.errors.validation_exception.ValidationException.from_json(
|
|
70
|
+
data, message
|
|
71
|
+
)
|
|
72
|
+
case _:
|
|
73
|
+
raise UnknownServiceError(code=code, message=message, response=response)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def handle_response(
|
|
77
|
+
response: zapros.Response,
|
|
78
|
+
) -> capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse:
|
|
79
|
+
out: capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse = capo_lambda_web.types.create_web_function_response.deserialize_json(
|
|
80
|
+
json.loads(response.read())
|
|
81
|
+
)
|
|
82
|
+
return out
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def async_handle_response(
|
|
86
|
+
response: zapros.Response,
|
|
87
|
+
) -> capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse:
|
|
88
|
+
out: capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse = capo_lambda_web.types.create_web_function_response.deserialize_json(
|
|
89
|
+
json.loads(await response.aread())
|
|
90
|
+
)
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_signer(
|
|
95
|
+
options: AsyncOperationOptions | OperationOptions,
|
|
96
|
+
auth_schemes: list[dict[str, Any]] | None = None,
|
|
97
|
+
) -> capo_lambda_web._auth._signers.Signer | None:
|
|
98
|
+
name_to_schema = {s["name"]: s for s in (auth_schemes or [])} # noqa: F841
|
|
99
|
+
if (
|
|
100
|
+
options.credentials_provider is not None
|
|
101
|
+
and name_to_schema
|
|
102
|
+
and not name_to_schema.keys() & {"sigv4", "sigv4-s3express"}
|
|
103
|
+
):
|
|
104
|
+
raise RuntimeError(
|
|
105
|
+
"Endpoint requires an unsupported auth scheme: " + ", ".join(name_to_schema)
|
|
106
|
+
)
|
|
107
|
+
if options.credentials_provider is not None:
|
|
108
|
+
endpoint_scheme = name_to_schema.get("sigv4") or name_to_schema.get(
|
|
109
|
+
"sigv4-s3express"
|
|
110
|
+
)
|
|
111
|
+
if endpoint_scheme is not None or not name_to_schema:
|
|
112
|
+
sigv4_config = capo_lambda_web._auth._sigv4.build_sigv4_auth_scheme(
|
|
113
|
+
"lambda", options.region, endpoint_scheme
|
|
114
|
+
)
|
|
115
|
+
if sigv4_config is not None:
|
|
116
|
+
return capo_lambda_web._auth._signers.SigV4Signer(
|
|
117
|
+
options.credentials_provider, auth_scheme=sigv4_config
|
|
118
|
+
)
|
|
119
|
+
raise RuntimeError("Auth was not resolved")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def build_request(
|
|
123
|
+
options: OperationOptions | AsyncOperationOptions,
|
|
124
|
+
input_: capo_lambda_web.types.create_web_function_request.CreateWebFunctionRequest,
|
|
125
|
+
) -> zapros.Request:
|
|
126
|
+
endpoint = resolve(
|
|
127
|
+
EndpointParams(
|
|
128
|
+
Region=options.region,
|
|
129
|
+
UseDualStack=options.use_dual_stack,
|
|
130
|
+
UseFIPS=options.use_fips,
|
|
131
|
+
Endpoint=options.endpoint,
|
|
132
|
+
)
|
|
133
|
+
) # noqa: F841
|
|
134
|
+
url = endpoint.url.rstrip("/") + "/2025-03-07/web-functions"
|
|
135
|
+
params: list[tuple[str, str]] = []
|
|
136
|
+
headers: dict[str, str] = {k: ", ".join(v) for k, v in endpoint.headers.items()}
|
|
137
|
+
body: bytes | None = json.dumps(
|
|
138
|
+
capo_lambda_web.types.create_web_function_request.serialize_json(input_),
|
|
139
|
+
allow_nan=False,
|
|
140
|
+
).encode()
|
|
141
|
+
headers["content-type"] = "application/json"
|
|
142
|
+
signer = get_signer(options, auth_schemes=endpoint.properties.get("authSchemes"))
|
|
143
|
+
normalized_url = zapros.URL(url)
|
|
144
|
+
for k, v in params:
|
|
145
|
+
normalized_url.search_params.append(k, v)
|
|
146
|
+
return zapros.Request(
|
|
147
|
+
normalized_url, "PUT", headers=headers, body=body, context={"signer": signer}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def create_web_function(
|
|
152
|
+
options: OperationOptions,
|
|
153
|
+
input_: capo_lambda_web.types.create_web_function_request.CreateWebFunctionRequest,
|
|
154
|
+
) -> tuple[
|
|
155
|
+
capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse,
|
|
156
|
+
zapros.Response,
|
|
157
|
+
]:
|
|
158
|
+
response = options.client.handler.handle(build_request(options, input_))
|
|
159
|
+
try:
|
|
160
|
+
if response.status >= 300:
|
|
161
|
+
response.read()
|
|
162
|
+
raise_error(response, handle_error)
|
|
163
|
+
return handle_response(response), response
|
|
164
|
+
except BaseException:
|
|
165
|
+
response.close()
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def async_create_web_function(
|
|
170
|
+
options: AsyncOperationOptions,
|
|
171
|
+
input_: capo_lambda_web.types.create_web_function_request.CreateWebFunctionRequest,
|
|
172
|
+
) -> tuple[
|
|
173
|
+
capo_lambda_web.types.create_web_function_response.CreateWebFunctionResponse,
|
|
174
|
+
zapros.Response,
|
|
175
|
+
]:
|
|
176
|
+
response = await options.client.handler.ahandle(build_request(options, input_))
|
|
177
|
+
try:
|
|
178
|
+
if response.status >= 300:
|
|
179
|
+
await response.aread()
|
|
180
|
+
raise_error(response, handle_error)
|
|
181
|
+
return await async_handle_response(response), response
|
|
182
|
+
except BaseException:
|
|
183
|
+
await response.aclose()
|
|
184
|
+
raise
|