Отладка Python и C++ вместе в Visual Studio

Большинство обычных отладчиков Python поддерживают только отладку кода Python, но это распространенная практика для разработчиков использовать Python с C или C++. Некоторые сценарии, использующие смешанный код, — это приложения, требующие высокой производительности или возможности непосредственного вызова API платформы, часто кодируются в Python и C++.

Visual Studio предоставляет интегрированную, одновременную отладку в смешанном режиме для кода Python и собственного кода C/C++. Поддержка доступна при выборе параметра собственных средств разработки Python для рабочей нагрузки разработки Python в установщике Visual Studio:

Снимок экрана: параметр собственных средств разработки Python, выбранный в установщике Visual Studio.

В этой статье вы узнаете, как работать со следующими функциями отладки в смешанном режиме:

  • объединенный стек вызовов;
  • Переход между кодом Python и машинным кодом
  • точки останова в обоих типах кода;
  • Просмотр представлений объектов Python в собственных кадрах и наоборот
  • Отладка в контексте проекта Python или проекта C++

Снимок экрана: пример отладки в смешанном режиме для кода Python и C++ в Visual Studio.

Необходимые компоненты

  • Visual Studio 2017 и более поздних версий. Отладка в смешанном режиме недоступна с Инструменты Python для Visual Studio 1.x в Visual Studio 2015 и более ранних версиях.

  • Visual Studio, установленная с поддержкой рабочих нагрузок Python. Дополнительные сведения см. в статье "Установка поддержки Python в Visual Studio".

Включение отладки в смешанном режиме в проекте Python

Ниже описано, как включить отладку в смешанном режиме в проекте Python:

  1. В Обозреватель решений щелкните правой кнопкой мыши проект Python и выберите "Свойства".

  2. В области "Свойства" выберите вкладку "Отладка", а затем выберите параметр отладки> "Включить отладку машинного кода":

    Снимок экрана: настройка свойства отладки машинного кода в Visual Studio.

    Этот параметр включает смешанный режим для всех сеансов отладки.

    Совет

    При включении отладки машинного кода окно вывода Python может закрыться сразу после завершения программы без приостановки и нажатия клавиши, чтобы продолжить запрос. Чтобы принудительно принудить приостановку и запрос после включения отладки машинного кода, добавьте -i аргумент в поле "Аргументы интерпретатора запуска>" на вкладке "Отладка". Этот аргумент помещает интерпретатор Python в интерактивный режим после выполнения кода. Программа ожидает нажатия клавиш CTRL+Z+ВВОД, чтобы закрыть окно.

  3. Нажмите кнопку "Сохранить файл>" (или CTRL+S), чтобы сохранить изменения свойств.

  4. Чтобы подключить отладчик смешанного режима к существующему процессу, выберите "Подключить отладку>к процессу". Откроется диалоговое окно.

    1. В диалоговом окне "Присоединение к процессу " выберите соответствующий процесс из списка.

    2. Чтобы подключиться к полю, используйте параметр "Выбрать", чтобы открыть диалоговое окно "Выбор типа кода".

    3. В диалоговом окне "Выбор типа кода" выберите параметр "Отладка этих типов кода".

    4. В списке выберите проверка box Python (собственный) и нажмите кнопку ОК:

      Снимок экрана: выбор типа кода Python (машинного кода) для отладки в Visual Studio.

    5. Нажмите кнопку "Присоединить" , чтобы запустить отладчик.

    Параметры типа кода являются постоянными. Если вы хотите отключить отладку в смешанном режиме и подключиться к другому процессу позже, удалите тип кода Python (машинный код) проверка box и выберите тип кода машинного кода проверка box.

    Вы можете выбрать другие типы кода в дополнение к параметру Native или вместо него. Например, если управляемое приложение размещает CPython, которое, в свою очередь, использует собственные модули расширения, и вы хотите выполнить отладку всех трех проектов кода, выберите Python, Native и Managed проверка boxes. Этот подход обеспечивает унифицированную отладку, включая объединенные стеки вызовов и пошаговое выполнение между всеми тремя средами выполнения.

Работа с виртуальными средами

