開発環境のセットアップ
まずはローカル環境をセットアップするための方法としてPoetryというパッケージマネージャーを使う方法とVenv + Pipを使う方法を紹介します。どちらか一方を好みやチームの方針に合わせて選ぶとよいでしょう。
Poetryを使った環境構築
PoetryはPythonのパッケージマネージャーです。プロジェクトごとに必要なライブラリをインストールして依存関係を管理、更新を簡単に行うことが出来ます。Nodejsのnpm等と同じように専用のロックファイルを作成することで、バージョン管理ツール等で配布しても確実に同じバージョンかつ依存関係の保ったパッケージ群を構成することが出来ます。
Linux, macOS, Windows (WSL)環境の場合は以下のコマンドで公式のインストーラーからPoetryのインストールが可能です
$ curl -sSL https://install.python-poetry.org | python3 -
また、pipxを使用している場合は以下のコマンドでもインストールが可能です
$ pipx install poetry
まずは以下のコマンドでプロジェクトを作成します。
$ poetry new <project name>
プロジェクトを作成すると以下のようなディレクトリ構成が作成されます。python_lambda
配下にこのプロジェクトのメインのソースコードは記述していき、それに対応するテストコードをtests
配下においていきます。インストールしたライブラリ等はpyproject.toml
にて管理されます。このファイルはTOML (Tom's Obvious, Minimal Language) という形式で書かれています。TOMLは設定ファイルやデータのシリアライズ形式の一つです。主にシンプルで人間が読みやすく、かつ機械的に解析しやすいことを目指して設計されています。poetry.lock
ファイルには、プロジェクトに必要なすべてのパッケージとその正確なバージョンが記録されています。これにより、異なる環境で同じ依存関係がインストールされ、予期せぬ問題を防ぐことができます。poetry.lock
に記録されたバージョンを基に、他の開発者やCI/CD環境でも同じ依存関係がインストールされるので、環境間での一貫性が保たれます。
python_lambda
├── README.md
├── poetry.lock
├── pyproject.toml
├── python_lambda
│ └── __init__.py
└── tests
└── __init__.py
poetry add
コマンドを使うとPython プロジェクトに新しい依存パッケージを追加する事ができます。指定したパッケージをプロジェクトの依存関係にインストールし、pyproject.toml
ファイルと poetry.lock
ファイルを自動的に更新します。requestsライブラリをインストールする場合は以下のようにします。インストールと同時に、pyproject.toml
ファイルの [tool.poetry.dependencies]
セクションに追加されます。また、poetry.lock
ファイルも更新されて、依存関係のバージョンが固定されます。
$ poetry add requests
開発環境専用の依存関係を追加するには、--dev
フラグを使用します。このオプションは、ユニットテスト、リンティング、ドキュメント生成などに必要なツールをインストールする際に便利です。
$ poetry add --dev pytest
これにより、pytest
が開発用の依存関係としてインストールされ、pyproject.toml
ファイルの [tool.poetry.dev-dependencies]
セクションに追加されます。以下が、requests
とpytest
をインストールした状態のpyproject.toml
です。
[tool.poetry]
name = "python-lambda"
version = "0.1.0"
description = ""
authors = ["horike37 <horike@serverless.co.jp>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.32.3"
[tool.poetry.dev.dependencies]
pytest = "^8.3.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Venvとpipを使った環境構築
VenvとはプロジェクトごとにPythonの環境を分離してくれるための仕組みです。これにより、pipでインストールされたライブラリはそのプロジェクトのみで使用できるようになります。これにより、開発者は異なるプロジェクト間での依存関係の競合を防ぎ、環境の一貫性を保つことができます。
プロジェクトのルートフォルダで以下のコマンドを実行することで専用の環境を作り出し有効化してくれます。
$ python3 -m venv venv
$ . venv/bin/activate
その後、requirements.txt
をインストールするようにします。
$ pip3 install -r requirements.txt
パッケージマネージャーとは、そのアプリケーション用にインストールされた外部ライブラリを記録し、新しいライブラリのインストール・新しいバージョンへの更新・以前インストールしたライブラリの削除を容易に行えるようにするためのツールです。Pythonではpip
というパッケージマネージャーがよく利用されます。
以下のコマンドで必要なライブラリをインストールします。
$ pip3 install boto3
そして一度インストールしたライブラリは以下のコマンドで表示できます。
$ pip3 freeze
agate==1.6.0
agate-dbf==0.2.0
agate-excel==0.2.1
agate-sql==0.5.2
そして、以下のように requirements.txt
というファイルにインストール済みのライブラリを書き込みます。最終的にそれをGitにコミットすることで使用するライブラリとそのバージョンを管理します。
$ pip3 freeze > requirements.txt
他のチームメンバーは以下のコマンドでrequirements.txt
に書かれたライブラリをローカル環境にインストールします。
$ pip3 install -r requirements.txt
ローカル開発の方法
ローカル開発ではSAM Localを使用することをお勧めします。AWSでクラウドネイティブ(サーバレス)な開発を行うために覚えておくべき開発のワークフローの記事の中で詳しく使用方法について述べていますので参照してみてください
Linterによる静的構文解析
ソースコードの静的構文解析は、開発において非常に重要な役割を果たします。誤った記法や不適切なコーディングスタイルを早期に検出することで、チーム全体で統一されたコード品質を保つことができ、後々のトラブルを未然に防ぐことができます。特に、大規模なプロジェクトや複数の開発者が関わるプロジェクトでは、コーディングルールに従った一貫性のあるコードを書くことが重要であり、静的構文解析はそのための強力な手段です。
静的構文解析ツールを導入することで、コードがデプロイされる前に潜在的なエラーや警告を検出し、修正することが可能になります。これにより、デプロイ後に予期せぬエラーが発生し、緊急対応を余儀なくされるリスクが大幅に低減されます。結果として、開発プロセス全体の効率が向上し、よりスムーズにプロジェクトを進めることができるのです。
Python+AWS Lambdaの開発においては、flake8 という静的構文解析ツールを使用することをお勧めします。flake8
は、Pythonの公式コーディングスタイルガイドである PEP 8 に準拠したコードの検査を行います。PEP 8 は、Pythonコードの読みやすさやメンテナンス性を高めるために提唱されたガイドラインであり、これに従うことで、コードベースの一貫性が保たれ、他の開発者がコードを理解しやすくなります。
ユニットテスト
Pythonでのユニットテストは、pytest というライブラリを活用することをお勧めします。Pythonの中では一番メジャーなテスト用のライブラリでしょう。pytest
は、シンプルでありながら強力な機能を持ち、ユニットテストの記述から実行まで、Pythonでのテストプロセスを包括的にサポートしてくれるオールインワンのフレームワークです。
pytest
の最大の魅力は、その使いやすさと柔軟性にあります。テストコードは非常に簡潔に書くことができ、わずかな記述で強力なテストを行うことが可能です。また、従来のPython標準ライブラリである unittest
モジュールと互換性があり、既存のテストコードをそのまま pytest
に移行することもできます。さらに、pytest
はデフォルトで非常に分かりやすいエラーメッセージを提供し、どのテストが失敗したのかを一目で理解できるように工夫されています。
pytest
は、シンプルなユニットテストだけでなく、複雑なテストシナリオにも対応しています。たとえば、テスト関数に複数のパラメータを渡して同じテストを異なる条件下で実行する「パラメトリックテスト」や、テストの前後に特定のセットアップやクリーンアップ処理を行う「フィクスチャ」の機能を利用することができます。これにより、再利用可能で効率的なテストコードを簡単に書くことができるようになります。
AWS Lambda Python Power Tools
AWS Lambda Python Power ToolsはPythonでのLambda開発で便利なトレーシングやデコレータ、ロギング、メトリクス、イベントハンドラーなどの機能を提供してくれるツールキットです。必ずインストールして開発を行うことをお勧めします。主に以下のような機能が提供されています。
ユーティリティ | 説明 |
---|---|
Tracing | Lambda関数ハンドラー、および同期・非同期関数をトレースするためのデコレータとユーティリティ |
Logger | 構造化ロギングを簡単にし、Lambdaコンテキストの重要な詳細情報で構造化ロギングを強化するデコレータ |
Metrics | CloudWatch Embedded Metric Format (EMF) を使用して非同期に作成されたカスタムメトリクス |
AppSync | Lambda Direct Resolver および Amplify GraphQL Transformer 関数用の AppSync イベントハンドラー |
API Gateway, ALB, Lambda Function URL | プロキシ統合を使用して起動された Lambda 関数用の Amazon API Gateway REST/HTTP API および ALB イベントハンドラー、Lambda Function URL |
Middleware factory | 各 Lambda 呼び出しの前後にロジックを実行するための独自のミドルウェアを作成するデコレータファクトリー |
Parameters | AWS Systems Manager Parameter Store、AWS Secrets Manager、または Amazon DynamoDB からパラメータ値を取得し、特定の時間キャッシュする |
Batch processing | AWS SQS バッチ処理の部分的な失敗を処理する |
Typing | IDEでの開発を迅速化するための静的型付けクラス |
Validation | インバウンドイベントやレスポンスのための JSON Schema バリデーター |
Event source data classes | 一般的な Lambda イベントトリガーのスキーマを記述するデータクラス |
Parser | Pydantic を使用したデータの解析と深いバリデーション |
Idempotency | 冪等な Lambda ハンドラー |
Data Masking | 機密データの削除や暗号化を簡単に行うための機能 |
Feature Flags | 入力に応じて一つ以上のフィーチャーを有効にするかどうかを評価するシンプルなルールエンジン |
Streaming | 利用可能なメモリより大きなデータセットをストリーミングデータとしてストリーム処理 |
Poetryやpipを使ってインストールが可能ですが、SAM CLIを使うことで以下のようにサンプルコードとともにAWS Lambda Python Power Toolsを組み込んだプロジェクトが生成可能です。これにより、開発者はすぐにプロジェクトの実装を始めることができ、複雑なセットアップを行う必要がありません。また、SAM CLIのテンプレートを利用することで、ベストプラクティスに従ったLambda関数の構築が容易に行えるため、より効率的な開発プロセスが実現します。
$ sam init --app-template hello-world-powertools-python --name sam-app --package-type Zip --runtime python3.11 --no-tracing
すると以下のようなhello worldのサンプルと共にプロジェクトが展開されます。Rest APIでhello worldのメッセージを返すのみのソースコードです。こちらを元にしてAWS Lambda Python Power Toolsの使い方を学んでいくのが良いでしょう。
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools import Logger
from aws_lambda_powertools import Tracer
from aws_lambda_powertools import Metrics
from aws_lambda_powertools.metrics import MetricUnit
app = APIGatewayRestResolver()
tracer = Tracer()
logger = Logger()
metrics = Metrics(namespace="Powertools")
@app.get("/hello")
@tracer.capture_method
def hello():
# adding custom metrics
# See: https://awslabs.github.io/aws-lambda-powertools-python/latest/core/metrics/
metrics.add_metric(name="HelloWorldInvocations", unit=MetricUnit.Count, value=1)
# structured log
# See: https://awslabs.github.io/aws-lambda-powertools-python/latest/core/logger/
logger.info("Hello world API - HTTP 200")
return {"message": "hello world"}
# Enrich logging with contextual information from Lambda
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
# Adding tracer
# See: https://awslabs.github.io/aws-lambda-powertools-python/latest/core/tracer/
@tracer.capture_lambda_handler
# ensures metrics are flushed upon request completion/failure and capturing ColdStart metric
@metrics.log_metrics(capture_cold_start_metric=True)
def lambda_handler(event: dict, context: LambdaContext) -> dict:
return app.resolve(event, context)