Compartir a través de


Depurar Python y C++ juntos en Visual Studio

La mayoría de los depuradores de Python normales solo admiten la depuración de código de Python, pero es habitual que los desarrolladores usen Python con C o C++. Algunos escenarios que usan código mixto son aplicaciones que requieren un alto rendimiento o la capacidad de invocar directamente las API de plataforma se codifican a menudo en Python y C o C++.

Visual Studio proporciona depuración en modo mixto integrado y simultáneo para código Python y C/C++ nativo. La compatibilidad está disponible al seleccionar la opción Herramientas de desarrollo nativo de Python para la carga de trabajo Desarrollo de Python en el instalador de Visual Studio:

Captura de pantalla que muestra la opción Herramientas de desarrollo nativas de Python seleccionada en el Instalador de Visual Studio.

En este artículo, explorarás cómo trabajar con las siguientes funcionalidades de depuración en modo mixto:

  • Pilas de llamadas combinadas
  • Paso entre Python y código nativo
  • Puntos de interrupción en ambos tipos de código
  • Visualización de representaciones de Python de objetos en marcos nativos y viceversa
  • Depuración en el contexto del proyecto de Python o del proyecto de C++

Captura de pantalla que muestra un ejemplo de depuración en modo mixto para código de Python y C++ en Visual Studio.

Prerrequisitos

  • Visual Studio 2017 y versiones posteriores. La depuración en modo mixto no está disponible con Herramientas de Python para Visual Studio 1.x en Visual Studio 2015 y versiones anteriores.

  • Visual Studio instalado con compatibilidad con cargas de trabajo de Python. Para obtener más información, consulte Instalación de compatibilidad con Python en Visual Studio.

Habilitar la depuración en modo mixto en un proyecto de Python

En los pasos siguientes se describe cómo habilitar la depuración en modo mixto en un proyecto de Python:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto de Python y seleccione Propiedades.

  2. En el panel Propiedades , seleccione la pestaña Depurar y, a continuación, seleccione la opción Habilitar depuración>de código nativo :

    Captura de pantalla que muestra cómo establecer la propiedad Habilitar depuración de código nativo en Visual Studio.

    Esta opción habilita el modo mixto para todas las sesiones de depuración.

    Sugerencia

    Al habilitar la depuración de código nativo, la ventana de salida de Python podría cerrarse inmediatamente después de que el programa finalice sin pausar ni mostrar la solicitud de Presionar cualquier tecla para continuar. Para forzar la pausa y el aviso después de habilitar la depuración de código nativo, agregue el argumento -i al campo Ejecutar>Argumentos del intérprete en la pestaña Depurar. Este argumento coloca el intérprete de Python en modo interactivo después de que se ejecute el código. El programa espera a que seleccione Ctrl+Z+Entrar para cerrar la ventana.

  3. Seleccione Guardar archivo>(oCtrl+S) para guardar los cambios de propiedad.

  4. Para asociar el depurador en modo mixto a un proceso existente, seleccione Depurar>Asociar al proceso. Se abre un cuadro de diálogo.

    1. En el cuadro de diálogo Adjuntar al Proceso, seleccione el proceso adecuado de la lista.

    2. Para el campo Asociar a, use la opción Seleccionar para abrir la ventana de diálogo Seleccionar tipo de código.

    3. En el cuadro de diálogo Seleccionar tipo de código, elija la opción Depurar estos tipos de código .

    4. En la lista, active la casilla Python (nativo) y seleccione Aceptar:

      Captura de pantalla que muestra cómo seleccionar el tipo de código python (nativo) para la depuración en Visual Studio.

    5. Seleccione Adjuntar para iniciar el depurador.

    La configuración del tipo de código es persistente. Si desea deshabilitar la depuración en modo mixto y asociar a otro proceso más adelante, desactive la casilla Tipo de código de Python (nativo) y active la casilla Tipo de código nativo .

    Puede seleccionar otros tipos de código además de o en lugar de la opción Nativa . Por ejemplo, si una aplicación administrada hospeda CPython, que a su vez usa módulos de extensión nativos y desea depurar los tres proyectos de código, active las casillas Python, Nativo y Administrado . Este enfoque proporciona una experiencia de depuración unificada, incluidas las pilas de llamadas combinadas y la depuración paso a paso entre los tres entornos de ejecución.

Trabajar con entornos virtuales

