次の方法で共有


Visual Studio で Python と C++ を一緒にデバッグする

ほとんどの通常の Python デバッガーでは Python コードのデバッグのみがサポートされていますが、開発者は C または C++ で Python を使用するのが一般的です。 混合コードを使用するシナリオには、高パフォーマンスを必要とするアプリケーションや、プラットフォーム API を直接呼び出す機能が Python と C または C++ でコーディングされることが多いシナリオがあります。

Visual Studio では、Python とネイティブ C/C++ コード用の統合された同時混合モード デバッグが提供されます。 サポートは、Visual Studio インストーラーで Python 開発ワークロードの Python ネイティブ開発ツール オプションを選択した場合に利用できます。

Visual Studio インストーラーで選択されている Python ネイティブ開発ツール オプションを示すスクリーンショット。

この記事では、次の混合モード デバッグ機能を使用する方法について説明します。

  • 結合されたコールスタック
  • Python とネイティブ コードの間のステップ
  • 両方の種類のコードのブレークポイント
  • ネイティブ フレームでオブジェクトの Python 表現を表示し、その逆も表示する
  • Python プロジェクトまたは C++ プロジェクトのコンテキスト内でのデバッグ

Visual Studio での Python と C++ コードの混合モード デバッグの例を示すスクリーンショット。

[前提条件]

  • Visual Studio 2017 以降。 混合モードデバッグは、Visual Studio 2015 以前の Python Tools for Visual Studio 1.x では使用できません。

  • Python ワークロードをサポートしてインストールされた Visual Studio。 詳細については、「 Visual Studio での Python サポートのインストール」を参照してください。

Python プロジェクトで混合モード デバッグを有効にする

