modmex-lambda 0.1.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.
- modmex_lambda/__init__.py +62 -0
- modmex_lambda/data_classes/__init__.py +49 -0
- modmex_lambda/data_classes/api_gateway_authorizer_event.py +38 -0
- modmex_lambda/data_classes/api_gateway_proxy_event.py +328 -0
- modmex_lambda/data_classes/api_gateway_websocket_event.py +40 -0
- modmex_lambda/data_classes/cognito_user_pool_event.py +599 -0
- modmex_lambda/data_classes/common.py +441 -0
- modmex_lambda/event_handler/__init__.py +45 -0
- modmex_lambda/event_handler/api_gateway.py +331 -0
- modmex_lambda/event_handler/constants.py +3 -0
- modmex_lambda/event_handler/content_types.py +13 -0
- modmex_lambda/event_handler/cors.py +97 -0
- modmex_lambda/event_handler/dependencies/__init__.py +0 -0
- modmex_lambda/event_handler/dependencies/compat.py +231 -0
- modmex_lambda/event_handler/dependencies/dependant.py +279 -0
- modmex_lambda/event_handler/dependencies/dependency_middleware.py +423 -0
- modmex_lambda/event_handler/dependencies/depends.py +184 -0
- modmex_lambda/event_handler/dependencies/params.py +317 -0
- modmex_lambda/event_handler/dependencies/types.py +14 -0
- modmex_lambda/event_handler/exception_handler.py +70 -0
- modmex_lambda/event_handler/exceptions.py +72 -0
- modmex_lambda/event_handler/gateway_response.py +96 -0
- modmex_lambda/event_handler/middlewares.py +33 -0
- modmex_lambda/event_handler/params.py +44 -0
- modmex_lambda/event_handler/request.py +70 -0
- modmex_lambda/event_handler/response.py +60 -0
- modmex_lambda/event_handler/routing.py +507 -0
- modmex_lambda/event_handler/routing_fallbacks.py +92 -0
- modmex_lambda/event_handler/types.py +31 -0
- modmex_lambda/event_sources.py +53 -0
- modmex_lambda/exceptions.py +3 -0
- modmex_lambda/logging.py +99 -0
- modmex_lambda/params.py +3 -0
- modmex_lambda/parser.py +47 -0
- modmex_lambda/request.py +3 -0
- modmex_lambda/resolver.py +3 -0
- modmex_lambda/response.py +3 -0
- modmex_lambda/routing.py +3 -0
- modmex_lambda/shared/__init__.py +0 -0
- modmex_lambda/shared/cookies.py +84 -0
- modmex_lambda/shared/headers_serializer.py +65 -0
- modmex_lambda/shared/json_encoder.py +53 -0
- modmex_lambda/shared/types.py +4 -0
- modmex_lambda/validation.py +178 -0
- modmex_lambda-0.1.0.dist-info/METADATA +375 -0
- modmex_lambda-0.1.0.dist-info/RECORD +48 -0
- modmex_lambda-0.1.0.dist-info/WHEEL +4 -0
- modmex_lambda-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
from collections.abc import Callable, Iterator, Mapping
|
|
7
|
+
from typing import Any, overload
|
|
8
|
+
from modmex_lambda.shared.headers_serializer import BaseHeadersSerializer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _parse_cookie_string(cookie_string: str) -> dict[str, str]:
|
|
12
|
+
"""Parse a cookie string (``key=value; key2=value2``) into a dict."""
|
|
13
|
+
cookies: dict[str, str] = {}
|
|
14
|
+
for segment in cookie_string.split(";"):
|
|
15
|
+
stripped = segment.strip()
|
|
16
|
+
if "=" in stripped:
|
|
17
|
+
name, _, value = stripped.partition("=")
|
|
18
|
+
cookies[name.strip()] = value.strip()
|
|
19
|
+
return cookies
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CaseInsensitiveDict(dict):
|
|
23
|
+
"""Case insensitive dict implementation. Assumes string keys only."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, data=None, **kwargs):
|
|
26
|
+
super().__init__()
|
|
27
|
+
self.update(data, **kwargs)
|
|
28
|
+
|
|
29
|
+
def get(self, k, default=None):
|
|
30
|
+
return super().get(k.lower(), default)
|
|
31
|
+
|
|
32
|
+
def pop(self, k, *args):
|
|
33
|
+
return super().pop(k.lower(), *args)
|
|
34
|
+
|
|
35
|
+
def setdefault(self, k, default=None):
|
|
36
|
+
return super().setdefault(k.lower(), default)
|
|
37
|
+
|
|
38
|
+
def update(self, data=None, **kwargs):
|
|
39
|
+
if data is not None:
|
|
40
|
+
if isinstance(data, Mapping):
|
|
41
|
+
data = data.items()
|
|
42
|
+
super().update((k.lower(), v) for k, v in data)
|
|
43
|
+
super().update((k.lower(), v) for k, v in kwargs)
|
|
44
|
+
|
|
45
|
+
def __contains__(self, k):
|
|
46
|
+
return super().__contains__(k.lower())
|
|
47
|
+
|
|
48
|
+
def __delitem__(self, k):
|
|
49
|
+
super().__delitem__(k.lower())
|
|
50
|
+
|
|
51
|
+
def __eq__(self, other):
|
|
52
|
+
if not isinstance(other, Mapping):
|
|
53
|
+
return False
|
|
54
|
+
if not isinstance(other, CaseInsensitiveDict):
|
|
55
|
+
other = CaseInsensitiveDict(other)
|
|
56
|
+
return super().__eq__(other)
|
|
57
|
+
|
|
58
|
+
def __getitem__(self, k):
|
|
59
|
+
return super().__getitem__(k.lower())
|
|
60
|
+
|
|
61
|
+
def __setitem__(self, k, v):
|
|
62
|
+
super().__setitem__(k.lower(), v)
|
|
63
|
+
|
|
64
|
+
def __hash__(self):
|
|
65
|
+
# Convert the dictionary to a frozenset of tuples (key, value)
|
|
66
|
+
# where all keys are lowercase
|
|
67
|
+
items = frozenset((k.lower(), v) for k, v in self.items())
|
|
68
|
+
return hash(items)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DictWrapper(Mapping[str, Any]):
|
|
72
|
+
"""read-only dict wrapper with helper accessors."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, data: dict[str, Any], json_deserializer: Callable[[str], Any] | None = None) -> None:
|
|
75
|
+
self._data = data
|
|
76
|
+
self._json_deserializer = json_deserializer or json.loads
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def raw_event(self) -> dict[str, Any]:
|
|
80
|
+
return self._data
|
|
81
|
+
|
|
82
|
+
def __getitem__(self, key: str) -> Any:
|
|
83
|
+
return self._data[key]
|
|
84
|
+
|
|
85
|
+
def __iter__(self) -> Iterator[str]:
|
|
86
|
+
return iter(self._data)
|
|
87
|
+
|
|
88
|
+
def __len__(self) -> int:
|
|
89
|
+
return len(self._data)
|
|
90
|
+
|
|
91
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
92
|
+
return self._data.get(key, default)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class BaseProxyEvent(DictWrapper):
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def headers(self) -> dict[str, str]:
|
|
100
|
+
return CaseInsensitiveDict(self.get("headers"))
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def query_string_parameters(self) -> dict[str, str]:
|
|
104
|
+
return self.get("queryStringParameters", {}) or {}
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def multi_value_query_string_parameters(self) -> dict[str, list[str]]:
|
|
108
|
+
return self.get("multiValueQueryStringParameters", {}) or {}
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def resolved_query_string_parameters(self) -> dict[str, list[str]]:
|
|
112
|
+
return {k: v.split(",") for k, v in self.query_string_parameters.items()}
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def resolved_headers_field(self) -> dict[str, str]:
|
|
116
|
+
return self.headers
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def is_base64_encoded(self) -> bool:
|
|
120
|
+
return self.get("isBase64Encoded", False)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def body(self) -> str | None:
|
|
124
|
+
""" return body of the request as string"""
|
|
125
|
+
return self.get("body")
|
|
126
|
+
|
|
127
|
+
@cached_property
|
|
128
|
+
def json_body(self) -> Any:
|
|
129
|
+
"""Parses the body as json"""
|
|
130
|
+
if self.decoded_body:
|
|
131
|
+
return self._json_deserializer(self.decoded_body)
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
@cached_property
|
|
135
|
+
def decoded_body(self) -> str | None:
|
|
136
|
+
""" return body from base64 if encoded, otherwise return raw body"""
|
|
137
|
+
body = self.body
|
|
138
|
+
if self.is_base64_encoded and body:
|
|
139
|
+
return base64.b64decode(body.encode()).decode()
|
|
140
|
+
return body
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def path(self)->str:
|
|
145
|
+
return self['path']
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def http_method(self)->str:
|
|
149
|
+
return self['httpMethod']
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@overload
|
|
153
|
+
def get_query_string_value(self, name: str, default_value: str) -> str: ...
|
|
154
|
+
|
|
155
|
+
@overload
|
|
156
|
+
def get_query_string_value(self, name: str, default_value: str | None = None) -> str | None: ...
|
|
157
|
+
|
|
158
|
+
def get_query_string_value(self, name: str, default_value: str | None = None) -> str | None:
|
|
159
|
+
"""Get query string value by name"""
|
|
160
|
+
return self.query_string_parameters.get(name, default_value)
|
|
161
|
+
|
|
162
|
+
def get_multi_value_query_string_values(self, name: str, default_values: list[str] | None = None) -> list[str]:
|
|
163
|
+
"""Get multi value query string values by name"""
|
|
164
|
+
default = default_values or []
|
|
165
|
+
return self.multi_value_query_string_parameters.get(name, default)
|
|
166
|
+
|
|
167
|
+
def header_serializer(self) -> BaseHeadersSerializer:
|
|
168
|
+
raise NotImplementedError()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class RequestContextClientCert(DictWrapper):
|
|
172
|
+
@property
|
|
173
|
+
def client_cert_pem(self) -> str:
|
|
174
|
+
"""Client certificate pem"""
|
|
175
|
+
return self["clientCertPem"]
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def issuer_dn(self) -> str:
|
|
179
|
+
"""Issuer Distinguished Name"""
|
|
180
|
+
return self["issuerDN"]
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def serial_number(self) -> str:
|
|
184
|
+
"""Unique serial number for client cert"""
|
|
185
|
+
return self["serialNumber"]
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def subject_dn(self) -> str:
|
|
189
|
+
"""Subject Distinguished Name"""
|
|
190
|
+
return self["subjectDN"]
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def validity_not_after(self) -> str:
|
|
194
|
+
"""Date when the cert is no longer valid
|
|
195
|
+
|
|
196
|
+
eg: Aug 5 00:28:21 2120 GMT"""
|
|
197
|
+
return self["validity"]["notAfter"]
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def validity_not_before(self) -> str:
|
|
201
|
+
"""Cert is not valid before this date
|
|
202
|
+
|
|
203
|
+
eg: Aug 29 00:28:21 2020 GMT"""
|
|
204
|
+
return self["validity"]["notBefore"]
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class APIGatewayEventIdentity(DictWrapper):
|
|
208
|
+
@property
|
|
209
|
+
def access_key(self) -> str | None:
|
|
210
|
+
return self.get("accessKey")
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def account_id(self) -> str | None:
|
|
214
|
+
"""The AWS account ID associated with the request."""
|
|
215
|
+
return self.get("accountId")
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def api_key(self) -> str | None:
|
|
219
|
+
"""For API methods that require an API key, this variable is the API key associated with the method request.
|
|
220
|
+
For methods that don't require an API key, this variable is null."""
|
|
221
|
+
return self.get("apiKey")
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def api_key_id(self) -> str | None:
|
|
225
|
+
"""The API key ID associated with an API request that requires an API key."""
|
|
226
|
+
return self.get("apiKeyId")
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def caller(self) -> str | None:
|
|
230
|
+
"""The principal identifier of the caller making the request."""
|
|
231
|
+
return self.get("caller")
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def cognito_authentication_provider(self) -> str | None:
|
|
235
|
+
"""A comma-separated list of the Amazon Cognito authentication providers used by the caller
|
|
236
|
+
making the request. Available only if the request was signed with Amazon Cognito credentials."""
|
|
237
|
+
return self.get("cognitoAuthenticationProvider")
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def cognito_authentication_type(self) -> str | None:
|
|
241
|
+
"""The Amazon Cognito authentication type of the caller making the request.
|
|
242
|
+
Available only if the request was signed with Amazon Cognito credentials."""
|
|
243
|
+
return self.get("cognitoAuthenticationType")
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def cognito_identity_id(self) -> str | None:
|
|
247
|
+
"""The Amazon Cognito identity ID of the caller making the request.
|
|
248
|
+
Available only if the request was signed with Amazon Cognito credentials."""
|
|
249
|
+
return self.get("cognitoIdentityId")
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def cognito_identity_pool_id(self) -> str | None:
|
|
253
|
+
"""The Amazon Cognito identity pool ID of the caller making the request.
|
|
254
|
+
Available only if the request was signed with Amazon Cognito credentials."""
|
|
255
|
+
return self.get("cognitoIdentityPoolId")
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def principal_org_id(self) -> str | None:
|
|
259
|
+
"""The AWS organization ID."""
|
|
260
|
+
return self.get("principalOrgId")
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def source_ip(self) -> str:
|
|
264
|
+
"""The source IP address of the TCP connection making the request to API Gateway."""
|
|
265
|
+
return self["sourceIp"]
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def user(self) -> str | None:
|
|
269
|
+
"""The principal identifier of the user making the request."""
|
|
270
|
+
return self.get("user")
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def user_agent(self) -> str | None:
|
|
274
|
+
"""The User Agent of the API caller."""
|
|
275
|
+
return self.get("userAgent")
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def user_arn(self) -> str | None:
|
|
279
|
+
"""The Amazon Resource Name (ARN) of the effective user identified after authentication."""
|
|
280
|
+
return self.get("userArn")
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def client_cert(self) -> RequestContextClientCert | None:
|
|
284
|
+
client_cert = self.get("clientCert")
|
|
285
|
+
return None if client_cert is None else RequestContextClientCert(client_cert)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class BaseRequestContext(DictWrapper):
|
|
290
|
+
@property
|
|
291
|
+
def account_id(self) -> str:
|
|
292
|
+
"""The AWS account ID associated with the request."""
|
|
293
|
+
return self["accountId"]
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def api_id(self) -> str:
|
|
297
|
+
"""The identifier API Gateway assigns to your API."""
|
|
298
|
+
return self["apiId"]
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def domain_name(self) -> str | None:
|
|
302
|
+
"""A domain name"""
|
|
303
|
+
return self.get("domainName")
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def domain_prefix(self) -> str | None:
|
|
307
|
+
return self.get("domainPrefix")
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def extended_request_id(self) -> str | None:
|
|
311
|
+
"""An automatically generated ID for the API call, which contains more useful information
|
|
312
|
+
for debugging/troubleshooting."""
|
|
313
|
+
return self.get("extendedRequestId")
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def protocol(self) -> str:
|
|
317
|
+
"""The request protocol, for example, HTTP/1.1."""
|
|
318
|
+
return self["protocol"]
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def http_method(self) -> str:
|
|
322
|
+
"""The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT."""
|
|
323
|
+
return self["httpMethod"]
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def identity(self) -> APIGatewayEventIdentity:
|
|
327
|
+
return APIGatewayEventIdentity(self["identity"])
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def path(self) -> str:
|
|
331
|
+
return self["path"]
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def stage(self) -> str:
|
|
335
|
+
"""The deployment stage of the API request"""
|
|
336
|
+
return self["stage"]
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def request_id(self) -> str:
|
|
340
|
+
"""The ID that API Gateway assigns to the API request."""
|
|
341
|
+
return self["requestId"]
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def request_time(self) -> str | None:
|
|
345
|
+
"""The CLF-formatted request time (dd/MMM/yyyy:HH:mm:ss +-hhmm)"""
|
|
346
|
+
return self.get("requestTime")
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def request_time_epoch(self) -> int:
|
|
350
|
+
"""The Epoch-formatted request time."""
|
|
351
|
+
return self["requestTimeEpoch"]
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def resource_id(self) -> str:
|
|
355
|
+
return self["resourceId"]
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def resource_path(self) -> str:
|
|
359
|
+
return self["resourcePath"]
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class RequestContextV2Http(DictWrapper):
|
|
363
|
+
@property
|
|
364
|
+
def method(self) -> str:
|
|
365
|
+
return self["method"]
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def path(self) -> str:
|
|
369
|
+
return self["path"]
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def protocol(self) -> str:
|
|
373
|
+
"""The request protocol, for example, HTTP/1.1."""
|
|
374
|
+
return self["protocol"]
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def source_ip(self) -> str:
|
|
378
|
+
"""The source IP address of the TCP connection making the request to API Gateway."""
|
|
379
|
+
return self["sourceIp"]
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def user_agent(self) -> str:
|
|
383
|
+
"""The User Agent of the API caller."""
|
|
384
|
+
return self["userAgent"]
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class BaseRequestContextV2(DictWrapper):
|
|
388
|
+
@property
|
|
389
|
+
def account_id(self) -> str:
|
|
390
|
+
"""The AWS account ID associated with the request."""
|
|
391
|
+
return self["accountId"]
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def api_id(self) -> str:
|
|
395
|
+
"""The identifier API Gateway assigns to your API."""
|
|
396
|
+
return self["apiId"]
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def domain_name(self) -> str:
|
|
400
|
+
"""A domain name"""
|
|
401
|
+
return self["domainName"]
|
|
402
|
+
|
|
403
|
+
@property
|
|
404
|
+
def domain_prefix(self) -> str:
|
|
405
|
+
return self["domainPrefix"]
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def http(self) -> RequestContextV2Http:
|
|
409
|
+
return RequestContextV2Http(self["http"])
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def request_id(self) -> str:
|
|
413
|
+
"""The ID that API Gateway assigns to the API request."""
|
|
414
|
+
return self["requestId"]
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def route_key(self) -> str:
|
|
418
|
+
"""The selected route key."""
|
|
419
|
+
return self["routeKey"]
|
|
420
|
+
|
|
421
|
+
@property
|
|
422
|
+
def stage(self) -> str:
|
|
423
|
+
"""The deployment stage of the API request"""
|
|
424
|
+
return self["stage"]
|
|
425
|
+
|
|
426
|
+
@property
|
|
427
|
+
def time(self) -> str:
|
|
428
|
+
"""The CLF-formatted request time (dd/MMM/yyyy:HH:mm:ss +-hhmm)."""
|
|
429
|
+
return self["time"]
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def time_epoch(self) -> int:
|
|
433
|
+
"""The Epoch-formatted request time."""
|
|
434
|
+
return self["timeEpoch"]
|
|
435
|
+
|
|
436
|
+
@property
|
|
437
|
+
def authentication(self) -> RequestContextClientCert | None:
|
|
438
|
+
"""Optional when using mutual TLS authentication"""
|
|
439
|
+
authentication = self.get("authentication") or {}
|
|
440
|
+
client_cert = authentication.get("clientCert")
|
|
441
|
+
return None if client_cert is None else RequestContextClientCert(client_cert)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from . import content_types
|
|
8
|
+
from .api_gateway import (
|
|
9
|
+
ApiGatewayHttpResolver,
|
|
10
|
+
ApiGatewayRestResolver,
|
|
11
|
+
Response,
|
|
12
|
+
)
|
|
13
|
+
from .dependencies.depends import Depends
|
|
14
|
+
|
|
15
|
+
_EXPORTS = {
|
|
16
|
+
"ApiGatewayHttpResolver": ("modmex_lambda.event_handler.api_gateway", "ApiGatewayHttpResolver"),
|
|
17
|
+
"ApiGatewayRestResolver": ("modmex_lambda.event_handler.api_gateway", "ApiGatewayRestResolver"),
|
|
18
|
+
"Response": ("modmex_lambda.event_handler.api_gateway", "Response"),
|
|
19
|
+
"Depends": ("modmex_lambda.event_handler.dependencies.depends", "Depends"),
|
|
20
|
+
"content_types": ("modmex_lambda.event_handler.content_types", None),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"ApiGatewayHttpResolver",
|
|
25
|
+
"ApiGatewayRestResolver",
|
|
26
|
+
"Response",
|
|
27
|
+
"Depends",
|
|
28
|
+
"content_types",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def __getattr__(name: str) -> Any:
|
|
33
|
+
target = _EXPORTS.get(name)
|
|
34
|
+
if target is None:
|
|
35
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
36
|
+
|
|
37
|
+
module_name, attr = target
|
|
38
|
+
module = import_module(module_name)
|
|
39
|
+
value = module if attr is None else getattr(module, attr)
|
|
40
|
+
globals()[name] = value
|
|
41
|
+
return value
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __dir__() -> list[str]:
|
|
45
|
+
return sorted([*globals().keys(), *__all__])
|