def lambda_task(func): """ Decorator to make function run under zappa environment. Args: func (function): the function to be wrapped Further requirements: - func must be an independent top-level function. i.e. not a class method or an anonymous function - args & kwargs must be serializable Usage: @lambda_task def render(question_id): question.render() # You can call above function wherever you want; render(question_id=1) # and zappa task (which is actually AWS lambda function) will be invoked. """ task_path = get_func_task_path(func) def _run_async(*args, **kwargs): """ This is the wrapping async function that replaces the decorated function. Returns: The object returned includes state of the dispatch. """ send_result = CustomLambdaAsyncResponse().send(task_path, args, kwargs) return send_result update_wrapper(_run_async, func) _run_async.service = 'lambda' _run_async.sync = func return _run_async
def test_async_call_with_defaults(self): """Change a task's asynchronousity at runtime.""" # Import the task first to make sure it is decorated whilst the # environment is unpatched. async_me = import_and_get_task("tests.test_app.async_me") lambda_async_mock = mock.Mock() lambda_async_mock.return_value.send.return_value = "Running async!" with mock.patch.dict('zappa.async.ASYNC_CLASSES', {'lambda': lambda_async_mock}): # First check that it still runs synchronously by default self.assertEqual(async_me("123"), "run async when on lambda 123") # Now patch the environment to make it look like we are running on # AWS Lambda options = { 'AWS_LAMBDA_FUNCTION_NAME': 'MyLambda', 'AWS_REGION': 'us-east-1' } with mock.patch.dict(os.environ, options): self.assertEqual(async_me("qux"), "Running async!") # And check the dispatching class got called correctly lambda_async_mock.assert_called_once_with( aws_region='us-east-1', capture_response=False, delay_seconds=0, lambda_function_name="MyLambda") lambda_async_mock.return_value.send.assert_called_with( get_func_task_path(async_me), ("qux", ), {})
def test_async_call_with_defaults(self): """Change a task's asynchronousity at runtime.""" # Import the task first to make sure it is decorated whilst the # environment is unpatched. async_me = import_and_get_task("tests.test_app.async_me") lambda_async_mock = mock.Mock() lambda_async_mock.return_value.send.return_value = "Running async!" with mock.patch.dict('zappa.async.ASYNC_CLASSES', {'lambda': lambda_async_mock}): # First check that it still runs synchronously by default self.assertEqual(async_me("123"), "run async when on lambda 123") # Now patch the environment to make it look like we are running on # AWS Lambda options = { 'AWS_LAMBDA_FUNCTION_NAME': 'MyLambda', 'AWS_REGION': 'us-east-1' } with mock.patch.dict(os.environ, options): self.assertEqual(async_me("qux"), "Running async!") # And check the dispatching class got called correctly lambda_async_mock.assert_called_once() lambda_async_mock.assert_called_with(aws_region='us-east-1', capture_response=False, lambda_function_name="MyLambda") lambda_async_mock.return_value.send.assert_called_with( get_func_task_path(async_me), ("qux",), {})
def test_async_sqs_call(self): """ Call a task with sqs async service. """ async_sqs_me = import_and_get_task("tests.test_app.async_sqs_me") sqs_client_mock = mock.Mock() sqs_client_mock.get_queue_url = mock.MagicMock( return_value={ 'QueueUrl': 'https://us-east-1.queue.amazonaws.com/1' }) sqs_client_mock.send_message = mock.MagicMock( return_value={ 'MD5OfMessageBody': 'string', 'MD5OfMessageAttributes': 'string', 'MessageId': '1234', 'SequenceNumber': '1' }) with mock.patch('zappa.async.SQS_CLIENT', sqs_client_mock, create=True): # First check that it still runs synchronously by default self.assertEqual(async_sqs_me("123"), "run async with sqs service when on lambda 123") # Now patch the environment to make it look like we are running on # AWS Lambda options = { 'AWS_LAMBDA_FUNCTION_NAME': 'MyLambda', 'AWS_REGION': 'us-east-1' } with mock.patch.dict(os.environ, options): async_sqs_me("qux") # And check the sqs client got invoked correctly sqs_client_mock.get_queue_url.assert_called_once_with( QueueName='MyLambda-zappa-async') sqs_client_mock.send_message.assert_called_once_with( QueueUrl='https://us-east-1.queue.amazonaws.com/1', MessageBody=json.dumps({ "task_path": get_func_task_path(async_sqs_me), "capture_response": False, "response_id": None, "args": ["qux"], "kwargs": {}, "zappaAsyncCommand": "zappa.async.route_sqs_task" }), DelaySeconds=0)
def send_to_other_lambda(function_in_lambda, *args, **kwargs): """ Call the function_in_lambda in another lambda instead of the running one and return the result. :param function_in_lambda: The function to call. :param args: The arguments to this function. :param kwargs: The lwargs to this function. :return: The result of this call. """ lambda_function_name = os.environ.get('AWS_LAMBDA_FUNCTION_NAME') aws_region = os.environ.get('AWS_REGION') task_path = get_func_task_path(function_in_lambda) result = LambdaSyncResponse(lambda_function_name=lambda_function_name, aws_region=aws_region).send( task_path, args, kwargs).response return result
def run_lambda(data, map_function, compression, invoke_lambda): """ Run a given map_function on the given data and return the result. For this: * the data is encoded (using compression or not), * a lambda function is invoked, which decoded the data, calls the map_function and encoded the result again * the result is decoded again and returned. :param data: The data that is sent to the lambda function :param map_function: The function that is called in the lambda on the data. :param compression: Turn on compression during streaming or not. :param invoke_lambda: Call another lambda or do everything in this lambda :return: The result of the function call. """ encoded_data = encode_payload(data, compression) map_function = get_func_task_path(map_function) if not invoke_lambda: encoded_result = function_in_lambda(encoded_data, map_function, compression) else: encoded_result = send_to_other_lambda(function_in_lambda, encoded_data, map_function, compression) return decode_payload(encoded_result, compression)
def test_nofails_funcs(self): funk = import_and_get_task("tests.test_app.schedule_me") get_func_task_path(funk) is_from_router()
def test_nofails_funcs(self): funk = import_and_get_task("tests.test_app.async_me") get_func_task_path(funk) self.assertEqual(funk.__name__, 'async_me')