Debugowanie języka Python i języka C++ razem w programie Visual Studio
Większość zwykłych debugerów języka Python obsługuje tylko debugowanie kodu w języku Python, ale często zdarza się, aby deweloperzy używali języka Python z językiem C lub C++. Niektóre scenariusze korzystające z kodu mieszanego to aplikacje wymagające wysokiej wydajności lub możliwość bezpośredniego wywoływania interfejsów API platformy są często kodowane w językach Python i C lub C++.
Program Visual Studio zapewnia zintegrowane, jednoczesne debugowanie w trybie mieszanym dla języka Python i natywnego kodu C/C++. Obsługa jest dostępna po wybraniu opcji natywnych narzędzi programistycznych języka Python dla obciążenia Programowanie w języku Python w instalatorze programu Visual Studio:
W tym artykule przedstawiono sposób pracy z następującymi funkcjami debugowania w trybie mieszanym:
- Połączone stosy wywołań
- Krok między językiem Python i kodem natywnym
- Punkty przerwania w obu typach kodu
- Wyświetlanie reprezentacji obiektów w języku Python w ramkach natywnych i na odwrót
- Debugowanie w kontekście projektu w języku Python lub w projekcie C++
Wymagania wstępne
Program Visual Studio 2017 lub nowszy. Debugowanie w trybie mieszanym nie jest dostępne w narzędziach Python Tools for Visual Studio 1.x w programie Visual Studio 2015 i starszych wersjach.
Program Visual Studio zainstalowany z obsługą obciążeń języka Python. Aby uzyskać więcej informacji, zobacz Instalowanie obsługi języka Python w programie Visual Studio.
Włączanie debugowania w trybie mieszanym w projekcie języka Python
W poniższych krokach opisano sposób włączania debugowania w trybie mieszanym w projekcie języka Python:
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt języka Python i wybierz polecenie Właściwości.
W okienku Właściwości wybierz kartę Debugowanie, a następnie wybierz opcję Debuguj> Włącz debugowanie kodu natywnego:
Ta opcja umożliwia tryb mieszany dla wszystkich sesji debugowania.
Napiwek
Po włączeniu debugowania kodu natywnego okno danych wyjściowych języka Python może zostać zamknięte natychmiast po zakończeniu działania programu bez wstrzymywania i wyświetlania klawisza Naciśnij dowolny klawisz, aby kontynuować monit. Aby wymusić wstrzymanie i monit po włączeniu debugowania kodu natywnego, dodaj
-i
argument do pola Uruchom>argumenty interpretera na karcie Debugowanie . Ten argument umieszcza interpreter języka Python w tryb interaktywny po uruchomieniu kodu. Program czeka na wybranie klawiszy Ctrl+Z+Enter, aby zamknąć okno.Wybierz pozycję Zapisz plik>(lub Ctrl+S), aby zapisać zmiany właściwości.
Aby dołączyć debuger trybu mieszanego do istniejącego procesu, wybierz pozycję Dołącz debugowanie>do procesu. Zostanie otwarte okno dialogowe.
W oknie dialogowym Dołączanie do procesu wybierz odpowiedni proces z listy.
W polu Dołącz do użyj opcji Wybierz, aby otworzyć okno dialogowe Wybieranie typu kodu.
W oknie dialogowym Wybieranie typu kodu wybierz opcję Debuguj te typy kodu.
Na liście zaznacz pole wyboru Python (natywne) i wybierz przycisk OK:
Wybierz pozycję Dołącz , aby uruchomić debuger.
Ustawienia typu kodu są trwałe. Jeśli chcesz wyłączyć debugowanie w trybie mieszanym i dołączyć go do innego procesu później, wyczyść pole wyboru Typ kodu języka Python (natywny) i zaznacz pole wyboru Typ kodu natywnego .
Możesz wybrać inne typy kodu oprócz lub zamiast opcji Natywna. Jeśli na przykład aplikacja zarządzana hostuje środowisko CPython, które z kolei korzysta z modułów rozszerzeń natywnych i chcesz debugować wszystkie trzy projekty kodu, zaznacz pola wyboru Python, Native i Managed . Takie podejście zapewnia ujednolicone środowisko debugowania, w tym połączone stosy wywołań i przechodzenie między wszystkimi trzema środowiskami uruchomieniowymi.
Praca ze środowiskami wirtualnymi
Jeśli używasz tej metody debugowania w trybie mieszanym dla środowisk wirtualnych (venvs), język Python dla systemu Windows używa python.exe
pliku wycinków dla programów venvs, które program Visual Studio znajduje i ładuje jako podproces.
W przypadku języka Python w wersji 3.8 lub nowszej tryb mieszany nie obsługuje debugowania wieloprocesowego. Po uruchomieniu sesji debugowania podprocesy wycinków są debugowane zamiast aplikacji. W przypadku scenariuszy dołączania obejście polega na dołączeniu do poprawnego
python.exe
pliku. Po uruchomieniu aplikacji z debugowaniem (na przykład za pomocą skrótu klawiaturowego F5 ) można utworzyć venv za pomocą poleceniaC:\Python310-64\python.exe -m venv venv --symlinks
. W poleceniu wstaw preferowaną wersję języka Python. Domyślnie tylko administratorzy mogą tworzyć symlinki w systemie Windows.W przypadku wersji języka Python starszych niż 3.8 debugowanie w trybie mieszanym powinno działać zgodnie z oczekiwaniami w przypadku venvs.
Uruchamianie w środowisku globalnym nie powoduje tych problemów dla żadnej wersji języka Python.
Instalowanie symboli języka Python
Po pierwszym uruchomieniu debugowania w trybie mieszanym może zostać wyświetlone okno dialogowe Wymagane symbole języka Python. Należy zainstalować symbole tylko raz dla dowolnego środowiska języka Python. Symbole są automatycznie dołączane, jeśli instalujesz obsługę języka Python za pośrednictwem Instalator programu Visual Studio (Visual Studio 2017 i nowszych). Aby uzyskać więcej informacji, zobacz Instalowanie symboli debugowania dla interpreterów języka Python w programie Visual Studio.
Uzyskiwanie dostępu do kodu źródłowego języka Python
Kod źródłowy standardowego języka Python można udostępnić podczas debugowania.
Przejdź do
https://www.python.org/downloads/source/
.Pobierz archiwum kodu źródłowego języka Python odpowiednie dla używanej wersji i wyodrębnij kod do folderu.
Gdy program Visual Studio wyświetli monit o lokalizację kodu źródłowego języka Python, wskaż określone pliki w folderze wyodrębniania.
Włączanie debugowania w trybie mieszanym w projekcie C/C++
Program Visual Studio 2017 w wersji 15.5 lub nowszej obsługuje debugowanie w trybie mieszanym z projektu C/C++. Przykładem tego użycia jest to, że chcesz osadzić język Python w innej aplikacji zgodnie z opisem w python.org.
W poniższych krokach opisano sposób włączania debugowania w trybie mieszanym dla projektu C/C++:
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt C/C++, a następnie wybierz polecenie Właściwości.
W okienku Strony właściwości wybierz kartę Debugowanie właściwości>konfiguracji.
Rozwiń menu rozwijane debugera , aby uruchomić opcję, a następnie wybierz pozycję Python/Native Debugowanie.
Uwaga
Jeśli nie widzisz opcji Debugowanie języka Python/natywnego, musisz najpierw zainstalować natywne narzędzia programistyczne języka Python przy użyciu Instalator programu Visual Studio. Opcja debugowania natywnego jest dostępna w ramach obciążenia programowanie w języku Python. Aby uzyskać więcej informacji, zobacz Instalowanie obsługi języka Python w programie Visual Studio.
Wybierz przycisk OK , aby zapisać zmiany.
Debugowanie uruchamiania programu
Jeśli używasz tej metody, nie można debugować uruchamiania py.exe
programu, ponieważ powoduje ono zduplikowanie podrzędnego python.exe
podprocesu. Debuger nie jest dołączany do podprocesu. W tym scenariuszu obejściem jest uruchomienie python.exe
programu bezpośrednio z argumentami w następujący sposób:
W okienku Strony właściwości dla projektu C/C++ przejdź do karty Debugowanie właściwości>konfiguracji.
Dla opcji Polecenie określ pełną ścieżkę
python.exe
do pliku programu.Określ żądane argumenty w polu Argumenty poleceń.
Dołączanie debugera w trybie mieszanym
W przypadku programu Visual Studio 2017 w wersji 15.4 i starszych debugowanie bezpośredniego trybu mieszanego jest włączone tylko podczas uruchamiania projektu języka Python w programie Visual Studio. Obsługa jest ograniczona, ponieważ projekty C/C++ używają tylko natywnego debugera.
W tym scenariuszu obejściem jest osobne dołączenie debugera:
Uruchom projekt C++ bez debugowania, wybierając pozycję Debuguj>rozpocznij bez debugowania lub użyj skrótu klawiaturowego Ctrl+F5.
Aby dołączyć debuger trybu mieszanego do istniejącego procesu, wybierz pozycję Dołącz debugowanie>do procesu. Zostanie otwarte okno dialogowe.
W oknie dialogowym Dołączanie do procesu wybierz odpowiedni proces z listy.
W polu Dołącz do użyj opcji Wybierz, aby otworzyć okno dialogowe Wybieranie typu kodu.
W oknie dialogowym Wybieranie typu kodu wybierz opcję Debuguj te typy kodu.
Na liście zaznacz pole wyboru Python i wybierz przycisk OK.
Wybierz pozycję Dołącz , aby uruchomić debuger.
Napiwek
Możesz dodać wstrzymanie lub opóźnienie w aplikacji C++, aby upewnić się, że nie wywołuje kodu języka Python, który chcesz debugować przed dołączeniem debugera.
Eksplorowanie funkcji specyficznych dla trybu mieszanego
Program Visual Studio udostępnia kilka funkcji debugowania w trybie mieszanym, aby ułatwić debugowanie aplikacji:
- Połączony stos wywołań
- Krok między językiem Python i kodem natywnym
- Widok wartości PyObject w kodzie natywnym
- Widok wartości natywnych w kodzie języka Python
Używanie połączonego stosu wywołań
W oknie Stos wywołań są wyświetlane ramki stosu natywnego i python przeplatane z przejściami oznaczonymi między nimi:
Aby przejść wyświetlane jako [Kod zewnętrzny] bez określania kierunku przejścia, ustaw opcję Narzędzia>Opcje>Debugowanie>ogólne>Włącz tylko mój kod.
Aby uaktywnić dowolną ramkę wywołania, kliknij dwukrotnie ramkę. Ta akcja powoduje również otwarcie odpowiedniego kodu źródłowego, jeśli jest to możliwe. Jeśli kod źródłowy jest niedostępny, ramka jest nadal aktywna i można sprawdzić zmienne lokalne.
Krok między językiem Python i kodem natywnym
Program Visual Studio udostępnia polecenia Step Into (F11) lub Step Out (Shift+F11), aby umożliwić debugerowi trybu mieszanego poprawne obsługę zmian między typami kodu.
Gdy język Python wywołuje metodę typu zaimplementowanego w języku C, krok po wywołaniu tej metody zatrzymuje się na początku funkcji natywnej, która implementuje metodę.
To samo zachowanie występuje, gdy kod natywny wywołuje funkcję interfejsu API języka Python, która powoduje wywoływanie kodu w języku Python. Przejście do wywołania
PyObject_CallObject
metody na wartość funkcji pierwotnie zdefiniowanej w języku Python zatrzymuje się na początku funkcji języka Python.Przechodzenie z języka Python do natywnego jest również obsługiwane w przypadku funkcji natywnych wywoływanych z języka Python za pośrednictwem typów ctypes.
Używanie widoku wartości PyObject w kodzie natywnym
Gdy ramka natywna (C lub C++) jest aktywna, jej zmienne lokalne są wyświetlane w oknie Ustawień lokalnych debugera. W natywnych modułach rozszerzeń języka Python wiele z tych zmiennych jest typu PyObject
(czyli typedef dla _object
), lub kilka innych podstawowych typów języka Python. W debugowaniu w trybie mieszanym te wartości przedstawiają inny węzeł podrzędny oznaczony etykietą [Widok języka Python].
Aby wyświetlić reprezentację języka Python zmiennej, rozwiń węzeł. Widok zmiennych jest identyczny z tym, co widać, jeśli zmienna lokalna odwołująca się do tego samego obiektu znajduje się w ramce języka Python. Elementy podrzędne tego węzła można edytować.
Aby wyłączyć tę funkcję, kliknij prawym przyciskiem myszy w dowolnym miejscu w oknie Ustawienia lokalne i przełącz opcję menu Pokaż węzły widoku języka Python>:
Typy C pokazujące węzły widoku języka Python
Następujące typy C pokazują węzły [Widok języka Python], jeśli są włączone:
PyObject
PyVarObject
PyTypeObject
PyByteArrayObject
PyBytesObject
PyTupleObject
PyListObject
PyDictObject
PySetObject
PyIntObject
PyLongObject
PyFloatObject
PyStringObject
PyUnicodeObject
[Widok języka Python] Nie jest automatycznie wyświetlane dla typów utworzonych samodzielnie. Podczas tworzenia rozszerzeń dla języka Python 3.x ten brak zwykle nie jest problemem. Każdy obiekt ma ob_base
ostatecznie pole jednego z wymienionych typów języka C, co powoduje wyświetlenie widoku [Python].
Wyświetlanie wartości natywnych w kodzie języka Python
Możesz włączyć widok [C++ dla wartości natywnych w oknie Ustawienia lokalne , gdy ramka języka Python jest aktywna. Ta funkcja nie jest domyślnie włączona.
Aby włączyć tę funkcję, kliknij prawym przyciskiem myszy w oknie Ustawienia lokalne i ustaw opcję menu Python>Show C++ View Nodes (Pokaż węzły widoku języka C++).
Węzeł [widok języka C++] zawiera reprezentację podstawowej struktury języka C/C++ dla wartości, identycznej z tym, co widać w ramce natywnej. Przedstawia wystąpienie (dla którego
PyLongObject
jest typedef) dla długiej_longobject
liczby całkowitej języka Python i próbuje wywnioskować typy dla klas natywnych utworzonych samodzielnie. Elementy podrzędne tego węzła można edytować.
Jeśli pole podrzędne obiektu ma typ PyObject
lub inny obsługiwany typ, ma węzeł reprezentacji [Widok języka Python] (jeśli te reprezentacje są włączone). To zachowanie umożliwia nawigowanie po grafach obiektów, w których linki nie są bezpośrednio widoczne dla języka Python.
W przeciwieństwie do węzłów [Widok języka Python], które używają metadanych obiektu języka Python do określenia typu obiektu, nie ma podobnie niezawodnego mechanizmu dla widoku [C++]. Ogólnie rzecz biorąc, biorąc pod uwagę wartość języka Python (czyli odwołanie) nie można niezawodnie określić, PyObject
która struktura języka C/C++ ją wspiera. Debuger trybu mieszanego próbuje odgadnąć typ, sprawdzając różne pola typu obiektu (na przykład PyTypeObject
przywoływalne przez jego ob_type
pole), które mają typy wskaźników funkcji. Jeśli jeden z tych wskaźników funkcji odwołuje się do funkcji, którą można rozpoznać, i ta funkcja ma self
parametr o typie bardziej specyficznym niż PyObject*
, przyjmuje się, że ten typ jest typem kopii zapasowej.
Rozważmy następujący przykład, w którym ob_type->tp_init
wartość dla danego obiektu wskazuje następującą funkcję:
static int FobObject_init(FobObject* self, PyObject* args, PyObject* kwds) {
return 0;
}
W takim przypadku debuger może poprawnie określić, że typ C obiektu to FobObject
. Jeśli debuger nie może określić bardziej precyzyjnego typu z tp_init
, przechodzi do innych pól. Jeśli nie jest w stanie wyłudić typu z dowolnego z tych pól, węzeł [C++ view] przedstawia obiekt jako PyObject
wystąpienie.
Aby zawsze uzyskać przydatną reprezentację niestandardowych typów utworzonych, najlepiej zarejestrować co najmniej jedną specjalną funkcję podczas rejestrowania typu i użyć silnie typizowanego self
parametru. Większość typów spełnia to wymaganie naturalnie. W przypadku innych typów inspekcja tp_init
jest zwykle najwygodniejszym wejściem do użytku w tym celu. Fikcyjna implementacja tp_init
typu, który jest obecny wyłącznie w celu włączenia wnioskowania typu debugera, może natychmiast zwrócić zero, jak w poprzednim przykładzie.
Przegląd różnic w porównaniu ze standardowym debugowaniem języka Python
Debuger trybu mieszanego różni się od standardowego debugera języka Python. Wprowadzono pewne dodatkowe funkcje, ale nie ma niektórych funkcji związanych z językiem Python w następujący sposób:
- Nieobsługiwane funkcje obejmują warunkowe punkty przerwania, okno Debugowanie interakcyjne i zdalne debugowanie międzyplatformowe.
- Okno Natychmiastowe jest dostępne, ale z ograniczonym podzestawem jego funkcjonalności, w tym wszystkie ograniczenia wymienione w tej sekcji.
- Obsługiwane wersje języka Python obejmują tylko wersje CPython 2.7 i 3.3 lub nowsze.
- Aby użyć języka Python z programem Visual Studio Shell (na przykład w przypadku zainstalowania go ze zintegrowanym instalatorem), program Visual Studio nie może otworzyć projektów języka C++. W związku z tym środowisko edycji dla plików C++ jest tylko podstawowym edytorem tekstów. Jednak debugowanie języka C/C++ i debugowanie w trybie mieszanym jest w pełni obsługiwane w powłoce z kodem źródłowym, przechodzenie do kodu natywnego i ocena wyrażeń języka C++ w oknach debugera.
- Podczas wyświetlania obiektów języka Python w oknach narzędzi debugera Locals and Watch debugger debuger w trybie mieszanym pokazuje tylko strukturę obiektów. Nie ocenia automatycznie właściwości ani nie wyświetla obliczonych atrybutów. W przypadku kolekcji są wyświetlane tylko elementy dla wbudowanych typów kolekcji (
tuple
,list
,dict
,set
). Niestandardowe typy kolekcji nie są wizualizowane jako kolekcje, chyba że są dziedziczone z określonego typu kolekcji wbudowanej. - Ocena wyrażeń jest obsługiwana zgodnie z opisem w poniższej sekcji.
Używanie oceny wyrażeń
Standardowy debuger języka Python umożliwia ocenę dowolnych wyrażeń języka Python w oknach kontrolnych i natychmiastowych , gdy debugowany proces jest wstrzymany w dowolnym momencie w kodzie, o ile nie jest blokowany w operacji we/wy lub w innym podobnym wywołaniu systemowym. W debugowaniu w trybie mieszanym dowolne wyrażenia mogą być oceniane tylko wtedy, gdy są zatrzymywane w kodzie języka Python, po punkcie przerwania lub podczas przechodzenia do kodu. Wyrażenia można oceniać tylko w wątku, w którym wystąpił punkt przerwania lub operacja kroku.
Gdy debuger zatrzymuje się w kodzie natywnym lub w kodzie języka Python, w którym nie są stosowane opisane warunki, takie jak po operacji wyjścia lub w innym wątku. Ocena wyrażeń jest ograniczona do uzyskiwania dostępu do zmiennych lokalnych i globalnych w zakresie aktualnie wybranej ramki, uzyskiwania dostępu do pól i indeksowania wbudowanych typów kolekcji z literałami. Na przykład następujące wyrażenie można ocenić w dowolnym kontekście (pod warunkiem, że wszystkie identyfikatory odnoszą się do istniejących zmiennych i pól odpowiednich typów):
foo.bar[0].baz['key']
Debuger trybu mieszanego rozwiązuje również takie wyrażenia inaczej. Wszystkie operacje dostępu do składowych wyszukują tylko pola, które są bezpośrednio częścią obiektu (np. wpis w obiekcie __dict__
lub __slots__
, lub pole natywnej struktury uwidocznionej w języku Python za pośrednictwem tp_members
), i ignorują dowolną __getattr__
logikę deskryptora , __getattribute__
lub deskryptora. Podobnie wszystkie operacje indeksowania ignorują __getitem__
metodę i uzyskują bezpośredni dostęp do wewnętrznych struktur danych kolekcji.
Ze względu na spójność ten schemat rozpoznawania nazw jest używany dla wszystkich wyrażeń, które są zgodne z ograniczeniami w przypadku oceny ograniczonego wyrażenia. Ten schemat jest stosowany niezależnie od tego, czy dowolne wyrażenia są dozwolone w bieżącym punkcie zatrzymania. Aby wymusić właściwą semantyka języka Python, gdy jest dostępny w pełni funkcjonalny ewaluator, należy ująć wyrażenie w nawiasy:
(foo.bar[0].baz['key'])