Cuando utilizas este método de depuración en modo mixto para entornos virtuales (venvs), Python para Windows emplea un python.exe archivo stub para venvs que Visual Studio encuentra y carga como un subproceso.

  • Para Python 3.8 y versiones posteriores, el modo mixto no admite la depuración en entornos multiproceso. Al iniciar la sesión de depuración, se depura el subproceso de stub en lugar de la aplicación. En escenarios de adjuntar, la solución alternativa consiste en adjuntar al archivo correcto python.exe. Al iniciar la aplicación con depuración (por ejemplo, mediante el método abreviado de teclado F5 ), puede crear su venv mediante el comando C:\Python310-64\python.exe -m venv venv --symlinks. En el comando , inserte la versión preferida de Python. De forma predeterminada, solo los administradores pueden crear vínculos simbólicos en Windows.

  • En el caso de las versiones de Python anteriores a la 3.8, la depuración en modo mixto debería funcionar según lo previsto con venvs.

La ejecución en un entorno global no provoca estos problemas para ninguna versión de Python.

Instalación de símbolos de Python

Al iniciar la depuración en modo mixto por primera vez, es posible que vea un cuadro de diálogo Símbolos de Python necesarios. Debe instalar los símbolos solo una vez para cualquier entorno de Python determinado. Los símbolos se incluyen automáticamente si instala compatibilidad con Python mediante el Instalador de Visual Studio (Visual Studio 2017 y versiones posteriores). Para obtener más información, consulte Instalación de símbolos de depuración para intérpretes de Python en Visual Studio.

Acceso al código fuente de Python

Puede hacer que el código fuente de Python estándar esté disponible para depuración.

  1. Ir a https://www.python.org/downloads/source/.

  2. Descargue el archivo de código fuente de Python adecuado para la versión y extraiga el código en una carpeta.

  3. Cuando Visual Studio solicita la ubicación del código fuente de Python, apunte a los archivos específicos de la carpeta de extracción.

Habilitar la depuración en modo mixto en un proyecto de C/C++

Visual Studio 2017, versión 15.5 y versiones posteriores, admite la depuración en modo mixto desde un proyecto de C/C++. Un ejemplo de este uso es cuando desea insertar Python en otra aplicación, tal como se describe en python.org.

En los pasos siguientes se describe cómo habilitar la depuración en modo mixto para un proyecto de C/C++:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto de C/C++ y seleccione Propiedades.

  2. En el panel Páginas de propiedades, seleccione la pestaña Propiedades de configuración>Depuración.

  3. Expanda el menú desplegable del depurador para iniciar la opción y seleccione Python/Depuración nativa.

    Captura de pantalla que muestra cómo seleccionar la opción Depuración nativa de Python para un proyecto de C/C++ en Visual Studio.

    Nota:

    Si no ve la opción Depuración de Python/Nativa, primero debe instalar las herramientas de desarrollo nativo de Python mediante el Instalador de Visual Studio. La opción de depuración nativa está disponible en la carga de trabajo de desarrollo de Python. Para obtener más información, consulte Instalación de compatibilidad con Python en Visual Studio.

  4. Seleccione Aceptar para guardar los cambios.

Depurar el iniciador de programas

Cuando se usa este método, no se puede depurar el py.exe iniciador de programas porque genera un subproceso secundario python.exe . El depurador no se asocia al subproceso. En este escenario, la solución alternativa consiste en iniciar el python.exe programa directamente con argumentos, como se indica a continuación:

  1. En el panel Páginas de propiedades del proyecto de C/C++, vaya a la pestaña Propiedades de configuración>Depuración.

  2. Para la opción Comando , especifique la ruta de acceso completa al archivo de python.exe programa.

  3. Especifique los argumentos deseados en el campo Argumentos de comando .

Adjuntar el depurador de modo mixto

Para Visual Studio 2017, versión 15.4 y anteriores, la depuración directa en modo mixto solo se habilita al iniciar un proyecto de Python en Visual Studio. La compatibilidad es limitada porque los proyectos de C/C++ solo usan el depurador nativo.

En este escenario, la solución alternativa es asociar el depurador por separado:

  1. Inicie el proyecto de C++ sin depurar seleccionando Depurar>Iniciar sin depurar o use el atajo de teclado Ctrl+F5.

  2. Para asociar el depurador en modo mixto a un proceso existente, seleccione Depurar>Asociar al proceso. Se abre un cuadro de diálogo.

    1. En el cuadro de diálogo Adjuntar al proceso, seleccione el proceso adecuado de la lista.

    2. Para el campo Asociar a, use la opción Seleccionar para abrir el cuadro de diálogo Seleccionar Tipo de Código.

    3. En el cuadro de diálogo Seleccionar tipo de código, elija la opción Depurar estos tipos de código .

    4. En la lista, active la casilla Python y seleccione Aceptar.

    5. Seleccione Adjuntar para iniciar el depurador.

Sugerencia

Puede agregar una pausa o un retraso en la aplicación de C++ para asegurarse de que no llama al código de Python que desea depurar antes de adjuntar el depurador.

Exploración de características específicas del modo mixto

Visual Studio proporciona varias características de depuración en modo mixto para facilitar la depuración de la aplicación:

Uso de una pila de llamadas combinada

