Gemeinsames Debuggen von Python und C++ in Visual Studio

Die meisten regulären Python-Debugger unterstützen nur das Debuggen von Python-Code, es ist jedoch üblich, dass Entwickler Python mit C oder C++ verwenden. Einige Szenarien, die gemischten Code verwenden, sind Anwendungen, die eine hohe Leistung oder die Möglichkeit zum direkten Aufrufen von Plattform-APIs erfordern und häufig in Python und C oder C++ codiert sind.

Visual Studio bietet integriertes, gleichzeitiges Debuggen im gemischten Modus für Python und nativen C/C++-Code. Die Unterstützung ist verfügbar, wenn Sie die Option Native Python-Entwicklungstools für den Workload Python-Entwicklung im Visual Studio-Installationsprogramm auswählen:

Screenshot, der die Option Python Native Entwicklertools zeigt, die im Visual Studio Installer ausgewählt wurde.

In diesem Artikel erfahren Sie, wie Sie mit den folgenden Features für das Debuggen im gemischten Modus arbeiten:

  • Kombinierte Aufruflisten
  • Abwechselnde Einzelschrittausführung zwischen Python und nativem Code
  • Haltepunkte in beiden Codetypen
  • Anzeigen von Python-Darstellungen von Objekten in nativen Frames und umgekehrt
  • Das Debuggen im Kontext eines Python-Projekts oder eines C++-Projekts

Screenshot eines Beispiels für das Debuggen im gemischten Modus für Python- und C++-Code in Visual Studio.

Voraussetzungen

  • Visual Studio 2017 und höher Das Debuggen im gemischten Modus steht für Python-Tools für Visual Studio 1.x in Visual Studio 2015 und niedrigeren Versionen nicht zur Verfügung.

  • Visual Studio wurde mit Unterstützung für Python-Workloads installiert. Weitere Informationen finden Sie unter Installieren von Python-Unterstützung in Visual Studio.

Aktivieren des Debuggens im gemischten Modus in einem Python-Projekt

Die folgenden Schritte beschreiben, wie Sie das Debuggen im gemischten Modus in einem Python-Projekt aktivieren:

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Python-Projekt, und wählen Sie Eigenschaften aus.

  2. Wählen Sie im Bereich Eigenschaften die Registerkarte Debuggen und dann die Option Debuggen>Debuggen von nativem Code aktivieren aus:

    Screenshot, der zeigt, wie Sie die Eigenschaft Debugging von nativem Code aktivieren in Visual Studio einstellen.

    Diese Option aktiviert den gemischten Modus für alle Debugsitzungen.

    Tipp

    Wenn Sie das Debuggen von nativem Code aktivieren, wird das Python-Ausgabefenster möglicherweise sofort nach Abschluss des Programms geschlossen, ohne anzuhalten und die Eingabeaufforderung Drücken Sie eine beliebige Taste, um fortzufahren... anzuzeigen. Um das Anhalten und die Eingabeaufforderung zu erzwingen, nachdem Sie das Debuggen von systemeigenem Code aktiviert haben, fügen Sie das Argument -i dem Feld Ausführen>Interpreterargumente auf der Registerkarte Debuggen hinzu. Dieses Argument versetzt den Python-Interpreter nach der Codeausführung in den interaktiven Modus. Das Programm wartet darauf, dass Sie STRG+Z+Eingabetaste drücken, um das Fenster zu schließen.

  3. Wählen Sie Datei>Speichern (oder STRG+S) aus, um die Eigenschaftsänderungen zu speichern.

  4. Wenn Sie den Debugger im gemischten Modus an einen vorhandenen Prozess anfügen möchten, wählen Sie Debuggen>An den Prozess anhängen aus. Ein Dialogfeld wird geöffnet.

    1. Wählen Sie im Dialogfeld An den Prozess anhängen den entsprechenden Prozess aus der Liste aus.

    2. Verwenden Sie für das Feld Anhängen an die Option Auswählen, um das Dialogfeld Codetyp auswählen zu öffnen.

    3. Wählen Sie im Dialogfeld Codetyp auswählen die Option Diese Codetypen debuggen aus.

    4. Aktivieren Sie in der Liste das Kontrollkästchen Python (nativ) und dann OK:

      Screenshot, der zeigt, wie Sie den Python-Codetyp (nativer) Codetyp für das Debuggen in Visual Studio auswählen.

    5. Wählen Sie Anfügen aus, um den Debugger zu starten.

    Die Codetypeinstellungen sind dauerhaft. Wenn Sie das Debuggen im gemischten Modus deaktivieren und später an einen anderen Prozess anfügen möchten, deaktivieren Sie das Codetyp-Kontrollkästchen Python (nativ), und aktivieren Sie das Codetyp-Kontrollkästchen Nativ.

    Sie können zusätzlich zur Option Nativ oder anstelle dieser auch andere Codetypen auswählen. Wenn beispielsweise eine verwaltete Anwendung CPython hostet, das wiederum native Erweiterungsmodule verwendet, und Sie alle drei Codeprojekte debuggen möchten, aktivieren Sie die Kontrollkästchen Python, Nativ und Verwaltet. Dieser Ansatz bietet Ihnen ein einheitliches Debugging-Erlebnis, einschließlich kombinierter Aufruflisten und einen Wechsel zwischen allen drei Laufzeiten.

