diff --git a/aws_lambda/aws_lambda.py b/aws_lambda/aws_lambda.py index b1c8ee7..388e4f9 100755 --- a/aws_lambda/aws_lambda.py +++ b/aws_lambda/aws_lambda.py @@ -3,6 +3,7 @@ import json import logging import os +import sys import time from imp import load_source from shutil import copy, copyfile @@ -12,6 +13,7 @@ import pip import yaml from . import project_template +from .context import MockContext from .helpers import mkdir, read, archive, timestamp @@ -41,7 +43,23 @@ def deploy(src): create_function(cfg, path_to_zip_file) -def invoke(src, alt_event=None, verbose=False): +def _load_json(base_dir, filename, default): + if filename is None: + filename = default + path = os.path.join(base_dir, filename) + try: + return read(path, loader=json.loads) + except IOError: + print("File does not exist: {}".format(path)) + # file does not exist or json is malformed + return None + except ValueError: + print("Could not decode JSON object: {}".format(path)) + print("Aborting...") + sys.exit(1) + + +def invoke(src, alt_event=None, alt_context=None, verbose=False): """Simulates a call to your function. :param str src: @@ -49,6 +67,8 @@ def invoke(src, alt_event=None, verbose=False): config.yaml and handler module (e.g.: service.py). :param str alt_event: An optional argument to override which event file to use. + :param str alt_context: + An optional argument to override which context file to use. :param bool verbose: Whether to print out verbose details. """ @@ -57,22 +77,16 @@ def invoke(src, alt_event=None, verbose=False): cfg = read(path_to_config_file, loader=yaml.load) # Load and parse event file. - if alt_event: - path_to_event_file = os.path.join(src, alt_event) - else: - path_to_event_file = os.path.join(src, 'event.json') - event = read(path_to_event_file, loader=json.loads) + event = _load_json(src, alt_event, 'event.json') + context = _load_json(src, alt_context, 'context.json') handler = cfg.get('handler') # Inspect the handler string (.) and translate it # into a function we can execute. fn = get_callable_handler_function(src, handler) - # TODO: look into mocking the ``context`` variable, currently being passed - # as None. - start = time.time() - results = fn(event, None) + results = fn(event, MockContext(context)) end = time.time() print("{0}".format(results)) diff --git a/aws_lambda/context.py b/aws_lambda/context.py new file mode 100644 index 0000000..6f24c19 --- /dev/null +++ b/aws_lambda/context.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +class MockContext(object): + def __init__(self, context): + self.function_name = 'Mock' + self.function_version = 'm1' + self.memory_limit_in_mb = '500' + self.aws_request_id = None + self.log_groupd_name = None + self.log_stream_name = None + self.identiy = FakeObject() + self.client_context = ClientContext(context) + + def get_remaining_time_in_millis(self): + # Returns the remaining execution time, in milliseconds, until + # AWS Lambda terminates the function. + return 0 + + +class FakeObject(object): + def __init__(self, context=None): + self._context = context or {} # if None was passed + + def __getattr__(self, attr): + return self._context.get(attr, None) + + +class ClientContext(object): + def __init__(self, context): + self.custom = FakeObject(context) + self.client = FakeObject() + self.env = {} diff --git a/aws_lambda/project_template/context.json b/aws_lambda/project_template/context.json new file mode 100644 index 0000000..8a79687 --- /dev/null +++ b/aws_lambda/project_template/context.json @@ -0,0 +1,3 @@ +{ + "foo": "bar" +} \ No newline at end of file diff --git a/aws_lambda/project_template/service.py b/aws_lambda/project_template/service.py index e5bcb68..644c417 100644 --- a/aws_lambda/project_template/service.py +++ b/aws_lambda/project_template/service.py @@ -3,6 +3,8 @@ def handler(event, context): # Your code goes here! + context_val = context.client_context.custom.foo + assert context_val == 'bar' e = event.get('e') pi = event.get('pi') return e + pi diff --git a/scripts/lambda b/scripts/lambda index af5b0a1..a8786c0 100755 --- a/scripts/lambda +++ b/scripts/lambda @@ -27,9 +27,10 @@ def build(): @click.command(help="Run a local test of your function.") @click.option('--event-file', default=None, help='Alternate event file.') +@click.option('--context-file', default=None, help='Alternate context file.') @click.option('--verbose', '-v', is_flag=True) -def invoke(event_file, verbose): - aws_lambda.invoke(CURRENT_DIR, event_file, verbose) +def invoke(event_file, context_file, verbose): + aws_lambda.invoke(CURRENT_DIR, event_file, context_file, verbose) @click.command(help="Register and deploy your code to lambda.")