1
votes

I'm trying to come up with a nice pattern for future lambdas that we write on our team.

One of the goals is to try to teach object oriented development to some team members, so I'd like to use that pattern here. I have that working OK on other real lambdas, but trying to add the useful library 'aws-lambda-powertools-python' I'm running into some problems.

Lambda code:

from aws_lambda_powertools import Logger

logger = Logger()


class LambdaExample:
    @classmethod
    def get_handler(cls, *args, **kwargs):
        """Get handler"""
        def handler(event, context):
            return cls(*args, **kwargs).handle(event, context)

        return handler

    @logger.inject_lambda_context(log_event=True)
    def handle(self, event, context):
        logger.info("Hello World")


lambda_handler = LambdaExample.get_handler()

The docs for powertools have a code sample for pytest. Their code sample isn't a complete file though, and it seems to assume that the lambda handler is a standalone function rather than a class method. I've tried to adapt it:

from collections import namedtuple
import pytest
from lambda_example import LambdaExample

@pytest.fixture
def lambda_context():
    lambda_context = {
        "function_name": "test",
        "memory_limit_in_mb": 128,
        "invoked_function_arn": "arn:aws:lambda:eu-west-1:809313241:function:test",
        "aws_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
    }

    return namedtuple("LambdaContext", lambda_context.keys())(*lambda_context.values())

class TestLambdaHandler:

    def test_handle_will_pass_when_env_setup(self, lambda_context):
        # Given
        test_event = {'test': 'event'}
        lambda_handler = LambdaExample()
        # When
        lambda_handler.handle(test_event, lambda_context)

The result I'm getting is:

>       lambda_handler.handle(test_event, lambda_context)
E       TypeError: decorate() takes 2 positional arguments but 3 were given

If I remove this line from the handler method it all works fine: "@logger.inject_lambda_context(log_event=True)"

The problem seems to be that the decorator doesn't work with the 'self' argument on the method.

Any ideas on how to get it going?

1
Why would you want to obfuscate a non-OO Lambda function invocation with an object-oriented wrapper? - jarmod
not shown in this simple example, but in a full one, the lambda object is instantiating the dependencies and wiring them together. basically a simple implementation of dependency injection. Since dependent classes arent required to resolve their own dependencies it makes testing them easier. also i prefer this pattern to using globals for reusing resources across multiple lambda invocations - Aidan

1 Answers

0
votes

Figured it out. It was a simple case of moving the annotation like so:

from aws_lambda_powertools import Logger

logger = Logger()


class LambdaExample:
    @classmethod
    def get_handler(cls, *args, **kwargs):
        """Get handler"""
        @logger.inject_lambda_context(log_event=True)
        def handler(event, context):
            return cls(*args, **kwargs).handle(event, context)

        return handler

    def handle(self, event, context):
        logger.info("Hello World")


lambda_handler = LambdaExample.get_handler()

Then in the test class invoke it same way as lambda:

    lambda_handler = LambdaExample.get_handler()
    response = lambda_handler(test_event, lambda_context)