From f6e2211da0b190c165a843c3867bb207026e5a3d Mon Sep 17 00:00:00 2001 From: Jordan Eremieff <1376648+jordaneremieff@users.noreply.github.com> Date: Sun, 13 Feb 2022 00:34:02 +1100 Subject: [PATCH] Replace awslambdaric-stubs with new types, refactor import style. (#235) * :art: Replace awslambdaric-stubs with new types, refactor import style. --- mangum/adapter.py | 26 +++--- mangum/handlers/__init__.py | 13 --- mangum/handlers/abstract_handler.py | 23 +++--- mangum/handlers/aws_alb.py | 6 +- mangum/handlers/aws_api_gateway.py | 16 ++-- mangum/handlers/aws_cf_lambda_at_edge.py | 4 +- mangum/handlers/aws_http_gateway.py | 4 +- mangum/protocols/http.py | 4 +- mangum/types.py | 83 ++++++++++++++++++-- requirements.txt | 1 - tests/handlers/test_abstract_handler.py | 2 +- tests/handlers/test_aws_alb.py | 5 +- tests/handlers/test_aws_api_gateway.py | 6 +- tests/handlers/test_aws_cf_lambda_at_edge.py | 2 +- tests/handlers/test_aws_http_gateway.py | 2 +- tests/test_http.py | 3 + tests/test_lifespan.py | 1 + 17 files changed, 123 insertions(+), 78 deletions(-) diff --git a/mangum/adapter.py b/mangum/adapter.py index 7811feed..de1f9c6d 100644 --- a/mangum/adapter.py +++ b/mangum/adapter.py @@ -1,15 +1,11 @@ import logging from contextlib import ExitStack -from typing import Any, Dict, TYPE_CHECKING -from .exceptions import ConfigurationError -from .handlers import AbstractHandler -from .protocols import HTTPCycle, LifespanCycle -from .types import ASGIApp - -if TYPE_CHECKING: # pragma: no cover - from awslambdaric.lambda_context import LambdaContext +from mangum.exceptions import ConfigurationError +from mangum.handlers.abstract_handler import AbstractHandler +from mangum.protocols import HTTPCycle, LifespanCycle +from mangum.types import ASGIApp, LambdaEvent, LambdaContext DEFAULT_TEXT_MIME_TYPES = [ @@ -53,7 +49,7 @@ def __init__( "Invalid argument supplied for `lifespan`. Choices are: auto|on|off" ) - def __call__(self, event: Dict[str, Any], context: "LambdaContext") -> dict: + def __call__(self, event: LambdaEvent, context: LambdaContext) -> dict: logger.debug("Event received.") with ExitStack() as stack: @@ -61,12 +57,10 @@ def __call__(self, event: Dict[str, Any], context: "LambdaContext") -> dict: lifespan_cycle = LifespanCycle(self.app, self.lifespan) stack.enter_context(lifespan_cycle) - handler = AbstractHandler.from_trigger( - event, context, self.api_gateway_base_path - ) - request = handler.request - - http_cycle = HTTPCycle(request) - response = http_cycle(self.app, handler.body) + handler = AbstractHandler.from_trigger( + event, context, self.api_gateway_base_path + ) + http_cycle = HTTPCycle(handler.request) + response = http_cycle(self.app, handler.body) return handler.transform_response(response) diff --git a/mangum/handlers/__init__.py b/mangum/handlers/__init__.py index 9ab3303d..e69de29b 100644 --- a/mangum/handlers/__init__.py +++ b/mangum/handlers/__init__.py @@ -1,13 +0,0 @@ -from .abstract_handler import AbstractHandler -from .aws_alb import AwsAlb -from .aws_api_gateway import AwsApiGateway -from .aws_cf_lambda_at_edge import AwsCfLambdaAtEdge -from .aws_http_gateway import AwsHttpGateway - -__all__ = [ - "AbstractHandler", - "AwsAlb", - "AwsApiGateway", - "AwsCfLambdaAtEdge", - "AwsHttpGateway", -] diff --git a/mangum/handlers/abstract_handler.py b/mangum/handlers/abstract_handler.py index 2bf5c437..666f633a 100644 --- a/mangum/handlers/abstract_handler.py +++ b/mangum/handlers/abstract_handler.py @@ -1,18 +1,15 @@ import base64 from abc import ABCMeta, abstractmethod -from typing import Dict, Any, TYPE_CHECKING, Tuple, List +from typing import Dict, Any, Tuple, List -from ..types import Response, Request - -if TYPE_CHECKING: # pragma: no cover - from awslambdaric.lambda_context import LambdaContext +from mangum.types import Response, Request, LambdaEvent, LambdaContext class AbstractHandler(metaclass=ABCMeta): def __init__( self, - trigger_event: Dict[str, Any], - trigger_context: "LambdaContext", + trigger_event: LambdaEvent, + trigger_context: LambdaContext, ): self.trigger_event = trigger_event self.trigger_context = trigger_context @@ -40,8 +37,8 @@ def transform_response(self, response: Response) -> Dict[str, Any]: @staticmethod def from_trigger( - trigger_event: Dict[str, Any], - trigger_context: "LambdaContext", + trigger_event: LambdaEvent, + trigger_context: LambdaContext, api_gateway_base_path: str = "/", ) -> "AbstractHandler": """ @@ -55,7 +52,7 @@ def from_trigger( "requestContext" in trigger_event and "elb" in trigger_event["requestContext"] ): - from . import AwsAlb + from mangum.handlers.aws_alb import AwsAlb return AwsAlb(trigger_event, trigger_context) if ( @@ -63,12 +60,12 @@ def from_trigger( and len(trigger_event["Records"]) > 0 and "cf" in trigger_event["Records"][0] ): - from . import AwsCfLambdaAtEdge + from mangum.handlers.aws_cf_lambda_at_edge import AwsCfLambdaAtEdge return AwsCfLambdaAtEdge(trigger_event, trigger_context) if "version" in trigger_event and "requestContext" in trigger_event: - from . import AwsHttpGateway + from mangum.handlers.aws_http_gateway import AwsHttpGateway return AwsHttpGateway( trigger_event, @@ -77,7 +74,7 @@ def from_trigger( ) if "resource" in trigger_event: - from . import AwsApiGateway + from mangum.handlers.aws_api_gateway import AwsApiGateway return AwsApiGateway( trigger_event, diff --git a/mangum/handlers/aws_alb.py b/mangum/handlers/aws_alb.py index 9e265cc5..c93f5bde 100644 --- a/mangum/handlers/aws_alb.py +++ b/mangum/handlers/aws_alb.py @@ -3,10 +3,8 @@ from typing import Any, Dict, Generator, List, Tuple from itertools import islice -from mangum.types import QueryParams - -from .abstract_handler import AbstractHandler -from .. import Response, Request +from mangum.types import Response, Request, QueryParams +from mangum.handlers.abstract_handler import AbstractHandler def all_casings(input_string: str) -> Generator[str, None, None]: diff --git a/mangum/handlers/aws_api_gateway.py b/mangum/handlers/aws_api_gateway.py index aaebc7a6..c97b684b 100644 --- a/mangum/handlers/aws_api_gateway.py +++ b/mangum/handlers/aws_api_gateway.py @@ -1,22 +1,16 @@ import base64 from urllib.parse import urlencode, unquote -from typing import Dict, Any, TYPE_CHECKING +from typing import Dict, Any -from mangum.types import QueryParams - -from .abstract_handler import AbstractHandler -from .. import Response, Request - - -if TYPE_CHECKING: # pragma: no cover - from awslambdaric.lambda_context import LambdaContext +from mangum.handlers.abstract_handler import AbstractHandler +from mangum.types import Response, Request, LambdaEvent, LambdaContext, QueryParams class AwsApiGateway(AbstractHandler): def __init__( self, - trigger_event: Dict[str, Any], - trigger_context: "LambdaContext", + trigger_event: LambdaEvent, + trigger_context: LambdaContext, api_gateway_base_path: str, ): super().__init__(trigger_event, trigger_context) diff --git a/mangum/handlers/aws_cf_lambda_at_edge.py b/mangum/handlers/aws_cf_lambda_at_edge.py index c9d2f314..def851ef 100644 --- a/mangum/handlers/aws_cf_lambda_at_edge.py +++ b/mangum/handlers/aws_cf_lambda_at_edge.py @@ -1,8 +1,8 @@ import base64 from typing import Dict, Any, List -from .abstract_handler import AbstractHandler -from .. import Response, Request +from mangum.handlers.abstract_handler import AbstractHandler +from mangum.types import Response, Request class AwsCfLambdaAtEdge(AbstractHandler): diff --git a/mangum/handlers/aws_http_gateway.py b/mangum/handlers/aws_http_gateway.py index 93543b34..feed1efb 100644 --- a/mangum/handlers/aws_http_gateway.py +++ b/mangum/handlers/aws_http_gateway.py @@ -2,8 +2,8 @@ import urllib.parse from typing import Dict, Any, List, Tuple -from . import AwsApiGateway -from .. import Response, Request +from mangum.handlers.aws_api_gateway import AwsApiGateway +from mangum.types import Response, Request class AwsHttpGateway(AwsApiGateway): diff --git a/mangum/protocols/http.py b/mangum/protocols/http.py index 5b33636e..7e82d017 100644 --- a/mangum/protocols/http.py +++ b/mangum/protocols/http.py @@ -63,9 +63,7 @@ def __call__(self, app: ASGIApp, initial_body: bytes) -> Response: asgi_task = self.loop.create_task(asgi_instance) self.loop.run_until_complete(asgi_task) - if self.response is None: - # Something really bad happened and we puked before we could get a - # response out + if self.response is None: # pragma: nocover self.response = Response( status=500, body=b"Internal Server Error", diff --git a/mangum/types.py b/mangum/types.py index e37d4ea4..1f8b8b14 100644 --- a/mangum/types.py +++ b/mangum/types.py @@ -10,7 +10,6 @@ MutableMapping, Awaitable, Callable, - TYPE_CHECKING, ) from typing_extensions import Protocol, TypeAlias @@ -21,15 +20,89 @@ Send: TypeAlias = Callable[[Message], Awaitable[None]] -if TYPE_CHECKING: # pragma: no cover - from awslambdaric.lambda_context import LambdaContext - - class ASGIApp(Protocol): async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: ... # pragma: no cover +LambdaEvent = Dict[str, Any] + + +class LambdaCognitoIdentity(Protocol): + """Information about the Amazon Cognito identity that authorized the request. + + **cognito_identity_id** - The authenticated Amazon Cognito identity. + **cognito_identity_pool_id** - The Amazon Cognito identity pool that authorized the + invocation. + """ + + cognito_identity_id: str + cognito_identity_pool_id: str + + +class LambdaMobileClient(Protocol): + """Mobile client information for the application and the device. + + **installation_id** - A unique identifier for an installation instance of an + application. + **app_title** - The title of the application. For example, "My App". + **app_version_code** - The version of the application. For example, "V2.0". + **app_version_name** - The version code for the application. For example, 3. + **app_package_name** - The name of the package. For example, "com.example.my_app". + """ + + installation_id: str + app_title: str + app_version_name: str + app_version_code: str + app_package_name: str + + +class LambdaMobileClientContext(Protocol): + """Information about client application and device when invoked via AWS Mobile SDK. + + **client** - A dict of name-value pairs that describe the mobile client application. + **custom** - A dict of custom values set by the mobile client application. + **env** - A dict of environment information provided by the AWS SDK. + """ + + client: LambdaMobileClient + custom: Dict[str, Any] + env: Dict[str, Any] + + +class LambdaContext(Protocol): + """The context object passed to the handler function. + + **function_name** - The name of the Lambda function. + **function_version** - The version of the function. + **invoked_function_arn** - The Amazon Resource Name (ARN) that's used to invoke the + function. Indicates if the invoker specified a version number or alias. + **memory_limit_in_mb** - The amount of memory that's allocated for the function. + **aws_request_id** - The identifier of the invocation request. + **log_group_name** - The log group for the function. + **log_stream_name** - The log stream for the function instance. + **identity** - (mobile apps) Information about the Amazon Cognito identity that + authorized the request. + **client_context** - (mobile apps) Client context that's provided to Lambda by the + client application. + """ + + function_name: str + function_version: str + invoked_function_arn: str + memory_limit_in_mb: int + aws_request_id: str + log_group_name: str + log_stream_name: str + identity: Optional[LambdaCognitoIdentity] + client_context: Optional[LambdaMobileClientContext] + + def get_remaining_time_in_millis(self) -> int: + """Returns the number of milliseconds left before the execution times out.""" + ... # pragma: no cover + + @dataclass class BaseRequest: """ diff --git a/requirements.txt b/requirements.txt index 12d5e910..d7ea423c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,6 @@ quart; python_version >= '3.7' mypy brotli brotli-asgi -awslambdaric-stubs types-dataclasses; python_version < '3.7' # For mypy # Docs mkdocs diff --git a/tests/handlers/test_abstract_handler.py b/tests/handlers/test_abstract_handler.py index fd2d4310..b38366e3 100644 --- a/tests/handlers/test_abstract_handler.py +++ b/tests/handlers/test_abstract_handler.py @@ -1,6 +1,6 @@ import pytest -from mangum.handlers import AbstractHandler +from mangum.handlers.abstract_handler import AbstractHandler def test_abstract_handler_unkown_event(): diff --git a/tests/handlers/test_aws_alb.py b/tests/handlers/test_aws_alb.py index 4027efaf..105ae81c 100644 --- a/tests/handlers/test_aws_alb.py +++ b/tests/handlers/test_aws_alb.py @@ -3,11 +3,12 @@ 1. https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html 2. https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html # noqa: E501 """ +from typing import Dict, List, Optional + import pytest from mangum import Mangum -from mangum.handlers import AwsAlb -from typing import Dict, List, Optional +from mangum.handlers.aws_alb import AwsAlb def get_mock_aws_alb_event( diff --git a/tests/handlers/test_aws_api_gateway.py b/tests/handlers/test_aws_api_gateway.py index 58a6ca56..d87b669b 100644 --- a/tests/handlers/test_aws_api_gateway.py +++ b/tests/handlers/test_aws_api_gateway.py @@ -1,9 +1,9 @@ -import pytest - import urllib.parse +import pytest + from mangum import Mangum -from mangum.handlers import AwsApiGateway +from mangum.handlers.aws_api_gateway import AwsApiGateway def get_mock_aws_api_gateway_event( diff --git a/tests/handlers/test_aws_cf_lambda_at_edge.py b/tests/handlers/test_aws_cf_lambda_at_edge.py index f7b28061..f1910ed1 100644 --- a/tests/handlers/test_aws_cf_lambda_at_edge.py +++ b/tests/handlers/test_aws_cf_lambda_at_edge.py @@ -3,7 +3,7 @@ import pytest from mangum import Mangum -from mangum.handlers import AwsCfLambdaAtEdge +from mangum.handlers.aws_cf_lambda_at_edge import AwsCfLambdaAtEdge def mock_lambda_at_edge_event( diff --git a/tests/handlers/test_aws_http_gateway.py b/tests/handlers/test_aws_http_gateway.py index 8c0c764f..2475014d 100644 --- a/tests/handlers/test_aws_http_gateway.py +++ b/tests/handlers/test_aws_http_gateway.py @@ -3,7 +3,7 @@ import pytest from mangum import Mangum -from mangum.handlers import AwsHttpGateway +from mangum.handlers.aws_http_gateway import AwsHttpGateway def get_mock_aws_http_gateway_event_v1( diff --git a/tests/test_http.py b/tests/test_http.py index 820fe324..b45ae518 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -3,11 +3,14 @@ import json import pytest + import brotli from brotli_asgi import BrotliMiddleware + from starlette.applications import Starlette from starlette.middleware.gzip import GZipMiddleware from starlette.responses import PlainTextResponse + from mangum import Mangum diff --git a/tests/test_lifespan.py b/tests/test_lifespan.py index 7114c24c..410e5a31 100644 --- a/tests/test_lifespan.py +++ b/tests/test_lifespan.py @@ -2,6 +2,7 @@ import logging import pytest + from starlette.applications import Starlette from starlette.responses import PlainTextResponse