python - How to invoke AWS lambda function with context argument - Stack Overflow
python - How can I import LambdaContext? - Stack Overflow
amazon web services - How to access context.identity from a Python AWS Lambda function? - Stack Overflow
python - Lambda handler Context aws_request_id using pytest setup - Stack Overflow
Videos
I am writing a Python Lambda that will be invoked via HTTP. A web service will make an HTTP call to an API Gateway resource that I define, which will then invoke the Lambda. My Lambda handler will look like:
def lambda_handler(event, context):
// do stuff down here
return responseObjectI am trying to find documentation on event and context so I know how to do things like:
-
extract query string parameters from requests
-
extract path parameters from requests
-
inspect the request entity
-
etc.
Surprising I can find no official AWS documentation on what fields/properties these two objects have on them when they are invoked from an API Gateway resource action. I found this article which was sort of helpful but nothing official from AWS. Can anyone point me in the right direction?
You could try using LocalStack:
LocalStack provides an easy-to-use test/mocking framework for developing Cloud applications.
Currently, the focus is primarily on supporting the AWS cloud stack.
LocalStack spins up the following core Cloud APIs on your local machine:
API Gateway at http://localhost:4567
Kinesis at http://localhost:4568
DynamoDB at http://localhost:4569
DynamoDB Streams at http://localhost:4570
Elasticsearch at http://localhost:4571
S3 at http://localhost:4572
Firehose at http://localhost:4573
Lambda at http://localhost:4574
SNS at http://localhost:4575
SQS at http://localhost:4576
Redshift at http://localhost:4577
ES (Elasticsearch Service) at http://localhost:4578
SES at http://localhost:4579
Route53 at http://localhost:4580
CloudFormation at http://localhost:4581
CloudWatch at http://localhost:4582
class LambdaContext defined in /var/runtime/awslambda/bootstrap.py which is used to launch users functions and has the following structure:
class LambdaContext(object):
def __init__(self, invokeid, context_objs, client_context, invoked_function_arn=None):
self.aws_request_id = invokeid
self.log_group_name = os.environ['AWS_LAMBDA_LOG_GROUP_NAME']
self.log_stream_name = os.environ['AWS_LAMBDA_LOG_STREAM_NAME']
self.function_name = os.environ["AWS_LAMBDA_FUNCTION_NAME"]
self.memory_limit_in_mb = os.environ['AWS_LAMBDA_FUNCTION_MEMORY_SIZE']
self.function_version = os.environ['AWS_LAMBDA_FUNCTION_VERSION']
self.invoked_function_arn = invoked_function_arn
self.client_context = make_obj_from_dict(ClientContext, client_context)
if self.client_context is not None:
self.client_context.client = make_obj_from_dict(Client, self.client_context.client)
self.identity = make_obj_from_dict(CognitoIdentity, context_objs)
def get_remaining_time_in_millis(self):
return lambda_runtime.get_remaining_time()
def log(self, msg):
str_msg = str(msg)
lambda_runtime.send_console_message(str_msg, byte_len(str_msg))
If you want to emulate it on your local environment, just add it into your script:
class ClientContext(object):
__slots__ = ['custom', 'env', 'client']
def make_obj_from_dict(_class, _dict, fields=None):
if _dict is None:
return None
obj = _class()
set_obj_from_dict(obj, _dict)
return obj
def set_obj_from_dict(obj, _dict, fields=None):
if fields is None:
fields = obj.__class__.__slots__
for field in fields:
setattr(obj, field, _dict.get(field, None))
class LambdaContext(object):
def __init__(self, invokeid, context_objs, client_context, invoked_function_arn=None):
self.aws_request_id = invokeid
self.log_group_name = os.environ['AWS_LAMBDA_LOG_GROUP_NAME']
self.log_stream_name = os.environ['AWS_LAMBDA_LOG_STREAM_NAME']
self.function_name = os.environ["AWS_LAMBDA_FUNCTION_NAME"]
self.memory_limit_in_mb = os.environ['AWS_LAMBDA_FUNCTION_MEMORY_SIZE']
self.function_version = os.environ['AWS_LAMBDA_FUNCTION_VERSION']
self.invoked_function_arn = invoked_function_arn
self.client_context = make_obj_from_dict(ClientContext, client_context)
if self.client_context is not None:
self.client_context.client = None
self.identity = None
def get_remaining_time_in_millis(self):
return None
def log(self, msg):
str_msg = str(msg)
print(str_msg)
# lambda_runtime.send_console_message(str_msg, byte_len(str_msg))
The AWS Lambda context object is not a dictionary. aws_request_id is not a dictionary key, it's an attribute of the context object. Modifying your mock to be a dictionary will lead to your code breaking when being run as an actual Lambda function. The correct way to solve this is to ensure that your mock object is an authentic reproduction of the actual Lambda context object.
You could write a simple class and set the aws_request_id attribute on it as follows:
class LambdaContext:
aws_request_id = 'abcdef'
# ...
context = LambdaContext()
aws_request_id = context.aws_request_id
Unfortunately, the above didn't work for me. Got around this using the following:
from dataclasses import dataclass
@pytest.fixture
def context():
@dataclass
class LambdaContext:
function_name: str = "test"
aws_request_id: str = "88888888-4444-4444-4444-121212121212"
invoked_function_arn: str = "arn:aws:lambda:eu-west-1:123456789101:function:test"
return LambdaContext()
def test_lambda(context):
my_class = MyClass(EVENT, context)
You can't replace the work with does with an expression, no. There are no hacks to get you there either, because there is no way to handle exceptions and finalisation within an expression.
That's because you can only use one expression in a lambda. with is a statement, not an expression. You'd have to replace that with exception handling (try..except..finally) and calls to the __enter__ and __exit__ methods (storing the __exit__ method first). However, exception handling can only be done with statements, because an exception ends the current expression immediately. See Python Try Catch Block inside lambda.
Your only option is to stick to using a proper function instead.
One possible workaround for getting lambdas working with a context manager is to make the context manager a ContextDecorator, then both with statements and lambda expressions will work because a lambda can use the decorator pattern instead.
Example
from contextlib import ContextDecorator
def f(x):
"""Just prints the input, but this could be any arbitrary function."""
print(x)
class mycontext(ContextDecorator):
def __enter__(self):
f('Starting')
return self
def __exit__(self, *exc):
f('Finishing')
return False
with mycontext():
f('The bit in the middle')
mycontext()(lambda: f('The bit in the middle'))()