|
| 1 | +from itertools import chain |
1 | 2 | import logging |
2 | 3 | from contextlib import ExitStack |
| 4 | +from typing import List, Optional, Type |
| 5 | +import warnings |
3 | 6 |
|
4 | | - |
5 | | -from mangum.exceptions import ConfigurationError |
6 | | -from mangum.handlers.abstract_handler import AbstractHandler |
7 | 7 | from mangum.protocols import HTTPCycle, LifespanCycle |
8 | | -from mangum.types import ASGIApp, LambdaEvent, LambdaContext |
| 8 | +from mangum.handlers import ALB, HTTPGateway, APIGateway, LambdaAtEdge |
| 9 | +from mangum.exceptions import ConfigurationError |
| 10 | +from mangum.types import ( |
| 11 | + ASGIApp, |
| 12 | + LifespanMode, |
| 13 | + LambdaConfig, |
| 14 | + LambdaEvent, |
| 15 | + LambdaContext, |
| 16 | + LambdaHandler, |
| 17 | +) |
9 | 18 |
|
10 | 19 |
|
11 | | -DEFAULT_TEXT_MIME_TYPES = [ |
12 | | - "text/", |
13 | | - "application/json", |
14 | | - "application/javascript", |
15 | | - "application/xml", |
16 | | - "application/vnd.api+json", |
17 | | -] |
| 20 | +logger = logging.getLogger("mangum") |
18 | 21 |
|
19 | 22 |
|
20 | | -logger = logging.getLogger("mangum") |
| 23 | +HANDLERS: List[Type[LambdaHandler]] = [ |
| 24 | + ALB, |
| 25 | + HTTPGateway, |
| 26 | + APIGateway, |
| 27 | + LambdaAtEdge, |
| 28 | +] |
21 | 29 |
|
22 | 30 |
|
23 | 31 | class Mangum: |
24 | | - """ |
25 | | - Creates an adapter instance. |
26 | | -
|
27 | | - * **app** - An asynchronous callable that conforms to version 3.0 of the ASGI |
28 | | - specification. This will usually be an ASGI framework application instance. |
29 | | - * **lifespan** - A string to configure lifespan support. Choices are `auto`, `on`, |
30 | | - and `off`. Default is `auto`. |
31 | | - * **text_mime_types** - A list of MIME types to include with the defaults that |
32 | | - should not return a binary response in API Gateway. |
33 | | - * **api_gateway_base_path** - A string specifying the part of the url path after |
34 | | - which the server routing begins. |
35 | | - """ |
36 | | - |
37 | 32 | def __init__( |
38 | 33 | self, |
39 | 34 | app: ASGIApp, |
40 | | - lifespan: str = "auto", |
| 35 | + lifespan: LifespanMode = "auto", |
41 | 36 | api_gateway_base_path: str = "/", |
| 37 | + custom_handlers: Optional[List[Type[LambdaHandler]]] = None, |
42 | 38 | ) -> None: |
| 39 | + if lifespan not in ("auto", "on", "off"): |
| 40 | + raise ConfigurationError( |
| 41 | + "Invalid argument supplied for `lifespan`. Choices are: auto|on|off" |
| 42 | + ) |
| 43 | + |
43 | 44 | self.app = app |
44 | 45 | self.lifespan = lifespan |
45 | | - self.api_gateway_base_path = api_gateway_base_path |
| 46 | + self.api_gateway_base_path = api_gateway_base_path or "/" |
| 47 | + self.config = LambdaConfig(api_gateway_base_path=self.api_gateway_base_path) |
46 | 48 |
|
47 | | - if self.lifespan not in ("auto", "on", "off"): |
48 | | - raise ConfigurationError( |
49 | | - "Invalid argument supplied for `lifespan`. Choices are: auto|on|off" |
| 49 | + if custom_handlers is not None: |
| 50 | + warnings.warn( # pragma: no cover |
| 51 | + "Support for custom event handlers is currently provisional and may " |
| 52 | + "drastically change (or be removed entirely) in the future.", |
| 53 | + FutureWarning, |
50 | 54 | ) |
51 | 55 |
|
52 | | - def __call__(self, event: LambdaEvent, context: LambdaContext) -> dict: |
53 | | - logger.debug("Event received.") |
| 56 | + self.custom_handlers = custom_handlers or [] |
| 57 | + |
| 58 | + def infer(self, event: LambdaEvent, context: LambdaContext) -> LambdaHandler: |
| 59 | + for handler_cls in chain( |
| 60 | + self.custom_handlers, |
| 61 | + HANDLERS, |
| 62 | + ): |
| 63 | + handler = handler_cls.infer( |
| 64 | + event, |
| 65 | + context, |
| 66 | + self.config, |
| 67 | + ) |
| 68 | + if handler: |
| 69 | + break |
| 70 | + else: |
| 71 | + raise RuntimeError( # pragma: no cover |
| 72 | + "The adapter was unable to infer a handler to use for the event. This " |
| 73 | + "is likely related to how the Lambda function was invoked. (Are you " |
| 74 | + "testing locally? Make sure the request payload is valid for a " |
| 75 | + "supported handler.)" |
| 76 | + ) |
| 77 | + |
| 78 | + return handler |
54 | 79 |
|
| 80 | + def __call__(self, event: LambdaEvent, context: LambdaContext) -> dict: |
| 81 | + handler = self.infer(event, context) |
55 | 82 | with ExitStack() as stack: |
56 | | - if self.lifespan != "off": |
| 83 | + if self.lifespan in ("auto", "on"): |
57 | 84 | lifespan_cycle = LifespanCycle(self.app, self.lifespan) |
58 | 85 | stack.enter_context(lifespan_cycle) |
59 | 86 |
|
60 | | - handler = AbstractHandler.from_trigger( |
61 | | - event, context, self.api_gateway_base_path |
62 | | - ) |
63 | | - http_cycle = HTTPCycle(handler.request) |
64 | | - response = http_cycle(self.app, handler.body) |
| 87 | + http_cycle = HTTPCycle(handler.scope, handler.body) |
| 88 | + http_response = http_cycle(self.app) |
| 89 | + |
| 90 | + return handler(http_response) |
65 | 91 |
|
66 | | - return handler.transform_response(response) |
| 92 | + assert False, "unreachable" # pragma: no cover |
0 commit comments