Forked from alexcasalboni/aws-lambda-static-type-checker.md
Created
September 20, 2021 04:34
-
-
Save vinitkumar/0f5abbeb13b3610fda239cda03eded17 to your computer and use it in GitHub Desktop.
Revisions
-
alexcasalboni revised this gist
Jan 6, 2018 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed. -
alexcasalboni revised this gist
Jan 6, 2018 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -43,6 +43,7 @@ Check out the two Lambda Functions below for more examples. * Run `mypy YOURFILE.py` or add `mypy` to your IDE linting configuration (for VSCode, you'll need to enable the `python.linting.mypyEnabled` setting) * Create a local `lambda_types.py` file (you can find it below) and customize it as needed * Learn more about the built-in `typing` module [here](https://docs.python.org/3/library/typing.html) * (Just FYI: you can run all the tests below with [Nose](http://nose.readthedocs.io/): `pip install nose` and then simply run `nosetests`) ## Tips & Tricks -
alexcasalboni revised this gist
Jan 6, 2018 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -51,5 +51,6 @@ Check out the two Lambda Functions below for more examples. * Make sure your functions/methods are annotated if you want them to be checked * You can use either annotations (natively supported only by Python3, sometimes bring to decreased readability) or special comments (e.g. `# type: str`) * You don't have to annotate every single function/file of your codebase to benefit from static type checking (i.e. you could focus on critical or semantically ambiguous/complex sections) * Writing tests becomes easier/faster as well since many type-related errors will be detected at "compile time" and you won't have to unit-test them (for example, see the `lambda_repeat.get_output` function below) * You can customize `LambdaDict` based on your own event structures and conventions. For example, you may want to use `Dict[str, str]` or `Dict[str, Dict[str, Any]]` instead of `Dict[str, Any]` * Thanks to code completion, you won't have to memorize all the [LambdaContext](https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html) attributes and methods (e.g. `context.get_remaining_time_in_millis()`, `context.client_context.client.installation_id`, etc.) -
alexcasalboni created this gist
Jan 6, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,55 @@ # How to use Python3 Type Hints in AWS Lambda *TL;DR* > Static Type Checkers help you find simple (but subtle) bugs in your Python code. Check out `lambda_types.py` and incrementally improve your code base and development/debugging experience with type hints. Your Lambda Function code will go from this: ```python def handler(event, context): first_name = event.get('first_name') or 'John' last_name = event.get('last_name') or 'Smith' return { 'message': get_message(first_name, last_name), } def get_message(first_name, last_name): return 'Hello {} {}!'.format(first_name, last_name) ``` to this: ```python def handler(event: LambdaDict, context: LambdaContext) -> LambdaDict: first_name: str = event.get('first_name') or 'John' last_name: str = event.get('last_name') or 'Smith' return { 'message': get_message(first_name, last_name), } def get_message(first_name: str, last_name: str): return 'Hello {} {}!'.format(first_name, last_name) ``` Check out the two Lambda Functions below for more examples. # Instructions * Create a Python3 virtual env with `virtualenv venv --python=python3 && source venv/bin/activate` * Install [mypy](http://mypy-lang.org/) with `pip install mypy` * Run `mypy YOURFILE.py` or add `mypy` to your IDE linting configuration (for VSCode, you'll need to enable the `python.linting.mypyEnabled` setting) * Create a local `lambda_types.py` file (you can find it below) and customize it as needed * Learn more about the built-in `typing` module [here](https://docs.python.org/3/library/typing.html) ## Tips & Tricks * Static code annotations will not affect your code execution, as they are only useful for static checks and code completion * Of course, static typing works fine with Python classes as well * Make sure your functions/methods are annotated if you want them to be checked * You can use either annotations (natively supported only by Python3, sometimes bring to decreased readability) or special comments (e.g. `# type: str`) * You don't have to annotate every single function/file of your codebase to benefit from static type checking (i.e. you could focus on critical or semantically ambiguous/complex sections) * You can customize `LambdaDict` based on your own event structures and conventions. For example, you may want to use `Dict[str, str]` or `Dict[str, Dict[str, Any]]` instead of `Dict[str, Any]` * Thanks to code completion, you won't have to memorize all the [LambdaContext](https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html) attributes and methods (e.g. `context.get_remaining_time_in_millis()`, `context.client_context.client.installation_id`, etc.) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,19 @@ """ Example #1 """ import os from lambda_types import LambdaDict, LambdaContext MSG_TEMPLATE: str = os.environ.get('MSG_TEMPLATE') or 'Hello {} {}!' STAGE: str = os.environ.get('stage') or 'dev' def handler(event: LambdaDict, context: LambdaContext) -> LambdaDict: print('Received event {} for stage {}'.format(event, STAGE)) first_name: str = event.get('first_name') # optional last_name: str = event.get('last_name') # optional return { 'message': get_message(first_name, last_name), } def get_message(first_name: str = 'John', last_name: str = 'Smith'): return MSG_TEMPLATE.format(first_name, last_name) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,19 @@ """ Example #2 """ import os from lambda_types import LambdaDict, LambdaContext N: int = int(os.environ.get('N') or 10) STAGE: str = os.environ.get('stage') or 'dev' def handler(event: LambdaDict, context: LambdaContext) -> LambdaDict: print('Received event {} for stage {}'.format(event, STAGE)) input: str = event['input'] # required return { 'output': get_output(input, N), } def get_output(input: str, num: int): """ Return the input string repeated N times. """ return input * num This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,39 @@ """ Note: this code is used only by the static type checker! """ from typing import Dict, Any LambdaDict = Dict[str, Any] class LambdaCognitoIdentity(object): cognito_identity_id: str cognito_identity_pool_id: str class LambdaClientContextMobileClient(object): installation_id: str app_title: str app_version_name: str app_version_code: str app_package_name: str class LambdaClientContext(object): client: LambdaClientContextMobileClient custom: LambdaDict env: LambdaDict class LambdaContext(object): 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: LambdaCognitoIdentity client_context: LambdaClientContext @staticmethod def get_remaining_time_in_millis() -> int: return 0 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,89 @@ import unittest from lambda_types import LambdaDict, LambdaContext from lambda_message import handler as handler_message, get_message from lambda_repeat import handler as handler_repeat, get_output class TestMessageFunction(unittest.TestCase): def setUp(self): self.context = LambdaContext() def test_handler(self) -> None: event: LambdaDict = { "first_name": "Alex", "last_name": "Casalboni", } result = handler_message(event, self.context) self.assertIn('message', result) def test_handler_empty(self) -> None: event: LambdaDict = {} result = handler_message(event, self.context) self.assertIn('message', result) def test_message_default(self) -> None: msg = get_message() self.assertIsInstance(msg, str) self.assertIn('Hello', msg) self.assertIn('John', msg) self.assertIn('Smith', msg) self.assertTrue(msg.endswith('!')) def test_message_firstname(self) -> None: msg = get_message(first_name='Charlie') self.assertIsInstance(msg, str) self.assertIn('Hello', msg) self.assertIn('Charlie', msg) self.assertIn('Smith', msg) self.assertTrue(msg.endswith('!')) def test_message_lastname(self) -> None: msg = get_message(last_name='Brown') self.assertIsInstance(msg, str) self.assertIn('Hello', msg) self.assertIn('John', msg) self.assertIn('Brown', msg) self.assertTrue(msg.endswith('!')) def test_message(self) -> None: msg = get_message(first_name='Charlie', last_name='Brown') self.assertIsInstance(msg, str) self.assertIn('Hello', msg) self.assertIn('Charlie', msg) self.assertIn('Brown', msg) self.assertTrue(msg.endswith('!')) class TestRepeatFunction(unittest.TestCase): def setUp(self): self.context = LambdaContext() def test_handler(self) -> None: event: LambdaDict = { "input": "NaN", } result = handler_repeat(event, self.context) self.assertIn('output', result) self.assertEqual(30, len(result['output'])) def test_handler_empty(self) -> None: event: LambdaDict = {} with self.assertRaises(KeyError): handler_repeat(event, self.context) def test_repeat_empty_string(self) -> None: output = get_output('', 100) self.assertIsInstance(output, str) self.assertEqual(0, len(output)) def test_repeat_zero(self) -> None: output = get_output('hello', 0) self.assertIsInstance(output, str) self.assertEqual(0, len(output)) def test_repeat(self) -> None: output = get_output('hello', 10) self.assertIsInstance(output, str) self.assertEqual(50, len(output))