При использовании этого метода отладки в смешанном режиме для виртуальных сред (venvs), Python для Windows использует python.exe заглушку для venvs, который Visual Studio находит и загружает в качестве подпроцесса.

  • Для Python 3.8 и более поздних версий смешанный режим не поддерживает многопроцессную отладку. При запуске сеанса отладки подпроцесс заглушки выполняется отладка, а не приложение. В сценариях присоединения обходное решение заключается в подключении к правильному python.exe файлу. При запуске приложения с отладкой (например, с помощью сочетания клавиш F5 ) можно создать venv с помощью команды C:\Python310-64\python.exe -m venv venv --symlinks. В команде вставьте предпочитаемую версию Python. По умолчанию в Windows могут создаваться только администраторы.

  • Для версий Python до версии 3.8 отладка смешанного режима должна работать должным образом с venvs.

Выполнение в глобальной среде не приводит к возникновению этих проблем для любой версии Python.

Установка символов Python

При первом запуске отладки в смешанном режиме может появиться диалоговое окно "Обязательные символы Python". Необходимо установить символы только один раз для любой конкретной среды Python. Символы автоматически включаются при установке поддержки Python с помощью Установщика Visual Studio (Visual Studio 2017 и более поздних версий). Дополнительные сведения см. в разделе "Установка символов отладки для интерпретаторов Python" в Visual Studio.

Доступ к исходному коду 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 в другое приложение, как описано в python.org.

Ниже описано, как включить отладку в смешанном режиме для проекта C/C++:

  1. В Обозреватель решений щелкните правой кнопкой мыши проект C/C++ и выберите "Свойства".

  2. На панели "Страницы свойств" перейдите на вкладку "Свойства>конфигурации отладки".

  3. Разверните раскрывающееся меню для отладчика, чтобы запустить параметр и выберите Python/Native Debugging.

    Снимок экрана: выбор параметра

    Примечание.

    Если вы не видите параметр отладки Python/Native, сначала необходимо установить собственные средства разработки Python с помощью Установщика Visual Studio. Параметр собственной отладки доступен в рабочей нагрузке разработки Python. Дополнительные сведения см. в статье "Установка поддержки Python в Visual Studio".

  4. Нажмите ОК, чтобы сохранить изменения.

Отладка средства запуска программы

При использовании этого метода невозможно выполнить отладку py.exe средства запуска программы, так как она создает дочерний python.exe подпроцесс. Отладчик не подключается к подпроцессу. В этом сценарии обходное решение заключается в том, чтобы запустить python.exe программу непосредственно с аргументами, как показано ниже.

  1. На панели "Страницы свойств" проекта C/C++ перейдите на вкладку "Свойства>конфигурации отладки".

  2. Для параметра Command укажите полный путь к файлу python.exe программы.

  3. Укажите нужные аргументы в поле "Аргументы команд".

Присоединение отладчика смешанного режима

Для Visual Studio 2017 версии 15.4 и более ранних версий прямая отладка в смешанном режиме включена только при запуске проекта Python в Visual Studio. Поддержка ограничена, так как проекты C/C++ используют только собственный отладчик.

В этом сценарии обходной путь заключается в подключении отладчика отдельно:

  1. Запустите проект C++ без отладки, выбрав "Начать отладку>" без отладки или используйте сочетание клавиш CTRL+F5.

  2. Чтобы подключить отладчик смешанного режима к существующему процессу, выберите "Подключить отладку>к процессу". Откроется диалоговое окно.

    1. В диалоговом окне "Присоединение к процессу " выберите соответствующий процесс из списка.

    2. Чтобы подключиться к полю, используйте параметр "Выбрать", чтобы открыть диалоговое окно "Выбор типа кода".

    3. В диалоговом окне "Выбор типа кода" выберите параметр "Отладка этих типов кода".

    4. В списке выберите элемент Python проверка box и нажмите кнопку "ОК".

    5. Нажмите кнопку "Присоединить" , чтобы запустить отладчик.

Совет

Вы можете добавить паузу или задержку в приложении C++, чтобы убедиться, что он не вызывает код Python, который необходимо выполнить отладку перед присоединением отладчика.

Изучение конкретных функций в смешанном режиме

Visual Studio предоставляет несколько функций отладки в смешанном режиме, чтобы упростить отладку приложения:

Использование объединенного стека вызовов

В окне стека вызовов представлены чередующиеся кадры стеков машинного кода и Python с помеченными переходами.

Снимок экрана: окно объединенного стека вызовов с отладкой в смешанном режиме в Visual Studio.

  • Чтобы переходы отображались как [внешний код] без указания направления перехода, задайте параметр "Параметры отладки>инструментов>>",>чтобы включить только мой код.

  • Чтобы сделать любой кадр вызова активным, дважды щелкните кадр. Это действие также открывает соответствующий исходный код, если это возможно. Если исходный код недоступен, кадр по-прежнему активен и локальные переменные можно проверить.

Переход между кодом Python и машинным кодом

Visual Studio предоставляет команды Step Into (F11) или Step Out (Shift+F11), чтобы отладчик смешанного режима правильно обрабатывал изменения между типами кода.

  • Когда Python вызывает метод типа, реализованного в C, шаг в вызове этого метода останавливается в начале собственной функции, реализующей метод.

  • Такое же поведение возникает, когда машинный код вызывает функцию API Python, которая приводит к вызову кода Python. Шаг в вызов PyObject_CallObject значения функции, первоначально определенного в Python, останавливается в начале функции Python.

  • Шаг с заходом из Python в машинный код также поддерживается для функций машинного кода, которые вызываются из Python с помощью модуля ctypes.

Использование представления значений PyObject в машинном коде

Если кадр машинного кода (C или C++) активен, его локальные переменные отображаются в окне локальных переменных отладчика. В собственных модулях расширения Python многие из этих переменных имеют тип (который является типизированным PyObject для _object), или несколько других основных типов Python. В отладке в смешанном режиме эти значения представляют еще один дочерний узел с меткой [представление Python].

  • Чтобы просмотреть представление Python переменной, разверните узел. Представление переменных идентично тому, что вы видите, если локальная переменная, ссылающаяся на тот же объект, присутствует в кадре Python. Дочерние узлы данного узла можно изменять.

    Снимок экрана: представление Python в окне

  • Чтобы отключить эту функцию, щелкните правой кнопкой мыши в любом месте в окне локальных переменных и снимите флажок Python>Показать узлы представлений Python:

    Снимок экрана: включение параметра

Типы C, показывающие узлы представления Python

В следующих типах C отображаются узлы [представление Python] , если они включены:

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

[Представление Python] не отображается автоматически для типов, которые вы создаете самостоятельно. При создании расширений для Python 3.x это отсутствие обычно не является проблемой. Любой объект в конечном счете ob_base имеет поле одного из перечисленных типов C, что приводит к отображению [представления Python] .

Просмотр собственных значений в коде Python

Вы можете включить представление [C++] для собственных значений в окне "Локальные" при активном фрейме Python. Эта функция по умолчанию не включена.

  • Чтобы включить эту функцию, щелкните правой кнопкой мыши в окне "Локальные" и установите пункт меню "Показать узлы представления C++ на Python>" (Показать узлы представления C++).

    Снимок экрана: включение параметров

  • Узел [представление C++] предоставляет представление базовой структуры C/C++ для значения, идентичного тому, что отображается в собственном кадре. В нем показан экземпляр _longobject (для которого PyLongObject является типдифец) для длинного целого числа Python, и он пытается определить типы для собственных классов, которые вы создаете самостоятельно. Дочерние узлы данного узла можно изменять.

    Снимок экрана: представление C++ в окне

Если дочернее поле объекта имеет тип PyObjectили другой поддерживаемый тип, он имеет узел представления [представление Python] (если эти представления включены). Это поведение позволяет перемещать графы объектов, в которых ссылки не предоставляются непосредственно в Python.

В отличие от узлов представления Python, использующих метаданные объекта Python для определения типа объекта, такого же надежного механизма для представления C++ не существует. В сущности, для заданного значения Python (для ссылки PyObject) невозможно точно определить резервную структуру C/C++. Отладчик смешанного режима пытается угадать тип, просматривая различные поля типа объекта (например PyTypeObject , на него ссылается ob_type поле), которые имеют типы указателей функций. Если одна из этих указателей функции ссылается на функцию, которую можно разрешить, и эта функция имеет self параметр с типом более конкретным, чем PyObject*, то этот тип считается резервным типом.