En la ventana Pila de llamadas se muestran los marcos de pila nativos y de Python intercalados, con transiciones marcadas entre las dos:

Captura de pantalla de la ventana de pila de llamadas combinada con depuración en modo mixto en Visual Studio.

  • Para que las transiciones aparezcan como [Código externo] sin especificar la dirección de la transición, use el panel Herramientas>Opciones. Expanda la sección Todas las configuraciones>Depuración>General, active la casilla Habilitar Solo Mi Código.
  • Para hacer que las transiciones aparezcan como [Código externo] sin especificar la dirección de la transición, use el cuadro de diálogo Herramientas>Opciones. Expanda la sección Depuración>general , active la casilla Habilitar solo mi código y, a continuación, seleccione Aceptar.
  • Para activar cualquier trama de llamada, haga doble clic sobre ella. Esta acción también abre el código fuente correspondiente, si es posible. Si el código fuente no está disponible, el marco todavía está activo y se pueden inspeccionar las variables locales.

Paso entre Python y código nativo

Visual Studio proporciona los comandos Step Into (F11) o Step Out (Mayús+F11) para permitir que el depurador en modo mixto controle correctamente los cambios entre tipos de código.

  • Cuando Python llama a un método de un tipo implementado en C, la ejecución paso a paso de la llamada a ese método se detiene al principio de la función nativa que lo implementa.

  • Este mismo comportamiento se produce cuando el código nativo llama a una función de LA API de Python que da lugar a que se invoque el código de Python. Entrar en una llamada a PyObject_CallObject sobre un valor funcional originalmente definido en Python se detiene al inicio de la función de Python.

  • La ejecución paso a paso desde Python a nativo también se admite para las funciones nativas invocadas desde Python a través de ctypes.

Uso de la vista de valores pyObject en código nativo

Cuando un marco nativo (C o C++) está activo, sus variables locales aparecen en la ventana Variables locales del depurador. En los módulos de extensión nativos de Python, muchas de estas variables son del tipo PyObject (que es una definición de tipo de _object), o de algunos otros tipos fundamentales de Python. En la depuración en modo mixto, estos valores presentan otro nodo secundario con la etiqueta [vista de Python].

  • Para ver la representación de Python de la variable, expanda el nodo. La vista de las variables es idéntica a la que ve si una variable local que hace referencia al mismo objeto está presente en un marco de Python. Los hijos de este nodo son editables.

    Captura de pantalla que muestra la vista de Python en la ventana Variables locales de Visual Studio.

  • Para deshabilitar esta característica, haga clic con el botón derecho en cualquier lugar de la ventana Variables locales y active la opción de menúMostrar nodos de vista de Python de >:

    Captura de pantalla que muestra cómo habilitar la opción Mostrar nodos de vista de Python para la ventana Variables locales.

Tipos de C que muestran nodos de vista de Python

Los siguientes tipos de C muestran nodos [vista de Python], si están habilitados:

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

[Vista de Python] no aparece automáticamente para los tipos que cree usted mismo. Al crear extensiones para Python 3.x, esta falta no suele ser un problema. En última instancia, cualquier objeto tiene un ob_base campo de uno de los tipos de C enumerados, lo que hace que aparezca [vista de Python ].

Visualización de valores nativos en código de Python

Puede habilitar una [vista de C++] para los valores nativos en la ventana Variables locales cuando un marco de Python está activo. Esta característica no está habilitada de forma predeterminada.

  • Para habilitar la característica, haga clic con el botón derecho en la ventana Variables locales y establezca la opción de menúVer nodos de C++ de >.

    Captura de pantalla que muestra cómo habilitar las opciones de visualización de nodos de C++ para la ventana Variables locales.

  • El nodo [Vista de C++] proporciona una representación de la estructura de C/C++ subyacente para un valor, idéntico a lo que se ve en un marco nativo. Muestra una instancia de _longobject (para la cual PyLongObject es un typedef) para un entero largo de Python e intenta inferir tipos para las clases nativas que creas tú mismo. Los hijos de este nodo son editables.

    Captura de pantalla que muestra la vista de C++ en la ventana Variables locales de Visual Studio.

Si un campo secundario de un objeto es de tipo PyObject, u otro tipo admitido, tiene un nodo de representación [vista de Python] (si esas representaciones están habilitadas). Este comportamiento permite navegar por gráficos de objetos en los que los vínculos no se exponen directamente a Python.

A diferencia de los nodos [vista de Python], que usan metadatos de objeto de Python para determinar el tipo del objeto, no hay ningún mecanismo de confianza similar para [vista de C++]. Por lo general, dado un valor de Python (es decir, una PyObject referencia), no es posible determinar de forma confiable qué estructura de C/C++ lo respalda. El depurador en modo mixto intenta inferir el tipo examinando varios campos del tipo del objeto (como el PyTypeObject, al que hace referencia el campo ob_type) que tienen tipos de puntero de función. Si uno de esos punteros de función hace referencia a una función que se puede resolver y esa función tiene un parámetro con un tipo más específico que , se asume que ese tipo es el tipo de respaldo.

