Article by: David Y.
Sentry は開発者がバグのあるコードを迅速かつ効果的に修正できるよう支援しています。この記事では、VSCode と Sentry Python SDK を活用し、Python コードをデバッグするための中級から上級レベルのテクニックを幅広く紹介します。
Python デバッグの例
デバッグを行うには、まずバグのあるコードが必要です。
そこで今回は短い Python スクリプトを用意しました。このスクリプトは、外部の JSON ファイルからユーザーデータを取得し、内部のデータ構造に格納するといったものです。
こうしたコードは、メールニュースレター管理ツールにユーザーアカウントをインポートする際にも利用されることがあります。
このコードは、小規模で完璧にフォーマットされた users.json を使用した単純なテストケースで正常に動作します。しかし、管理されたテスト環境を離れると例外が発生する可能性が出てきます。考えられる失敗パターンは以下のとおりです。
- users.json が存在しない
- users.json に無効な JSON が含まれている
- 一部のユーザーに必要なフィールドが欠落している
- 一部のフィールドのデータ方が想定と異なる
以下のセクションでは、さまざまなデバッグ環境を使用し、これらのケースを調査していきます。
実際に試しながら進める場合は、上記のコードをコピーしてシステム上の空のディレクトリに users.py というファイル名で保存してください(users.json は後ほど作成します)。
VSCode で Python をデバッグする方法
VSCode は、Python を含むさまざまなプログラミング言語で使用できるグラフィカルなデバッグインターフェースを提供しています。
まだ VSCode をインストールしていない場合は、こちらでインストールファイルと手順を確認できます。
以下では、VSCode のデバッグ機能をセットアップし、Python スクリプトをデバッグする方法 をご紹介します。
Pythonデバッグのための VSCode設定
VSCode でデバッグを行うために、以下の手順をお試しください。
1. Microsoft の Python 拡張機能と Python Debugger 拡張機能をインストールします。以下のショートカットでコマンドパレットを開くことができます。
-
- Mac:cmd+p
- Windows:ctrl+p
コマンドパレットを開いたら、以下2つのコマンドを実行します。
2. debugpy Python ライブラリをインストールします。VSCode のターミナルで、以下のコマンドを実行するだけでインストールが可能です。
3. Python プロジェクトを開いた状態で、サイドバーのRun and Debugタブに移動し、create a launch.json fileをクリックします。
4. 表示されたデバッガーオプションメニューで、Python Debuggerを選択してください。
5. デバッグ構成のメニューが表示されます。ここには様々な Python スクリプトやアプリケーションをデバッグするためのオプションが表示されます。今回は、最初のオプションPython Fileを選択してみます。
6. プロジェクトディレクトリ内に .vscode/launch.json ファイルが作成されました。このファイルを保存して閉じましょう。
これで、VSCode の Python スクリプトのデバッグ設定が完了しました。
VSCode で Python の FileNotFound 例外をデバッグする
Run and Debug サイドバーの上部にある緑色の矢印をクリックするか、キーボードの F5 を押下することで、スクリプトをデバッグモードで実行することができます。
users.py スクリプトは、エラーに遭遇するまで実行され続け、その時点で VSCode のウィンドウは次のような状態になります。
VSCode のデバッガーが FileNotFound 例外を表示し、エラーが発生した行をハイライトしています。
このコンテキストとエラーメッセージから、users.json が存在しないためにエラーが発生していることがわかります。
この問題を解決するために、次の 2 つの対応を行います。
- 例外処理を追加する
- 不足しているファイルを作成する
まず、load_users 関数にコードを追加します。ユーザーに対してわかりやすいエラーメッセージを表示し、正常に終了できるようにしましょう。関数は次のように変更してください。
デバッガーを再起動するには、画面上部の緑色の円形の矢印をクリックするか、以下のショートカットを実行してください。
・Mac:cmd+shift+F5
・Windows:ctrl+shift+F5
例外処理コードを追加したことで、スクリプトはターミナルにメッセージを表示し、正常に終了するようになりました。
VSCode で Python の JSONDecodeError 例外をデバッグする
それでは次に進みましょう。
プロジェクトディレクトリに users.json というファイルを作成し、デバッガーを起動します。すると次のようなエラーが表示されるはずです。
今回は、users.json ファイルの内容を解析しようとした際にエラーが発生しています。
おそらくファイルが空であることが原因です。
この問題を解決するためには、load_users 関数に except ブロックを追加し次のように変更します。
これでプログラムは正常に終了するようになりましたが、まだ解消していません。
users.json にデータを追加する必要があります。
VSCode でデバッグ中の変数を追跡する
ここまでVSCode を使って、比較的単純な例外をデバッグしてきました。
次は少し複雑なケースに進みます。
まず、users.json ファイルに以下のデータを追加してください。
コードをデバッグすると、次の KeyError 例外が発生するはずです。
このエラーは for ループの現在の反復処理で user_data に email キーが存在しないことを示しています。users.json には4件のデータしかないため、どのデータがエラーを引き起こしているのかすでにお気付きかもしれません。
しかし、もし users.json に数千件のデータが含まれていた場合はどうでしょう?手作業でエラー原因となったデータを探すのは大変です。
print() ステートメントを使って特定することもできますが、もっと効率的な方法があります。それがVariablesペインの活用です。
Run and Debugサイドバーの最上部にあるペインには、スクリプトの実行中に使用されているローカル変数とグローバル変数が一覧表示されます。
Locals 内の user_data 変数を展開すると、KeyError の原因となったデータが id = 2、name = ‘Bob Jones’ のデータであることがわかります。
実際に users.json を確認すると、このユーザーには email フィールドが存在していないようです。
この問題をどう解決するかは、アプリケーションの仕様や要件により異なりますが、次のような方法があります。
- users.json 全体を無効として処理し、プログラムを終了する
- フィールドが欠落しているユーザーをスキップする
- 欠落しているフィールドの値を None または適切なデフォルト値に設定する
多くは、2(スキップ)と3(デフォルト値を設定)の組み合わせがベストです。どのフィールドが欠落しているかによって最適解を選ぶと良いでしょう。
それでは、load_users() 関数を次のように書き換えてみます。
- id または name が欠落しているユーザーはスキップする
- email が欠落している場合は None を設定する
- preferences が欠落している場合は { “newsletter”: { “subscribed”: False }} を設定する
Variablesペインでは、すべてのグローバル変数とローカル変数を一覧表示できますが、大規模なプログラムでは毎回すべてを確認するのは非効率です。
そこで役立つのが Watchペイン です。特定の式を監視できるため、重要な変数の値を常にチェックしながらデバッグできます。
例えば、user_data をWatchペインに追加するには、右上の + ボタンをクリックし、user_data と入力します。これにより、スコープ内に user_data というローカル変数がある場合、その値が Watchペインに表示されるようになります。
VSCode でプログラムのフローをトレースする
次のエラーのデバッグに進む前に、デバッガを使ってプログラムの流れを追ってみましょう。これにはブレークポイントを使用します。
VSCode でブレークポイントを設定するには Python ファイルの左マージンにある、行番号の上にカーソルをホバーします。
すると、Click to add a breakpointというツールチップが表示され、赤い点が現れます。
その赤い点をクリックするとブレークポイントが設定され、行番号の横に赤い点が残ります。赤い点を再度クリックすると非表示になり、ブレークポイントが解除されたことを表します。
以下のようにブレークポイントを設定し、プログラムの実行をステップごとに確認できるようにしましょう。
- ブレークポイント 1(16 行目): __init__ 関数の冒頭で停止
- ブレークポイント 2(21 行目): load_users の with ステートメントで停止
- ブレークポイント 3(23 行目): load_users 内の for ループの各反復処理で停止
- ブレークポイント 4(35 行目): load_users 内のユーザー作成処理で停止
スクリプトをRun and Debugで実行すると、最初のブレークポイント(16 行目)で停止するはずです。
デバッグコントロールの上部にあるContinueボタンをクリックすると、次のブレークポイント(21 行目)まで進みます。
次に「Step Into」ボタンを押して with ブロックの内部へ。
すると with ブロック内の次の行(22 行目)に移動します。
注意:ContinueまたはStep Overを押すと、次のブレークポイント(23 行目)に直接進んでしまうため注意しましょう。これらの機能の簡単な概要はこちらの記事でご覧いただけます。
もう一度 Step Intoを押すと、次のブレークポイント(23 行目)に到達します。
ここから Step Into ボタンを使って 1行ずつ進めながら、サイドバーの VariablesペインとCall Stackペインをチェックしてみましょう。
実行の流れに応じて変数の値やコールスタックがどう変わるか観察してみてください。
最後のブレークポイント(35 行目)に到達したら、self.users にカーソルをホバーしてみましょう。
ポップアップが表示され、現在の self.users の値が確認できます。最初は空になっているはずです。
ここで Continueボタンをクリックして次のループへ進みましょう。
もう一度 self.users にカーソルをホバーしてみると、データが1つ追加されていることが確認できます。
デバッグ中は Watch ペインを活用することで、特定の変数の値をリアルタイムで追跡できます。必要な変数を追加しておけば『コードのどこでどう変化しているのか』がすぐに確認できるため便利です。
コードを進めながら、すべてのブレークポイントを通過し、次の例外が発生するまでデバッグを続けてみてください。
注意:すべての例外を解決したら、ブレークポイントを削除しておきましょう。各行の赤い点をクリックすることで解除できます。
VSCode で AttributeError 例外をデバッグする
load_users 関数のデバッグが終わり、ブレークポイントを削除したらプログラムを再実行してみましょう。他にエラーがないか確認します。すると、process_user 関数の58行目で AttributeError が発生するはずです。
このエラーは、NoneType の変数に対して split 関数を使用しようとしたことが原因で起きます。
user にカーソルをホバーするか、Variablesペインを確認すると、またしても id = 2 の “Bob Jones” に関するデータが原因となっていることがわかります。
これをより詳細に確認するために、Logpoint を活用する方法があります。
Logpoint とは、ブレークポイントの一種で、特定の行に到達したときにメッセージを出力するデバッグ用のツールのことです。
一時的な print() 文のようなものですが、コードを書き換えることなく設定することができます。設定の手順は以下のとおりです。
- 58 行目(email_domain: で始まる行)を右クリック
- メニューからAdd Logpoint…を選択
- 表示されたテキストボックスに、次のメッセージを入力
4. Enter を押下して確定する
デバッグを再起動すると、スクリプトは AttributeError に到達するまで実行されます。
Logpoint のメッセージは、VSCode の組み込みターミナルのタブの一つである Debug Console に出力されるため、エラーの原因となったデータをすぐに特定することができます。Debug Consoleの画面は次のようになります。
このエラーを解消するには user.email が “None” でないことを確認してから、ドメインを抽出する処理(split 関数)を実行するように修正しなければなりません。
以下のように process_user 関数を修正すれば、エラーを回避できます。
VSCode でコード実行をステップ実行しながら条件付きブレークポイントを使う
スクリプトを実行すると、58 行目(email_domain: で始まる行)で、別の例外が発生するはずです。今回は IndexError 例外が発生しています。split 関数で処理しようとしたメールアドレスに「@」が欠けていることで発生していると考えられます。
このバグをさらに調査するために、メールアドレスが処理される直前の 57 行目にブレークポイントを設定します。ただ、通常のブレークポイントではすべてのユーザーの処理で実行が停止してしまいます。
今回は id が “4” のユーザーの処理だけを確認したいので、条件付きブレークポイントを設定します。
条件付きブレークポイントは指定した式が true の場合にのみ実行を停止するブレークポイントです。(例: user.id == 4 の場合のみ実行を停止)
条件付きブレークポイントの設定方法は、以下のとおりです。
- 57 行目(last_name: で始まる行)の左マージンを右クリック
- メニューから「Add Conditional Breakpoint」を選択
- 表示されたテキストボックスに次の式を入力し Enter を押下
スクリプトを実行してデバッグを開始すると、57行目で一時停止します。ただしこれは 4番目のユーザーを処理しているときのみです。すでに分かっているように、次の 58行目では IndexError 例外が発生するはずです。
そしてその原因は このユーザーのメールアドレスに「@」が欠けているためだと考えられます。この仮説を素早く検証するために、コードを変更せずにテストを行いましょう。
VSCode の Debug Console を使えば、任意の Python コードを実行できるのでその場で値を変更して確認できます。この機能を使って不正なメールアドレスを修正してみましょう。
- VSCode の統合ターミナルの下部にあるDebug Consoleタブを開く
- Debug Consoleに以下のコードを入力し、Enter を押下
- user.email にカーソルをホバーし、変更が適用されたことを確認する
- 実行を続行する
するとスクリプトはエラーなく完了し、以下の出力がTerminalタブに表示されるはずです。
エラーの原因が確認できたので、users.jsonを編集し Daisy のメールアドレスを修正します。
またはコードを変更して、不正なメールアドレスを修正するか完全に除外する方法もあります。
Sentry を活用した Python のデバッグ
これまでにテストデータを使って発覚したバグを修正しながら、VSCode の強力なデバッグ機能について学んできました。しかしコードには、見つけられていないバグが潜んでいるかもしれません。
追加のテストを行えば多くのバグを見つけることができますが、実際のデータや本番環境でのユーザーの使用状況にさらされて、初めて明らかになるバグもあります。
これは本番環境にデプロイされた後に発生する可能性が高いものです。
そこで、Sentry を Python スクリプトに統合することで、ローカル開発環境の外でも継続的なデバッグが可能になります。
これを実演するため、Python の sentry_sdk モジュールをスクリプトに追加し、process_user 関数にブレッドクラム(Breadcrumbs)とエラーキャプチャ機能を組み込みます。
まず、Sentry Python SDK をインストールします。
Sentry にログイン(またはアカウントを作成)し、Python プロジェクトをセットアップします。
Python スクリプトを変更し、Sentry がエラーをキャプチャできるようにします。
- sentry_sdk をインポートし、sentry_sdk.init をスクリプトの先頭に追加
- 「YOUR-DSN-HERE」の部分を、自分の Sentry プロジェクトの DSN に置き換える
- process_user 関数の冒頭で、Sentry のブレッドクラム(Breadcrumb)を追加する
- これにより、エラー発生前のイベントの記録が可能になります。user_id などの情報を含めるようにします。
- process_user 関数内の重要な箇所で、追加のブレッドクラムを設定する
- 例えば『ユーザーが存在しない場合にブレッドクラムを追加する』などです。
- 例外が発生する前に、Sentry SDK でキャプチャさせる
Pythonのスクリプトは次のようになります。
これでスクリプトは Sentry にエラーを報告できるようになりました。
動作を確認するために、users.json にもう一名、不正なデータを持つユーザーを追加してみましょう。
このユーザーには名前(first name)のみがあり、コードが姓(last name)を抽出しようとする際にエラーが発生するはずです。
コードを実行し、例外が発生することを確認してください。
実行後、Sentry のダッシュボードに新しい Issue(問題)が表示されます。
このレポートには例外の詳細や、それに至るまでのブレッドクラム(Breadcrumbs)が含まれています。さらに、Sentry は user や user_id などの関連するローカル変数の値も記録しており、これを利用することで VSCode でのデバッグと同様に問題を特定し修正することができます。
Sentry が提供するその他のデバッグツール
Sentry はエラーや例外の監視だけでなく、Python 開発者にとって役立つ様々なデバッグツールを提供しています。
例えばこの記事でご紹介したケースよりも大規模なアプリケーションを開発している場合、ログの追加を検討したことがあるかもしれません。
Sentry はログモジュールと統合でき、ログをいちいち調査する手間を省きながらより充実したなデバッグ環境を提供します。
さらに、Sentry が活躍するのはエラーが起きた時だけではありません。
Python 向けパフォーマンス監視機能も充実しており、アプリケーション動作速度や最適化にも活用できます。ユーザー体験を向上させるためにも、パフォーマンスの監視は重要なポイントです。
Python アプリを迅速にデバッグし、確信を持ってデプロイする
今回、VSCode を使用した開発中のデバッグ方法と、Sentry を活用した本番環境でのデバッグ方法をご紹介してきました。
これらを組み合わせることで、問題の特定・理解・修正をスムーズに進めることができ、アプリケーションのダウンタイムを最小限に抑えながらデバッグスキルを高めることができます。
シンプルなエラー修正から、より複雑なバグ対応まで、Sentry と VSCode は高品質なコードを維持するための心強いツールです。
まだ Sentry を導入されていない方は、ぜひ無料で Sentry を導入し、Python アプリケーションの監視を始めてみましょう。
開発者同士の情報交換ができる Discord にもぜひご参加ください。
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談はこちらのフォームからお気軽にお問い合わせください。