Рассмотрим следующий пример, где ob_type->tp_init значение для заданного объекта указывает на следующую функцию:

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

В этом случае отладчик может правильно вывести тип объекта FobObjectВ. Если отладчик не может определить более точный тип, tp_initон переходит к другим полям. Если не удается определить тип на основе любого из этих полей, узел представления C++ представляет объект как экземпляр PyObject.

Чтобы всегда получать полезное представление пользовательских типов, рекомендуется зарегистрировать по крайней мере одну специальную функцию при регистрации типа и использовать строго типизированный параметр self. Большинство типов выполняют это требование естественным образом. Для других типов проверка tp_init обычно является наиболее удобной записью, используемой для этой цели. Фиктивная реализация tp_init для типа, который присутствует исключительно для включения вывода типа отладчика, может просто возвращать ноль немедленно, как в предыдущем примере.

Просмотр различий от стандартной отладки Python

Отладчик смешанного режима отличается от стандартного отладчика Python. В нем представлены некоторые дополнительные функции, но не хватает некоторых возможностей, связанных с Python, как показано ниже.

  • Неподдерживаемые функции включают условные точки останова, интерактивное окно отладки и кроссплатформенную удаленную отладку.
  • Окно интерпретации доступно, но с ограниченным подмножеством его функциональных возможностей, включая все ограничения, перечисленные в этом разделе.
  • Поддерживаемые версии Python включают только CPython 2.7 и 3.3+.
  • Чтобы использовать Python с Оболочкой Visual Studio (например, если установить его с помощью интегрированного установщика), Visual Studio не сможет открыть проекты C++ . В результате редактирование файлов C++ — это только базовый текстовый редактор. В оболочке полностью поддерживается отладка C/C++ и отладка в смешанном режиме, что позволяет работать с исходным кодом, переходить к машинному коду и вычислять выражения C++ в окнах отладчика.
  • При просмотре объектов Python в окнах инструментов "Локальные" и "Контрольные отладчики" отладчик смешанного режима отображает только структуру объектов. Он не вычисляет свойства автоматически или не отображает вычисляемых атрибутов. Для коллекций будут отображаться только элементы встроенных типов (tuple, list, dict, set). Пользовательские типы коллекций не визуализированы как коллекции, если они не наследуются от какого-то встроенного типа коллекции.
  • Оценка выражений обрабатывается, как описано в следующем разделе.

Использование вычисления выражений

Стандартный отладчик Python позволяет оценивать произвольные выражения Python в окнах "Контрольные и немедленные " при приостановке отлаживаемого процесса в любой точке кода, если он не блокируется в операции ввода-вывода или другом аналогичном системном вызове. В смешанном режиме отладки произвольные выражения можно вычислить только при остановке в коде Python после точки останова или при шаге с заходом в код. Выражения можно вычислить только в потоке, где были выполнены вышеуказанные операции.

Когда отладчик останавливается в машинном коде или в коде Python, где описанные условия не применяются, например после операции поэтапного выхода или в другом потоке). Оценка выражений ограничена доступом к локальным и глобальным переменным в область выбранного кадра, доступом к их полям и индексированием встроенных типов коллекций с помощью литералом. Например, следующее выражение можно оценить в любом контексте (если все идентификаторы ссылаются на существующие переменные и поля соответствующих типов):

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

Отладчик смешанного режима разрешает такие выражения по-разному. Все операции доступа к членам ищут только поля, которые непосредственно являются частью объекта (например, запись в ней __dict__ или __slots__поле собственной структуры, предоставляемой Python через tp_members), и игнорируйте любую __getattr____getattribute__логику дескриптора или дескриптора. Аналогичным образом все операции индексирования игнорируют __getitem__ и получают доступ к внутренним структурам данных коллекций напрямую.

Для обеспечения согласованности эта схема разрешения имен используется для всех выражений, которые соответствуют ограничениям для оценки ограниченного выражения. Эта схема применяется независимо от того, разрешены ли произвольные выражения в текущей точке остановки. Чтобы обеспечить правильную семантику Python, когда доступен полнофункциональный вычислитель, заключите выражение в скобки.

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