■python-lambda-local を試してみる
余分な工程を減らすことが目的
後でgit管理出来るよう、PEP 8に準拠したソースをローカルに保持しておく
コードを書く←ここ
Zipでひとつにまとめてアップロード
サンプルイベントをインプットにして Lambda 関数をテスト実行
CloudWatch Logs でログを確認してデバッグ
■まずは仮想環境(venv)に入る
$ mkdir lambda-local && cd lambda-local
$ python3 -m venv venv
$ source venv/bin/activate
$ pip --version
pip 23.0.1 from /home/bookworm/lambda-local/venv/lib/python3.11/site-packages/pip (python 3.11)
■python-lambda-local のインストール
$ pip install python-lambda-local
$ python-lambda-local -h | lsec usage
usage: python-lambda-local [-h] [-l LIBRARY_PATH] [-f HANDLER_FUNCTION]
[-t TIMEOUT] [-a ARN_STRING] [-v VERSION_NAME]
[-e ENVIRONMENT_VARIABLES] [--version]
FILE EVENT
$ pip freeze > requirements.txt
■debin-bookwormでのrequirements.txt
CloudShellと比べてurllib3のバージョンが新しい
$ cat requirements.txt
boto3==1.34.70
botocore==1.34.70
jmespath==1.0.1
python-dateutil==2.9.0.post0
python-lambda-local==0.1.13
s3transfer==0.10.1
six==1.16.0
urllib3==2.2.1
■aws CloudShellでのrewuirements.txt
debianと比べてurllib3のバージョンが古い
$ cat requirements.txt
boto3==1.34.70
botocore==1.34.70
jmespath==1.0.1
python-dateutil==2.9.0.post0
python-lambda-local==0.1.13
s3transfer==0.10.1
six==1.16.0
urllib3==1.26.18
■どちらでもインストール出来るように requirements.txt を書き換える
これで現バージョン相当をどこにでも作れるようになった
$ cat requirements.txt
boto3==1.34.70
botocore==1.34.70
jmespath==1.0.1
python-dateutil==2.9.0.post0
python-lambda-local==0.1.13
s3transfer==0.10.1
six==1.16.0
urllib3>=1.26.18
$ pip install -r requirements.txt
■lambdaでは「ファイル名.ハンドラ名」でハンドラが作成される
HelloWorldアプリを作成して実行
Duration: 0.36 msなので
$ tree HelloWorld/
HelloWorld/
├── app
│ └── lambda_function.py
└── test
└── event.json
$ find HelloWorld/ -type f \! -name "*.pyc" -exec awk '{print FILENAME,$0}' {} +
HelloWorld/test/event.json {}
HelloWorld/app/lambda_function.py def lambda_handler(event, context):
HelloWorld/app/lambda_function.py return "Hello world"
$ cd HelloWorld/;python-lambda-local -f lambda_handler app/lambda_function.py test/event.json;cd ../
[root - INFO - 2024-03-26 23:05:08,173] Event: {}
[root - INFO - 2024-03-26 23:05:08,173] START RequestId: 9ba20fcc-3b02-4dc6-838a-40bf2216d9c2 Version:
[root - INFO - 2024-03-26 23:05:08,185] END RequestId: 9ba20fcc-3b02-4dc6-838a-40bf2216d9c2
[root - INFO - 2024-03-26 23:05:08,186] REPORT RequestId: 9ba20fcc-3b02-4dc6-838a-40bf2216d9c2 Duration: 0.36 ms
[root - INFO - 2024-03-26 23:05:08,186] RESULT:
Hello world
■jsonの値を受け取って表示する、バージョン情報とタイムアウトを1秒にして実行
$ find HelloWorld_0.1/ -type f \! -name "*.pyc" -exec awk '{print FILENAME":"$0}' {} +
HelloWorld_0.1/event.json:{
HelloWorld_0.1/event.json: "id": "sample_001",
HelloWorld_0.1/event.json: "name": "sample name",
HelloWorld_0.1/event.json: "description":"sample description"
HelloWorld_0.1/event.json:}
HelloWorld_0.1/lambda_function.py:import json
HelloWorld_0.1/lambda_function.py:
HelloWorld_0.1/lambda_function.py:print('Loading function')
HelloWorld_0.1/lambda_function.py:
HelloWorld_0.1/lambda_function.py:
HelloWorld_0.1/lambda_function.py:def lambda_handler(event, context):
HelloWorld_0.1/lambda_function.py: print("id = " + event['id'])
HelloWorld_0.1/lambda_function.py: print("name " + event['name'])
HelloWorld_0.1/lambda_function.py: print("description = " + event['description'])
HelloWorld_0.1/lambda_function.py: return event['id']
HelloWorld_0.1/lambda_function.py:
■構文チェックのfalke8を入れる
$ pip install flake8
$ pip freeze > requirements.txt.flake8
$ sdiff -s requirements.txt*
> flake8==7.0.0
> mccabe==0.7.0
> pycodestyle==2.11.1
> pyflakes==3.2.0
■flake8でPEP 8の定義と照らす
$ flake8 lambda_function.py
lambda_function.py:1:1: F401 'json' imported but unused
lambda_function.py:11:5: E265 block comment should start with '# '
$ pip install black
$ pip freeze > requirements.txt.black
■「black」を入れてPEP 8を強制する
ダブルクォーテーションとシングルクォーテーションの取り扱いが変わる
$ sdiff -s requirements.txt.{flake8,black}
> black==24.3.0
> click==8.1.7
> mypy-extensions==1.0.0
> packaging==24.0
> pathspec==0.12.1
> platformdirs==4.2.0
■ところでディレクトリ階層は不要なので「app」や「test」をやめて、blackコマンドを適用する
$ cp lambda_function.py{,.backup}
$ black lambda_function.py
reformatted lambda_function.py
All done! ✨ 🍰 ✨
1 file reformatted.
$ flake8 lambda_function.py
lambda_function.py:1:1: F401 'json' imported but unused
$ sdiff -s lambda_function.py{,.backup}
print("Loading function") | print('Loading function')
print("id = " + event["id"]) | print("id = " + event['id'])
print("name " + event["name"]) | print("name " + event['name'])
print("description = " + event["description"]) | print("description = " + event['description'])
return event["id"]
■変更前に確認するなら「--diff」オプションで
$ black --help | lsec -sep "^ -" --diff
--color / --no-color Show (or do not show) colored diff. Only
applies when --diff is given.
--diff Don't write the files back, just output a
diff to indicate what changes Black would've
made. They are printed to stdout so
capturing them is simple.
■isortでimport文を修正する
blackはimport文をチェックしない、isortは順序しかチェックしない
$ pip install isort
$ pip freeze > requirements.txt.isort
$ sdiff -s requirements.txt{.black,.isort}
> isort==5.13.2
$ isort --diff lambda_function.py
■元のソースとの差異を確認
lambdaのバージョン指定は整数値の様子。
$ sdiff -s lambda_function.py{,.backup}
print("Loading function") | import json
>
> print('Loading function')
print("id = " + event["id"]) | print("id = " + event['id'])
print("name " + event["name"]) | print("name " + event['name'])
print("description = " + event["description"]) | print("description = " + event['description'])
return event["id"]
$ python-lambda-local -f lambda_handler lambda_function.py event.json -t 1 -v 1
[root - INFO - 2024-03-30 23:25:39,422] Event: {'id': 'sample_001', 'name': 'sample name', 'description': 'sample description'}
[root - INFO - 2024-03-30 23:25:39,422] START RequestId: 35478441-19b5-4297-823e-f43acf65bc1f Version: 1
Loading function
id = sample_001
name sample name
description = sample description
[root - INFO - 2024-03-30 23:25:39,435] END RequestId: 35478441-19b5-4297-823e-f43acf65bc1f
[root - INFO - 2024-03-30 23:25:39,435] REPORT RequestId: 35478441-19b5-4297-823e-f43acf65bc1f Duration: 0.63 ms
[root - INFO - 2024-03-30 23:25:39,435] RESULT:
sample_001
$ zip IdNameDesc.zip lambda_function.py event.json
adding: lambda_function.py (deflated 39%)
adding: event.json (deflated 38%)
■lambdaで実行
バージョンを指定すると、「$LATEST」箇所が整数値に変わる
Loading function
START RequestId: XXXXXXXX-0063-4c8c-b39c-3c049d7ed9c5 Version: $LATEST
id = sample_001
name sample name
description = sample description
END RequestId: XXXXXXXX-0063-4c8c-b39c-3c049d7ed9c5
REPORT RequestId: XXXXXXXX-0063-4c8c-b39c-3c049d7ed9c5 Duration: 2.35 ms Billed Duration: 3 ms Memory Size: 128 MB Max Memory Used: 34 MB Init Duration: 83.05 ms
■同様に
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sns-create-package.html
$ flake8 lambda_handler.py
lambda_handler.py:2:1: F401 'json' imported but unused
lambda_handler.py:5:1: E302 expected 2 blank lines, found 1
lambda_handler.py:6:5: E265 block comment should start with '# '
$ cp lambda_handler.py{,.backup}
$ black lambda_handler.py
reformatted lambda_handler.py
All done! ✨ 🍰 ✨
1 file reformatted.
■ちなみにコードを zip 化してアップロードできるlambda-uploaderは更新されていないので使わないことにした。
$ pip_search lambda-uploader | grep lambda-uploader
🐍 https://pypi.org/search/?q=lambda-uploader 🐍
│ 📂 lambda-uploader │ 1.3.0 │ 14-05-2018 │ AWS Python Lambda Packager
■ところで、解説用のドキュメントファイルがあるならまだしも、
動くコードしかないのであれば別のイライラポイントの発生源となるので、
「Javadoc コメント」のような共通テンプレートをはやめに用意するべき
※Javaコーディング規約参照
https://future-architect.github.io/coding-standards/documents/forJava/Java%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E8%A6%8F%E7%B4%84.html
Javadoc コメントには、少なくとも author と version(クラス)、param と return と exception(メソッド)を記述する
今後もバージョンアップのリリースが予定されているソースでは、上記に加えて since(バージョン)を記述する
@Overrideのあるメソッドでは、上記に加えて{@Inherit}を記述する