Considere el ejemplo siguiente, donde el ob_type->tp_init valor de de un objeto determinado apunta a la siguiente función:

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

En este caso, el depurador puede deducir correctamente que el tipo C del objeto es FobObject. Si el depurador no puede determinar un tipo más preciso de tp_init, pasa a otros campos. Si no puede deducir el tipo de cualquiera de esos campos, el nodo [vista de C++] presenta el objeto como una PyObject instancia.

Para obtener siempre una representación útil para los tipos creados personalizados, es mejor registrar al menos una función especial al registrar el tipo y usar un parámetro fuertemente tipado self . La mayoría de los tipos cumplen ese requisito de forma natural. Para otros tipos, la tp_init verificación suele ser la entrada más conveniente para este propósito. Una implementación ficticia de tp_init para un tipo que está presente únicamente para habilitar la inferencia de tipos de depurador puede devolver cero inmediatamente, como en el ejemplo anterior.

Revisión de las diferencias con respecto a la depuración estándar de Python

El depurador en modo mixto es distinto del depurador de Python estándar. Presenta algunas características adicionales, pero carece de algunas funcionalidades relacionadas con Python, como se indica a continuación:

  • Entre las características no admitidas se incluyen puntos de interrupción condicionales, la ventana Interactiva de depuración y la depuración remota multiplataforma.
  • La ventana Inmediato está disponible, pero con un subconjunto limitado de su funcionalidad, incluidas todas las limitaciones enumeradas en esta sección.
  • Las versiones de Python admitidas incluyen solo CPython 2.7 y 3.3+.
  • Para usar Python con Visual Studio Shell (por ejemplo, si lo instala con el instalador integrado), Visual Studio no puede abrir proyectos de C++. Como resultado, la experiencia de edición de los archivos de C++ es solo la de un editor de texto básico. Sin embargo, la depuración de C/C++ y la depuración en modo mixto son totalmente compatibles en Shell con acceso al código fuente, ingreso en código nativo y evaluación de expresiones de C++ en las ventanas del depurador.
  • Cuando se visualizan objetos de Python en las ventanas de herramientas de Variables locales y Inspección del depurador, el depurador de modo mixto solo muestra la estructura de los objetos. No evalúa automáticamente las propiedades ni muestra los atributos calculados. En el caso de las colecciones, solo muestra elementos para los tipos de colección integrados (tuple, list, dict, set). Los tipos de colección personalizados no se visualizan como colecciones, a menos que se hereden de algún tipo de colección integrado.
  • La evaluación de expresiones se lleva a cabo como se describe en la sección siguiente.

Uso de la evaluación de expresiones

El depurador estándar de Python permite la evaluación de expresiones arbitrarias de Python en las ventanas Inspección e Inmediato cuando el proceso depurado está en pausa en cualquier punto del código, siempre y cuando no esté bloqueado en una operación de E/S u otra llamada del sistema similar. En la depuración en modo mixto, las expresiones arbitrarias solo se pueden evaluar cuando el código de Python está detenido, tras alcanzar un punto de interrupción o al avanzar paso a paso en el código. Las expresiones solo se pueden evaluar en el subproceso en el que se produjo el punto de interrupción o la operación de ejecución paso a paso.

Cuando el depurador se detiene en código nativo o en código de Python donde no se aplican las condiciones descritas, como después de una operación de paso a paso o en otro subproceso). La evaluación de expresiones se limita a acceder a variables locales y globales en el ámbito del marco seleccionado actualmente, el acceso a sus campos y la indexación de tipos de colección integrados con literales. Por ejemplo, la siguiente expresión se puede evaluar en cualquier contexto (siempre que todos los identificadores hagan referencia a variables y campos existentes de tipos adecuados):

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

El depurador en modo mixto también resuelve estas expresiones de forma diferente. Todas las operaciones de acceso a miembros solo buscan campos que forman parte directamente del objeto (como una entrada en su __dict__ o __slots__, o un campo de una estructura nativa expuesta a Python a través de tp_members) y omiten cualquier lógica de descriptor en __getattr__ o __getattribute__. De forma similar, todas las operaciones de indexación omiten __getitem__y acceden directamente a las estructuras de datos internas de las colecciones.

Por motivos de coherencia, este esquema de resolución de nombres se usa para todas las expresiones que coinciden con las restricciones para la evaluación de expresiones limitadas. Este esquema se aplica independientemente de si se permiten expresiones arbitrarias en el punto de parada actual. Para forzar la semántica adecuada de Python cuando hay disponible un evaluador completo, incluya la expresión entre paréntesis:

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