Arbeiten mit virtuellen Umgebungen

Wenn Sie diese Methode des Debuggens im gemischten Modus für virtuelle Umgebungen (venvs) verwenden, verwendet Python für Windows eine python.exe-Stubdatei für venvs, die Visual Studio findet und als Unterprozess lädt.

  • Für Python 3.8 und höher unterstützt der gemischte Modus das Debuggen mit mehreren Prozessen nicht. Wenn Sie die Debugsitzung starten, wird der Stub-Unterprozess debuggt und nicht die Anwendung. Beim Anfügen von Szenarien besteht die Problemumgehung darin, die richtige python.exe-Datei anzufügen. Wenn Sie die Anwendung mit Debugging starten (z. B. über die F5-Tastenkombination, können Sie venv mithilfe des Befehls C:\Python310-64\python.exe -m venv venv --symlinks erstellen. Geben Sie im Befehl Ihre bevorzugte Python-Version ein. Standardmäßig können unter Windows nur Administratoren Symlinks erstellen.

  • Für Python-Versionen vor 3.8 sollte das Debuggen im gemischten Modus mit venvs wie erwartet funktionieren.

Beim Ausführen in einer globalen Umgebung treten bei keiner Python-Version Probleme auf.

Installieren von Python-Symbolen

Wenn Sie das Debuggen im gemischten Modus zum ersten Mal starten, wird womöglich das Dialogfeld Python-Symbole erforderlich angezeigt. Sie müssen die Symbole für jede Python-Umgebung nur einmal installieren. Symbole sind automatisch enthalten, wenn Sie die Python-Unterstützung über den Visual Studio-Installer (Visual Studio 2017 und höher) installieren. Weitere Informationen finden Sie unter Installieren von Debuggingsymbolen für Python-Interpreter in Visual Studio.

Auf den Python-Quellcode zugreifen

Sie können den Quellcode für Standard-Python selbst beim Debuggen verfügbar machen.

  1. Wechseln Sie zu https://www.python.org/downloads/source/.

  2. Laden Sie das für Ihre Version geeignete Python-Quellcodearchiv herunter, und extrahieren Sie den Code in einen Ordner.

  3. Wenn Visual Studio nach dem Speicherort des Python-Quellcodes fragt, verweisen Sie auf die spezifischen Dateien im Extraktionsordner.

Aktivieren des Debuggens im gemischten Modus in einem C/C++-Projekt

Visual Studio 2017, Version 15.5 und höher, unterstützt das Debuggen im gemischten Modus aus einem C/C++-Projekt. Ein Beispiel für diese Verwendung ist, wenn Sie Python in eine andere Anwendung einbetten möchten, wie auf python.org beschrieben.

Die folgenden Schritte beschreiben, wie Sie das Debuggen im gemischten Modus für ein C/C++-Projekt aktivieren:

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das C/C++-Projekt, und wählen Sie Eigenschaften aus.

  2. Wählen Sie im Bereich Eigenschaftenseiten die Registerkarte Konfigurationseigenschaften>Debugging aus.

  3. Erweitern Sie das Dropdownmenü für die Option Zu startender Debugger, und wählen Sie Python-/Natives Debugging aus.

    Screenshot, der zeigt, wie Sie die Option Python Natives Debugging für ein C/C++-Projekt in Visual Studio auswählen.

    Hinweis

    Wenn die Option Python-/Natives Debuggen nicht angezeigt wird, müssen Sie zunächst die nativen Python-Entwicklungstools mit dem Visual Studio-Installer installieren. Die native Debugging-Option ist unter der Python-Entwicklungs-Workload verfügbar. Weitere Informationen finden Sie unter Installieren von Python-Unterstützung in Visual Studio.

  4. Klicken Sie zum Speichern der Änderungen auf OK.

Debuggen des Programmstartprogramms

Wenn Sie diese Methode verwenden, können Sie das py.exe-Programmstartprogramm nicht debuggen, da es einen untergeordneten python.exe-Unterprozess erzeugt. Der Debugger wird nicht an den Unterprozess angefügt. Für dieses Szenario besteht die Problemumgehung darin, das python.exe-Programm wie folgt direkt mit Argumenten zu starten:

  1. Gehen Sie im Bereich Eigenschaftenseiten für das C/C++-Projekt zur Registerkarte Konfigurationseigenschaften>Debugging.

  2. Geben Sie für die Befehlsoption den vollständigen Pfad zur python.exe-Programmdatei an.

  3. Geben Sie die gewünschten Argumente im Feld Befehlsargumente an.

Anfügen des Debuggers im gemischten Modus

Für Visual Studio 2017, Version 15.4 und früher, ist das direkte Debuggen im gemischten Modus nur beim Starten eines Python-Projekts in Visual Studio aktiviert. Die Unterstützung ist eingeschränkt, da C/C++-Projekte nur den nativen Debugger verwenden.

In diesem Szenario besteht die Problemumgehung darin, den Debugger separat anzufügen:

  1. Starten Sie das C++-Projekt ohne Debuggen, indem Sie Debuggen>Ohne Debuggen starten auswählen, oder die Tastenkombination STRG+F5 verwenden.

  2. Wenn Sie den Debugger im gemischten Modus an einen vorhandenen Prozess anfügen möchten, wählen Sie Debuggen>An den Prozess anhängen aus. Ein Dialogfeld wird geöffnet.

    1. Wählen Sie im Dialogfeld An den Prozess anhängen den entsprechenden Prozess aus der Liste aus.

    2. Verwenden Sie für das Feld Anhängen an die Option Auswählen, um das Dialogfeld Codetyp auswählen zu öffnen.

    3. Wählen Sie im Dialogfeld Codetyp auswählen die Option Diese Codetypen debuggen aus.

    4. Aktivieren Sie in der Liste das Kontrollkästchen Python, und wählen Sie dann OK aus.

    5. Wählen Sie Anfügen aus, um den Debugger zu starten.

Tipp

Sie können der C++-Anwendung eine Pause oder Verzögerung hinzufügen, um sicherzustellen, dass sie nicht den Python-Code aufruft, den Sie debuggen möchten, bevor Sie den Debugger anhängen.

Entdecken spezieller Funktionen für den gemischten Modus

Visual Studio bietet mehrere Debuggingfunktionen im gemischten Modus, um das Debuggen Ihrer Anwendung zu vereinfachen:

Verwenden einer kombinierten Aufrufliste

Das Aufruflistenfenster zeigt die Stapelrahmen für den nativen und den Python-Code überlappend und mit markierten Übergängen zwischen beiden Codetypen an:

Screenshot des kombinierten Aufruflistenfensters mit Debugging im gemischten Modus in Visual Studio.

  • Um Übergänge als [externer Code] anzuzeigen, ohne die Richtung des Übergangs anzugeben, legen Sie die Option Extras>Optionen>Debugging>Allgemein>Nur meinen Code aktivieren fest.

  • Um einen Aufruflistenrahmen zu aktivieren, doppelklicken Sie auf den Rahmen. Diese Aktion öffnet, sofern möglich, auch den entsprechenden Quellcode. Wenn kein Quellcode verfügbar ist, wird der Rahmen dennoch aktiv, und die lokalen Variablen können überprüft werden.

Abwechselnde Einzelschrittausführung zwischen Python und nativem Code

Visual Studio bietet die Befehle Einzelschritt (F11) oder Schritt bis zur nächsten Anweisung in der aufrufenden Funktion (Umschalt+F11), um dem Debugger im gemischten Modus zu ermöglichen, Änderungen zwischen Codetypen korrekt zu verarbeiten.

  • Wenn Python eine Methode eines in C implementierten Typs aufruft, wird die Einzelschrittausführung eines Aufrufs dieser Methode am Beginn der nativen Funktion angehalten, die die Methode implementiert.

  • Das gleiche Verhalten tritt auf, wenn nativer Code eine Python-API-Funktion aufruft, was dazu führt, dass Python-Code aufgerufen wird. Die Einzelschrittausführung von PyObject_CallObject in einem Funktionswert, der ursprünglich in Python definiert wurde, wird am Beginn der Python-Funktion angehalten.

  • Die Einzelschrittausführung von nativem Code aus Python heraus wird ebenfalls für native Funktionen unterstützt, die über ctypes von Python aufgerufen wurden.

Verwenden der PyObject-Werte im nativen Code

Wenn ein nativer Frame (C oder C++) aktiv ist, werden die zugehörigen lokalen Variablen im Lokalfenster des Debuggers angezeigt. In nativen Python-Erweiterungsmodulen weisen viele dieser Variablen den Typ PyObject auf (der eine typedef für _object ist), oder einige andere grundlegende Python-Typen. Beim Debuggen im gemischten Modus zeigen diese Werte einen weiteren untergeordneten Knoten mit der Bezeichnung [Python-Ansicht] an.

  • Um die Python-Darstellung der Variablen anzuzeigen, erweitern Sie den Knoten. Die Ansicht der Variablen ist identisch mit der Ansicht, die Sie sehen, wenn eine lokale Variable, die auf dasselbe Objekt verweist, in einem Python-Frame vorhanden ist. Die untergeordneten Elemente dieses Knotens können bearbeitet werden.

    Screenshot der Python-Ansicht im Fenster Lokal in Visual Studio.

  • Um diese Funktion zu deaktivieren, klicken Sie mit der rechten Maustaste auf eine beliebige Stelle im Lokalfenster, und deaktivieren Sie die Menüoption Python>Python-Ansichtsknoten anzeigen:

    Screenshot, der zeigt, wie Sie die Option Python-Ansichtsknoten anzeigen für das Fenster Lokal aktivieren.

C-Typen, die Python-Ansichtsknoten anzeigen

Die folgenden C-Typen zeigen [Python-Ansicht]-Knoten an, sofern diese aktiviert sind:

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

[Python-Ansicht] wird nicht automatisch für Typen angezeigt, die Sie selbst erstellen. Wenn Sie Erweiterungen für Python 3.x erstellen, ist diese fehlende Anzeige normalerweise kein Problem. Jedes Objekt hat letztendlich ein ob_base Feld mit einem der aufgelisteten C-Typen, was dazu führt, dass [Python-Ansicht] angezeigt wird.

Anzeigen der nativen Werte im Python-Code

Sie können eine [C++-Ansicht] für native Werte im Fenster Lokal aktivieren, wenn ein Python-Frame aktiv ist. Dieses Feature ist nicht standardmäßig aktiviert.

  • Um die Funktion zu aktivieren, klicken Sie mit der rechten Maustaste in das Fenster Lokal, und legen Sie die Menüoption Python>C++-Ansichtsknoten anzeigen fest.

    Screenshot, der zeigt, wie Sie die Python-Ansichtsknoten-Optionen anzeigen für das Fenster Lokal aktivieren.

  • Der Knoten [C++-Ansicht] bietet eine Darstellung der zugrunde liegenden C/C++-Struktur für einen Wert. Dies entspricht der Darstellung, die in einem nativen Rahmen angezeigt würde. Der Knoten zeigt eine Instanz von _longobject (wofür PyLongObject eine typedef ist) für einen langen ganzzahligen Python-Wert an und versucht, Typen für native Klassen abzuleiten, die Sie selbst erstellt haben. Die untergeordneten Elemente dieses Knotens können bearbeitet werden.

    Screenshot der C++-Ansicht im Fenster Lokal in Visual Studio.

Wenn ein untergeordnetes Feld eines Objekts vom Typ PyObject oder einem anderen unterstützten Typ ist, verfügt es über einen [Python-Ansicht]-Darstellungsknoten (wenn diese Darstellungen aktiviert sind). Dieses Verhalten ermöglicht das Navigieren in Objektdiagrammen, bei denen Links nicht direkt für Python verfügbar gemacht werden.

Anders als bei [Python-Ansicht] -Knoten, die Python-Objektmetadaten zum Ermitteln des Objekttyps verwenden, gibt es keinen ähnlich zuverlässigen Mechanismus für [C++ View] . Allgemein gesagt: Es ist nicht möglich, für einen bestimmten Python-Wert (also einen PyObject-Verweis) zuverlässig zu ermitteln, welche C/C++-Struktur diesem zugrunde liegt. Der Debugger im gemischten Modus versucht, den Typ zu „erraten“. Dazu durchsucht der Debugger verschiedene Felder des Objekttyps (z. B. PyTypeObject, auf das durch das Feld ob_type verwiesen wird), die über Funktionszeigertypen verfügen. Wenn einer dieser Funktionszeiger auf eine Funktion verweist, die aufgelöst werden kann, und wenn diese Funktion über einen self-Parameter verfügt, dessen Typ spezifischer ist als PyObject*, wird angenommen, dass es sich bei diesem um den zugrunde liegenden Typ handelt.

Betrachten Sie das folgende Beispiel, in dem der ob_type->tp_init-Wert eines bestimmten Objekts auf die folgende Funktion zeigt:

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

In diesem Fall kann der Debugger ordnungsgemäß darauf schließen, dass der C-Typ des Objekts FobObject lautet. Wenn der Debugger aus tp_init keinen genaueren Typ ermitteln kann, geht er zu anderen Feldern über. Wenn aus keinem der Felder ein Typ hergeleitet werden kann, zeigt der [C++ View] -Knoten das Objekt als PyObject-Instanz an.

Um jederzeit eine sinnvolle Darstellung für benutzerdefiniert erstellte Typen zu erzielen, empfiehlt es sich, beim Registrieren des Typs mindestens eine Sonderfunktion zu registrieren und einen stark typisierten self-Parameter zu verwenden. Die meisten Typen erfüllen diese Anforderung auf natürliche Weise. Für andere Typen ist die tp_init-Inspektion normalerweise der bequemste Eintrag für diesen Zweck. Eine Dummyimplementierung von tp_init für einen Typ, der nur dafür da ist, den Typrückschluss für den Debugger zu ermöglichen, kann sofort Null zurückgeben, wie im vorherigen Beispiel veranschaulicht.

Ansehen der Unterschiede zum Python-Standarddebugging

Der Debugger mit gemischten Modus unterscheidet sich vom standardmäßigen Python-Debugger. Er führt einige zusätzliche Funktionen ein, es fehlen jedoch einige Python-bezogene Funktionen, wie folgende:

  • Zu den nicht unterstützten Funktionen gehören bedingte Haltepunkte, das Fenster zum interaktiven Debuggen und plattformübergreifendes Remotedebuggen.
  • Das Direktfenster ist verfügbar, allerdings mit eingeschränkter Funktionalität – einschließlich aller in diesem Abschnitt aufgeführten Einschränkungen.
  • Zu den unterstützten Python-Versionen gehören nur CPython 2.7 und 3.3+.
  • Um Python mit Visual Studio Shell zu verwenden (z. B. wenn Sie es mit dem integrierten Installationsprogramm installieren), kann Visual Studio keine C++-Projekte öffnen. Daher entspricht die Bearbeitungserfahrung für C++-Dateien nur der eines einfachen Texteditors. Das C/C++-Debuggen und das Debuggen im gemischten Modus werden in Shell jedoch vollständig unterstützt, einschließlich Quellcode, Einzelschrittausführung im nativen Code und C++-Ausdrucksauswertung in Debuggerfenstern.
  • Beim Anzeigen von Python-Objekten in den Toolfenstern Lokal und Überwachung des Debuggers zeigt der Debugger im gemischten Modus nur die Struktur der Objekte an. Es werden weder automatisch Eigenschaften ausgewertet noch berechnete Attribute angezeigt. Bei Auflistungen werden nur Elemente für integrierte Auflistungstypen angezeigt (tuple, list, dict, set). Benutzerdefinierte Auflistungstypen werden nur dann als Auflistungen visualisiert, wenn sie von einem integrierten Auflistungstyp vererbt werden.
  • Die Ausdrucksauswertung erfolgt wie im folgenden Abschnitt beschrieben.

Verwenden der Ausdrucksauswertung

Der Python-Standarddebugger ermöglicht die Auswertung beliebiger Python-Ausdrücke in Überwachungs- und Direktfenstern, wenn der Debuggingprozess an einem beliebigen Punkt im Code angehalten wird, sofern der Prozess nicht durch einen E/A-Vorgang oder einen anderen ähnlichen Systemaufruf blockiert wird. Beim Debuggen im gemischten Modus können beliebige Ausdrücke nur ausgewertet werden, wenn sie im Python-Code nach einem Haltepunkt beendet wurden oder Sie den Code schrittweise ausführen. Ausdrücke können nur in dem Thread ausgewertet werden, in dem der Haltepunkt oder die Ausführung in Einzelschritten aufgetreten ist.

Wenn der Debugger im nativen Code oder im Python-Code beendet wird, bei dem die beschriebenen Bedingungen nicht gelten, z. B. nach einem „Ausführen bis Rücksprung“-Vorgang oder in einem anderen Thread). Die Ausdrucksauswertung beschränkt sich auf den Zugriff auf lokale und globale Variablen im Bereich des aktuell ausgewählten Frames, den Zugriff auf deren Felder und die Indizierung integrierter Sammlungstypen mit Literalen. Der folgende Ausdruck kann z. B. in jedem Kontext ausgewertet werden (vorausgesetzt, dass alle Bezeichner auf vorhandene Variablen und Felder mit geeigneten Typen verweisen):

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

Der Debugger für den gemischten Modus löst solche Ausdrücke ebenfalls anders auf. Alle Vorgänge mit Memberzugriff schlagen nur Felder nach, die ein direkter Teil des Objekts sind (z. B. ein Eintrag in __dict__- oder __slots__-Elementen oder einem Feld der nativen Struktur, die über tp_members für Python verfügbar gemacht wurde), und ignorieren alle __getattr__- und __getattribute__-Elemente sowie jegliche Deskriptorlogik. Alle Indizierungsvorgänge ignorieren __getitem__ und greifen direkt auf die inneren Datenstrukturen von Auflistungen zu.

Aus Gründen der Konsistenz wird dieses Namensauflösungsschema für alle Ausdrücke verwendet, die den Einschränkungen für die eingeschränkte Ausdrucksauswertung entsprechen. Dieses Schema wird unabhängig davon angewendet, ob beliebige Ausdrücke am aktuellen Stopppunkt zulässig sind. Um eine ordnungsgemäße Python-Semantik zu erzwingen, wenn ein Auswerter mit vollständigem Funktionsumfang verfügbar ist, schließen Sie den Ausdruck in Klammern ein:

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