次の手順では、Python プロジェクトで混合モード デバッグを有効にする方法について説明します。

  1. ソリューション エクスプローラーで、Python プロジェクトを右クリックし、[プロパティ] を選択します

  2. [プロパティ] ウィンドウで、[デバッグ] タブを選択し、[デバッグ>ネイティブ コードデバッグを有効にする] オプションを選択します。

    Visual Studio でネイティブ コード デバッグを有効にするプロパティを設定する方法を示すスクリーンショット。

    このオプションでは、すべてのデバッグ セッションで混合モードが有効になります。

    ヒント

    ネイティブ コードのデバッグを有効にすると、プログラムが終了した直後に Python 出力ウィンドウが閉じられ、一時停止せずに [キーを押して続行する ]プロンプトが表示されることがあります。 ネイティブ コードのデバッグを有効にした後に一時停止とプロンプトを強制するには、[-iの [実行>Interpreter 引数] フィールドに引数を追加します。この引数は、コードの実行後に Python インタープリターを対話型モードにします。 プログラムは、Ctrl +Z+Enter を押してウィンドウを閉じるのを待っています。

  3. [ファイル>保存 (または Ctrl+) 選択して、プロパティの変更を保存します。

  4. 混合モード デバッガーを既存のプロセスにアタッチするには、 デバッグ>プロセスへのアタッチを選択します。 ダイアログが開きます。

    1. [ プロセスにアタッチ ] ダイアログで、一覧から適切なプロセスを選択します。

    2. [ アタッチする ] フィールドで、[ 選択 ] オプションを使用して [ コードの種類の選択] ダイアログを開きます。

    3. [ コードの種類の選択 ] ダイアログで、[ これらのコードの種類をデバッグ する] オプションを選択します。

    4. 一覧で Python (ネイティブ) チェック ボックスをオンにし、[ OK] を選択します

      Visual Studio でデバッグ用の Python (ネイティブ) コードの種類を選択する方法を示すスクリーンショット。

    5. [ アタッチ] を選択してデバッガーを起動します。

    コードの種類の設定は永続的です。 混合モードのデバッグを無効にして、後で別のプロセスにアタッチする場合は、[ Python (ネイティブ) コードの種類] チェック ボックスをオフにして、[ ネイティブ コードの種類] チェック ボックスをオンにします。

    ネイティブ オプションに加えて、または代わりに他のコードの種類を選択できます。 たとえば、マネージド アプリケーションが CPython をホストし、ネイティブ拡張モジュールを使用し、3 つのコード プロジェクトすべてをデバッグする場合は、 Pythonネイティブおよびマネージド のチェック ボックスをオンにします。 この方法では、結合された呼び出し履歴や 3 つのランタイム間のステップ実行など、統合されたデバッグ エクスペリエンスが提供されます。

仮想環境を操作する

仮想環境 (venv) に対してこの混合モード デバッグの方法を使用する場合、Python for Windows では、Visual Studio がサブプロセスとして検索して読み込む venv 用の python.exe スタブ ファイルが使用されます。

  • Python 3.8 以降では、混合モードではマルチプロセス デバッグはサポートされていません。 デバッグ セッションを開始すると、スタブ サブプロセスはアプリケーションではなくデバッグされます。 アタッチシナリオの場合、回避策は正しい python.exe ファイルにアタッチすることです。 デバッグ ( F5 キーボード ショートカットなど) を使用してアプリケーションを起動する場合は、コマンド C:\Python310-64\python.exe -m venv venv --symlinksを使用して venv を作成できます。 このコマンドで、任意のバージョンの Python を挿入します。 既定では、管理者のみが Windows でシンボリック リンクを作成できます。

  • 3.8 より前のバージョンの Python の場合、混合モードのデバッグは venv で想定どおりに動作する必要があります。

グローバル環境で実行しても、Python のどのバージョンでもこれらの問題は発生しません。

Python シンボルをインストールする

混合モードで初めてデバッグを開始すると、 Python シンボルが必要 なダイアログが表示されることがあります。 特定の Python 環境に対してシンボルを 1 回だけインストールする必要があります。 Visual Studio インストーラー (Visual Studio 2017 以降) を使用して Python サポートをインストールすると、シンボルが自動的に含まれます。 詳細については、「 Visual Studio での Python インタープリターのデバッグ シンボルのインストール」を参照してください。

Python ソース コードにアクセスする

デバッグ時に標準 Python 自体のソース コードを使用できるようにすることができます。

  1. https://www.python.org/downloads/source/ にアクセスします。

  2. バージョンに適した Python ソース コード アーカイブをダウンロードし、フォルダーにコードを抽出します。

  3. Visual Studio で Python ソース コードの場所の入力を求められたら、抽出フォルダー内の特定のファイルをポイントします。

C/C++ プロジェクトで混合モード デバッグを有効にする

Visual Studio 2017 バージョン 15.5 以降では、C/C++ プロジェクトからの混合モード デバッグがサポートされています。 この使用方法の例は、 python.org で説明されているように、Python を別のアプリケーションに埋め込む場合です

次の手順では、C/C++ プロジェクトの混合モード デバッグを有効にする方法について説明します。

  1. ソリューション エクスプローラーで、C/C++ プロジェクトを右クリックし、[プロパティ] を選択します。

  2. [プロパティ ページ] ウィンドウで、[構成プロパティ] タブ>[依存関係] タブを選択します。

  3. [ 起動するデバッガー ] オプションのドロップダウン メニューを展開し、[ Python/ネイティブ デバッグ] を選択します。

    Visual Studio で C/C++ プロジェクトの Python ネイティブ デバッグ オプションを選択する方法を示すスクリーンショット。

    Python/Native Debugging オプションが表示されない場合は、まず Visual Studio インストーラーを使用して Python ネイティブ開発ツールをインストールする必要があります。 ネイティブ デバッグ オプションは、Python 開発ワークロードで使用できます。 詳細については、「 Visual Studio での Python サポートのインストール」を参照してください。

  4. [ OK] を 選択して変更を保存します。

プログラム起動ツールをデバッグする

このメソッドを使用すると、子py.exeサブプロセスが生成されるため、python.exe プログラム起動ツールをデバッグできません。 デバッガーはサブプロセスにアタッチされません。 このシナリオでは、回避策として、次のように引数を指定して python.exe プログラムを直接起動します。

  1. C/C++ プロジェクトのプロパティ ページウィンドウで、構成プロパティからデバッグタブへ移動します。

  2. コマンド オプションには、python.exe プログラム ファイルへの完全パスを指定します。

  3. [Command Arguments]\(コマンド引数\) フィールドに目的 の引数を 指定します。

混合モード デバッガーをアタッチする

Visual Studio 2017 バージョン 15.4 以前の場合、直接混合モード デバッグは、Visual Studio で Python プロジェクトを起動する場合にのみ有効になります。 C/C++ プロジェクトではネイティブ デバッガーのみを使用するため、サポートは制限されています。

このシナリオの回避策は、デバッガーを個別にアタッチすることです。

  1. デバッグなしで C++ プロジェクトを開始するには、[デバッグ>デバッグなしで開始] を選択するか、キーボード ショートカット Ctrl+F5 を使用します。

  2. 混合モード デバッガーを既存のプロセスにアタッチするには、 デバッグ>プロセスへのアタッチを選択します。 ダイアログが開きます。

    1. [ プロセスにアタッチ ] ダイアログで、一覧から適切なプロセスを選択します。

    2. [ アタッチする ] フィールドで、[ 選択 ] オプションを使用して [ コードの種類の選択] ダイアログを開きます。

    3. [ コードの種類の選択 ] ダイアログで、[ これらのコードの種類をデバッグ する] オプションを選択します。

    4. 一覧で [ Python ] チェック ボックスをオンにし、[ OK] を選択します

    5. [ アタッチ] を選択してデバッガーを起動します。

ヒント

C++ アプリケーションで一時停止または遅延を追加して、デバッガーをアタッチする前にデバッグする Python コードが呼び出されないようにすることができます。

混合モード固有の機能を調べる

Visual Studio には、アプリケーションのデバッグを容易にするために、いくつかの混合モード デバッグ機能が用意されています。

結合された呼び出し履歴を使用する

[呼び出し履歴] ウィンドウには、ネイティブと Python の両方のスタック フレームが混在し、2つの間の遷移が示されています。

Visual Studio での混合モード デバッグを使用した結合呼び出し履歴ウィンドウのスクリーンショット。

  • 切り替えの方向を指定せずに画面切り替えを [外部コード] として表示するには、[ ツール>オプション ] ウィンドウを使用します。 [>Debugging>General] セクションを展開し、[マイ コードのみを有効にする] チェック ボックスをオンにします。
  • 画面切り替えの方向を指定せずに画面切り替えを [外部コード] として表示するには、[ ツール>オプション ] ダイアログを使用します。 [ デバッグ>General ] セクションを展開し、[ マイ コードのみを有効にする ] チェック ボックスをオンにして、[ OK] を選択します
  • 呼び出しフレームをアクティブにするには、フレームをダブルクリックします。 このアクションでは、可能であれば、対応するソース コードも開きます。 ソース コードを使用できない場合でも、フレームはアクティブになり、ローカル変数を検査できます。

Python とネイティブ コードの間のステップ

Visual Studio では、ステップ イン (F11) または ステップ アウト (Shift+F11) コマンドを使用して、混合モード デバッガーでコードの種類間の変更を正しく処理できるようにします。

  • Python が C で実装されている型のメソッドを呼び出すと、そのメソッドの呼び出しをステップインすると、そのメソッドを実装するネイティブ関数の先頭で停止します。

  • この同じ動作は、ネイティブ コードが Python API 関数を呼び出し、その結果 Python コードが呼び出される場合に発生します。 Python で最初に定義された関数値に対する PyObject_CallObject の呼び出しへのステップインは、Python 関数の先頭で停止します。

  • Python からネイティブへのステップインは、 ctypes を介して Python から呼び出されるネイティブ関数でもサポートされています。

ネイティブ コードで PyObject 値ビューを使用する

ネイティブ (C または C++) フレームがアクティブな場合、そのローカル変数はデバッガーの [ローカル] ウィンドウに表示されます。 ネイティブ Python 拡張モジュールでは、これらの変数の多くは PyObject 型 ( _objectの typedef) またはその他のいくつかの 基本的な Python 型です。 混合モードデバッグでは、これらの値は [Python ビュー] というラベルの付いた別の子ノードを示します。

  • 変数の Python 表現を表示するには、ノードを展開します。 変数のビューは、同じオブジェクトを参照するローカル変数が Python フレームに存在する場合と同じです。 このノードの子は編集可能です。

    Visual Studio の [ローカル] ウィンドウの Python ビューを示すスクリーンショット。

  • この機能を無効にするには、[ ローカル] ウィンドウ内の任意の場所を右クリックし、 Python>[Python ビュー ノードの表示 ] メニュー オプションを切り替えます。

    [ローカル] ウィンドウの [Python ビュー ノードの表示] オプションを有効にする方法を示すスクリーンショット。

Python ビュー ノードを表示する C 型

次の C 型は 、[Python ビュー] ノードを表示します (有効な場合)。

  • PyObject
  • PyVarObject
  • PyTypeObject
  • PyByteArrayObject
  • PyBytesObject
  • PyTupleObject
  • PyListObject
  • PyDictObject
  • PySetObject
  • PyIntObject
  • PyLongObject
  • PyFloatObject
  • PyStringObject
  • PyUnicodeObject

[Python ビュー] は、自分で作成した型に対して自動的に表示されません。 Python 3.x 用の拡張機能を作成する場合、通常、この不足は問題になりません。 どのオブジェクトにも、最終的にリストされている C 型の 1 つの ob_base フィールドがあり、[ Python ビュー] が 表示されます。

Python コードでネイティブ値を表示する

Python フレームがアクティブな場合、[ローカル] ウィンドウでネイティブ値の [C++ ビュー] を有効にすることができます。 この機能は、既定で有効になっています。

  • この機能を有効にするには、[ ローカル] ウィンドウで右クリックし、[ Python>Show C++ View Nodes]\(C++ ビュー ノードの表示 \) メニュー オプションを設定します。

    [ローカル] ウィンドウの [C++ ビュー ノードの表示] オプションを有効にする方法を示すスクリーンショット。

  • [C++ ビュー] ノードは、ネイティブ フレームに表示されるのと同じ値の基になる C/C++ 構造体の表現を提供します。 Python の長整数の _longobject ( PyLongObject は typedef) のインスタンスを示し、自分で作成したネイティブ クラスの型を推論しようとします。 このノードの子は編集可能です。

    Visual Studio の [ローカル] ウィンドウの C++ ビューを示すスクリーンショット。

オブジェクトの子フィールドが PyObject型またはサポートされている別の型の場合は、[ Python ビュー] リプレゼンテーション ノード (これらの表現が有効な場合) があります。 この動作により、リンクが Python に直接公開されていないオブジェクト グラフ内を移動できます。

Python オブジェクト メタデータを使用してオブジェクトの種類を決定する [Python ビュー] ノードとは異なり、[ C++ ビュー] には同様に信頼性の高いメカニズムはありません。 一般に、Python 値 (つまり、 PyObject 参照) では、どの C/C++ 構造体がバックアップされているかを確実に判断することはできません。 混合モード デバッガーは、関数ポインター型を持つオブジェクトの型のさまざまなフィールド (PyTypeObject フィールドによって参照されるob_typeなど) を調べることで、型の推測を試みます。 これらの関数ポインターの 1 つが解決できる関数を参照し、その関数にselfよりも具体的な型を持つPyObject* パラメーターがある場合、その型はバッキング型と見なされます。

次の例では、特定のオブジェクトの ob_type->tp_init 値が次の関数を指しています。

static int FobObject_init(FobObject* self, PyObject* args, PyObject* kwds) {
    return 0;
}

この場合、デバッガーは、オブジェクトの C 型が FobObjectされていることを正しく推測できます。 デバッガーが tp_initからより正確な型を判断できない場合は、他のフィールドに移動します。 これらのフィールドから型を推測できない場合、[ C++ ビュー] ノードはオブジェクトを PyObject インスタンスとして表示します。

カスタム作成型の便利な表現を常に取得するには、型を登録するときに少なくとも 1 つの特別な関数を登録し、厳密に型指定された self パラメーターを使用することをお勧めします。 ほとんどの型は、その要件を自然に満たします。 他のタイプの場合、通常、 tp_init 検査は、この目的に使用する最も便利なエントリです。 デバッガーの型推論を有効にするためにのみ存在する型の tp_init のダミー実装では、前の例のように、すぐに 0 を返すことができます。

標準の Python デバッグとの違いを確認する

混合モード デバッガーは、 標準の Python デバッガーとは異なります。 いくつかの追加機能が導入されていますが、次のような Python 関連の機能が不足しています。

  • サポートされていない機能には、条件付きブレークポイント、 デバッグ対話型 ウィンドウ、クロスプラットフォーム リモート デバッグが含まれます。
  • イミディエイト ウィンドウは使用できますが、このセクションに記載されているすべての制限事項を含め、機能のサブセットは限られています。
  • サポートされている Python バージョンには、CPython 2.7 および 3.3 以降のみが含まれます。
  • Visual Studio Shell で Python を使用するには (統合インストーラーでインストールする場合など)、Visual Studio で C++ プロジェクトを開くことができません。 その結果、C++ ファイルの編集エクスペリエンスは、基本的なテキスト エディターのみです。 ただし、C/C++ デバッグと混合モード デバッグは、ソース コード、ネイティブ コードへのステップイン、デバッガー ウィンドウでの C++ 式の評価を使用して、シェルで完全にサポートされています。
  • [ローカル] および [ウォッチ] デバッガー ツール ウィンドウで Python オブジェクトを表示すると、混合モード デバッガーにはオブジェクトの構造のみが表示されます。 プロパティを自動的に評価したり、計算された属性を表示したりすることはありません。 コレクションの場合、組み込みのコレクション型 (tuplelistdictset) の要素のみが表示されます。 カスタム コレクション型は、組み込みのコレクション型から継承されない限り、コレクションとして視覚化されません。
  • 式の評価は、以下のセクションで説明される通りに処理されます。

式の評価を使用する

標準の Python デバッガーでは、I/O 操作やその他の同様のシステム呼び出しでブロックされない限り、デバッグされたプロセスがコード内の任意の時点で一時停止されたときに、 ウォッチ ウィンドウと イミディエイト ウィンドウで任意の Python 式を評価できます。 混合モードデバッグでは、Python コードで停止した場合、ブレークポイントの後、またはコードにステップインした場合にのみ、任意の式を評価できます。 式は、ブレークポイントまたはステップ実行操作が発生したスレッドでのみ評価できます。

デバッガーがネイティブ コードで停止したとき、または説明されている条件が適用されない Python コード (ステップアウト操作後、別のスレッド上など) で停止する場合)。 式の評価は、現在選択中のフレームのスコープ内にあるローカル変数とグローバル変数へのアクセス、これらのフィールドへのアクセス、ならびにリテラルを使った組み込みコレクション型のインデックス付けに限定されます。 たとえば、次の式は任意のコンテキストで評価できます (すべての識別子が適切な型の既存の変数とフィールドを参照している場合)。

foo.bar[0].baz['key']

混合モード デバッガーでは、このような式も異なる方法で解決されます。 すべてのメンバー アクセス操作は、オブジェクトの直接の一部であるフィールド ( __dict__ または __slots__のエントリ、または tp_members を介して Python に公開されるネイティブ構造体のフィールドなど) のみを検索し、 __getattr____getattribute__、または記述子ロジックを無視します。 同様に、すべてのインデックス作成操作は __getitem__を無視し、コレクションの内部データ構造に直接アクセスします。

一貫性を保つため、この名前解決スキームは、制限付き式の評価の制約に一致するすべての式に使用されます。 このスキームは、現在のストップ ポイントで任意の式が許可されるかどうかに関係なく適用されます。 フル機能のエバリュエーターが使用可能な場合に適切な Python セマンティクスを強制するには、式をかっこで囲みます。

(foo.bar[0].baz['key'])