serverless framework の記事ばかり投稿している遠藤です。
serverless はとても便利なのですが、個人的に惜しい点が1つあり、それが PyCharm で step 実行できない(できるのかもしれないけど、正しいやり方を知らない)点です。
今回は、応急処置的に、 break point を張って step 実行する方法を備忘録として記載いたします。
ちょっとした小ネタ記事になりますのでご了承ください。
環境
- OS: macOS Monterey
- python: 3.6.9
- PyCharm: 2021.1.2(Professional Eddition)
- serverless:
- Framework Core: 2.71.0
- Plugin: 5.5.3
- SDK: 4.3.0
- Components: 3.18.1
コードをいじる
まず、 step 実行したい function の handler に少し手を加えます。
serverless.yml に以下のような定義をしていた場合
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
実行部分は以下のようになっているかと思います。
import json
def hello(event, context):
response = {"statusCode": 200, "body": "body"}
return response
この先頭部分に、 sleep を入れます。
個人的には 20 秒くらいがおすすめです。
import json
import time # ← 追加
def hello(event, context):
time.sleep(20) # ← 追加
response = {"statusCode": 200, "body": "body"}
return response
BreakPoint を張る
次に、 sleep の後ろくらいで、 適当に break point を張っておきます。
以下のように、赤枠を囲ったあたりをクリックすると、赤い丸印が付き、処理が入ってきた時に、一時停止できます。
serverless を実行する
続いて serverless を local 実行します。
実行方法は、
$ serverless invoke local -f {function_name}
でも
$ serverless offline start # (要プラグイン)
でも構いません。
API ではない場合などもあるので、 invoke
の方が確実です。
offline start
を実行した場合は、起動後に curl などで get なり post などを叩きましょう。
Process に attach する
実行開始したら、 sleep で指定した時間(今回は 20 秒)以内に、 python の process へ PyCharm を attach させます。
PyCharm には実行中の python process に attach する機能がありますが、 serverless
コマンドは node.js で実行されるため、デバッグ実行や attach ができません。
ただし、 node.js の内部では最終的に python を呼び出しているので、そのタイミングで sleep を入れてデバッグしちゃおう!という、せせこましい手段になります。
(参考)最終的に python を呼び出している箇所
以下が invoke local した後に実行されている処理ですが、 node.js の child_process を使って invoke.py
を呼び出しているのが分かるかと思います。
https://github.com/serverless/serverless/blob/master/lib/plugins/aws/invokeLocal/index.js#L602
const wrapperPath = await this.resolveRuntimeWrapperPath('invoke.py');
return new Promise((resolve, reject) => {
const python = spawn(
runtime.split('.')[0],
['-u', wrapperPath, handlerPath, handlerName],
{ env: process.env },
{ shell: true }
);
python.stdout.on('data', (buf) => {
legacy.consoleLog(buf.toString());
writeText(buf.toString());
});
python.stderr.on('data', (buf) => {
legacy.consoleLog(buf.toString());
writeText(buf.toString());
});
python.on('close', () => resolve());
let isRejected = false;
python.on('error', (error) => {
isRejected = true;
reject(error);
});
process.nextTick(() => {
if (isRejected) return; // Runtime not available
python.stdin.write(input);
python.stdin.end();
});
});
}
attach の方法
PyCharm のメニューバーから Run を選び
出てきたメニューの中から、 Attach to Process... を選びます
serverless の実行が python まで到達していないと、 attach 可能なプロセスがないと表示されます。
python が起動し、 sleep に入っていると以下のように attach 可能なプロセスが出てきます。
serverless invoke してから、実際に python が走り始めるまで数秒あるため、 sleep の前に、 "start" とかをログ出力しておくと、 attach を仕掛けるタイミングが分かりやすいです。
Let's Debug!
ここまで来たらあとは自由にデバッグできます。
step over するもよし、 step into するもよし、やりたい放題です。
終わりに
sleep を毎回仕込んだり、リリース時に消すのを忘れないように、など面倒な点もありますので、どうしてもステップ実行したい!という緊急用になるかなと思います。
知ってしまえば大したことのない方法ですが、今まで頑張って log を各行に仕込んで動作確認していたので、ほんの少し効率的に開発ができるようになるかもしれません。
ところで、PyCharm でのデバッグ方法を探していたところ、なにやら chalice という面白そうな framework を見つけました。
(なんとこちらは、デフォルトでデバッグできる!)
chalice
についても近々、ご紹介できたらなと思っています。