Partilhar via


Depurar Python e C++ em conjunto no Visual Studio

A maioria dos depuradores Python regulares suporta apenas a depuração de código Python, mas é prática comum os programadores usarem Python com C ou C++. Alguns cenários que usam código misto são aplicações que exigem alto desempenho ou a capacidade de invocar diretamente APIs da plataforma que são frequentemente codificadas em Python e C ou C++.

O Visual Studio fornece depuração integrada e simultânea em modo misto para Python e código nativo C/C++. O suporte está disponível quando seleciona a opção de ferramentas nativas de desenvolvimento em Python para a carga de trabalho de Desenvolvimento Python no instalador Visual Studio:

Captura de ecrã que mostra a opção de ferramentas nativas de desenvolvimento em Python selecionada no Visual Studio Installer.

Neste artigo, irá explorar como trabalhar com as seguintes funcionalidades de depuração em modo misto:

  • Pilhas de chamadas combinadas
  • Passo entre Python e código nativo
  • Pontos de interrupção em ambos os tipos de código
  • Veja representações em Python de objetos em frames nativos e vice-versa
  • Depuração no contexto do projeto Python ou do projeto C++

Captura de ecrã que mostra um exemplo de depuração em modo misto para código Python e C++ no Visual Studio.

Pré-requisitos

  • Visual Studio 2017 e posteriores. A depuração em modo misto não está disponível com o Python Tools para Visual Studio 1.x no Visual Studio 2015 e versões anteriores.

  • Visual Studio instalado com suporte para cargas de trabalho em Python. Para obter mais informações, consulte Instalar suporte a Python no Visual Studio.

Ativar a depuração em modo misto num projeto em Python

Os passos seguintes descrevem como ativar a depuração em modo misto num projeto Python:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto Python e selecione Propriedades.

  2. No painel de Propriedades , selecione o separador Debug e depois selecione a opção Debug>Enable native code debuging :

    Captura de ecrã que mostra como definir a propriedade Ativar depuração de código nativo no Visual Studio.

    Esta opção ativa o modo misto para todas as sessões de depuração.

    Sugestão

    Quando você ativa a depuração de código nativo, a janela de saída do Python pode fechar imediatamente após a conclusão do programa sem pausar e mostrando o prompt Pressione qualquer tecla para continuar . Para forçar a pausa e o prompt após ativar a depuração de código nativo, adicione o -i argumento para o campo Executar>Argumentos do Interpretador na guia Depurar. Este argumento coloca o interpretador Python no modo interativo após a execução do código. O programa espera que você selecione Ctrl+Z+Enter para fechar a janela.

  3. Selecione Salvar arquivo> (ou Ctrl+) para salvar as alterações de propriedade.

  4. Para anexar o depurador de modo misto a um processo existente, selecione Depurar>Anexar ao Processo. Abre-se um diálogo.

    1. No diálogo Anexar ao Processo , selecione o processo apropriado da lista.

    2. Para o campo Anexar a, use a opção Selecionar para abrir a caixa de diálogo Selecionar Tipo de Código.

    3. No diálogo Selecionar Tipo de Código , escolha a opção Depurar estes tipos de código .

    4. Na lista, seleciona a caixa de seleção para Python (nativo) e seleciona OK:

      Captura de ecrã que mostra como selecionar o tipo de código Python (nativo) para depuração no Visual Studio.

    5. Selecione Anexar para iniciar o depurador.

    As definições do tipo de código são persistentes. Se quiser desativar a depuração em modo misto e anexar a outro processo mais tarde, desmarque a caixa de seleção do tipo de código Python (nativo) e selecione a caixa de seleção do tipo de código nativo.

    Pode selecionar outros tipos de código além ou em vez da opção Nativa . Por exemplo, se uma aplicação gerida hospeda o CPython, que por sua vez usa módulos de extensão nativos, e quiser depurar os três projetos de código, selecione as caixas de seleção Python, Nativo e Gerido . Esta abordagem proporciona-lhe uma experiência de debugging unificada, incluindo pilhas de chamadas combinadas e avançando entre os três runtimes.

Trabalho com ambientes virtuais

Quando se utiliza este método de depuração em modo misto para ambientes virtuais (venvs), o Python para Windows utiliza um python.exe ficheiro stub para venvs que o Visual Studio encontra e carrega como subprocesso.

  • Para Python 3.8 e posteriores, o modo misto não suporta depuração multiprocesso. Quando inicias a sessão de depuração, o subprocesso do stub é depurado em vez da aplicação. Para cenários de anexação, a solução alternativa é anexar ao ficheiro correto python.exe. Quando inicia a aplicação com depuração (como através do atalho de teclado F5), pode criar o seu venv usando o comando C:\Python310-64\python.exe -m venv venv --symlinks. No comando, insere a sua versão preferida de Python. Por defeito, apenas os administradores podem criar links simbólicos no Windows.

  • Para versões de Python anteriores à 3.8, a depuração em modo misto deverá funcionar como esperado com o venvs.

Correr num ambiente global não causa estes problemas em nenhuma versão do Python.

Instalar símbolos Python

Quando você iniciar a depuração em modo misto pela primeira vez, pode ver um diálogo de Símbolos do Python Requeridos. Precisas de instalar os símbolos apenas uma vez em cada ambiente Python. Os símbolos são automaticamente incluídos se instalar suporte a Python através do Visual Studio Installer (Visual Studio 2017 e versões posteriores). Para mais informações, consulte Instalar símbolos de depuração para interpretadores Python no Visual Studio.

Aceder ao código-fonte Python

Podes disponibilizar o código-fonte do próprio Python padrão durante a depuração.

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

  2. Descarregue o arquivo do código-fonte em Python apropriado para a sua versão e extraia o código para uma pasta.

  3. Quando o Visual Studio solicitar a localização do código-fonte Python, aponte para os ficheiros específicos na pasta de extração.

Ativar a depuração em modo misto num projeto C/C++

Visual Studio 2017 versão 15.5 e posteriores suportam a depuração em modo misto em projetos C/C++. Um exemplo desta utilização é quando se quer incorporar Python noutra aplicação, como descrito no python.org.

Os passos seguintes descrevem como ativar a depuração em modo misto para um projeto C/C++:

  1. No Explorador de Soluções, clique com o botão direito no projeto C/C++ e selecione Propriedades.

  2. No painel de Páginas de Propriedades, selecione o separador Propriedades de Configuração>Configuração e Depuração.

  3. Expanda o menu suspenso da opção Depurador para iniciar e selecione Python/Depuração Nativa.

    Captura de ecrã que mostra como selecionar a opção de Depuração Nativa em Python para um projeto C/C++ no Visual Studio.

    Observação

    Se não vires a opção Python/Nativa de Depuração, precisas primeiro de instalar as ferramentas nativas de desenvolvimento em Python através do Visual Studio Installer. A opção de depuração nativa está disponível no workload de desenvolvimento do Python. Para obter mais informações, consulte Instalar suporte a Python no Visual Studio.

  4. Selecione OK para salvar as alterações.

Depurar o lançador de programas

Quando utilizas este método, não podes depurar o iniciador do programa py.exe porque ele gera um subprocesso filho python.exe. O depurador não se liga ao subprocesso. Neste cenário, a solução alternativa é lançar o python.exe programa diretamente com argumentos, da seguinte forma:

  1. No painel de Páginas de Propriedades do projeto C/C++, vá para a aba Propriedades de Configuração>Depuração.

  2. Para a opção Comando , especifique o caminho completo para o python.exe ficheiro do programa.

  3. Especifique os argumentos desejados no campo Command Arguments .

Anexar o depurador de modo misto

Para o Visual Studio 2017 versão 15.4 e anteriores, a depuração direta em modo misto está ativada apenas ao lançar um projeto Python no Visual Studio. O suporte é limitado porque os projetos C/C++ usam apenas o depurador nativo.

Neste cenário, a solução alternativa é anexar o depurador separadamente:

  1. Inicie o projeto C++ sem depuração selecionando Depuração>Iniciar sem Depuração ou use o atalho de teclado Ctrl+F5.

  2. Para anexar o depurador de modo misto a um processo existente, selecione Depurar>Anexar ao Processo. Abre-se um diálogo.

    1. No diálogo Anexar ao Processo , selecione o processo apropriado da lista.

    2. Para o campo Anexar a, use a opção Selecionar para abrir a caixa de diálogo Selecionar Tipo de Código.

    3. No diálogo Selecionar Tipo de Código , escolha a opção Depurar estes tipos de código .

    4. Na lista, seleciona a caixa de seleção Python e seleciona OK.

    5. Selecione Anexar para iniciar o depurador.

Sugestão

Podes adicionar uma pausa ou atraso na aplicação C++ para garantir que não chama o código Python que queres depurar antes de anexares o depurador.

Explore funcionalidades específicas do modo misto

O Visual Studio oferece várias funcionalidades de depuração em modo misto para facilitar a depuração da sua aplicação:

Use uma pilha de chamadas combinada

A janela Call Stack mostra frames de pilha nativos e de Python intercalados, com transições marcadas entre ambos.

Captura de ecrã da janela combinada da pilha de chamadas com depuração em modo misto no Visual Studio.

  • Para fazer as transições aparecerem como [Código Externo] sem especificar a direção da transição, use o painel de Opções de Ferramentas>. Expanda a secção de Todas as Definições>a Depuração>Geral, selecione a caixa Ativar Apenas o Meu Código.
  • Para que as transições apareçam como [Código Externo] sem especificar a direção da transição, use o diálogo Opções de Ferramentas>. Expanda a secção > Depuração Geral, selecione a caixa de seleção Ativar Apenas o Meu Código e depois selecione OK.
  • Para ativar qualquer frame de chamada, clique duas vezes no frame. Esta ação também abre o código-fonte correspondente, se possível. Se o código-fonte não estiver disponível, o frame continua ativo e as variáveis locais podem ser inspecionadas.

Passo entre Python e código nativo

O Visual Studio fornece os comandos Step Into (F11) ou Step Out (Shift+F11) para permitir que o depurador de modo misto trate corretamente das alterações entre tipos de código.

  • Quando o Python chama um método de um tipo implementado em C, ao entrar numa chamada para esse método, a execução para no início da função nativa que implementa o método.

  • Este mesmo comportamento ocorre quando código nativo chama uma função API Python, o que resulta na invocação de código Python. Entrar numa chamada para PyObject_CallObject para um valor de função originalmente definido em Python interrompe no início da função Python.

  • A transição de Python para nativo também é suportada para funções nativas invocadas a partir de Python via ctypes.

Usar a vista de valores PyObject em código nativo

Quando um frame nativo (C ou C++) está ativo, as suas variáveis locais aparecem na janela Locals do depurador. Nos módulos nativos de extensão Python, muitas destas variáveis são do tipo PyObject (que é um typedef para _object), ou de alguns outros tipos fundamentais de Python. Na depuração em modo misto, estes valores apresentam outro nó filho rotulado [Python view].

  • Para visualizar a representação em Python da variável, expanda o nó. A visão das variáveis é idêntica à que se vê se uma variável local que faz referência ao mesmo objeto estiver presente num frame Python. Os filhos deste nó são editáveis.

    Captura de ecrã que mostra a Vista Python na janela Locais no Visual Studio.

  • Para desativar esta funcionalidade, clique com o botão direito em qualquer lugar na janela Locais e desative a opção do menu Python>Mostrar Python View Nodes:

    Captura de ecrã que mostra como ativar a opção Mostrar Nós de Visualização Python na janela Locais.

Tipos C que mostram nós de exibição Python

Os seguintes tipos de C mostram os nós [Python view], se ativados:

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

[Vista Python] não aparece automaticamente para tipos que crias tu próprio. Quando crias extensões para Python 3.x, esta falta normalmente não é um problema. Qualquer objeto tem, em última análise, um ob_base campo de um dos tipos C listados, o que faz com que [vista Python] apareça.

Visualizar valores nativos em código Python

Podes ativar uma [vista C++] para valores nativos na janela Locais quando um frame Python estiver ativo. Esta funcionalidade não está ativada por predefinição.

  • Para ativar a funcionalidade, clique com o botão direito na janela Locais e ative a opção de menu Python>Show C++ View Nodes.

    Captura de ecrã que mostra como ativar as opções Mostrar Nós de Visualização C++ para a janela Locais.

  • O nó [vista C++] fornece uma representação da estrutura C/C++ subjacente para um valor, idêntica ao que vê num quadro nativo. Mostra uma instância de _longobject (do qual PyLongObject é um typedef) para um inteiro longo do Python, e tenta inferir tipos para classes nativas que você mesmo escreve. Os filhos deste nó são editáveis.

    Captura de ecrã que mostra a Visualização C++ na janela Locais no Visual Studio.

Se um campo filho de um objeto for do tipo PyObject, ou outro tipo suportado, então tem um nó de representação [vista Python] (se essas representações estiverem ativadas). Este comportamento torna possível navegar por grafos de objetos onde os links não estão diretamente expostos ao Python.

Ao contrário dos nós [vista Python], que usam metadados de objetos Python para determinar o tipo do objeto, não existe um mecanismo igualmente fiável para [vista C++]. De um modo geral, dado um valor em Python (isto é, uma PyObject referência), não é possível determinar de forma fiável qual a estrutura C/C++ que o sustenta. O depurador de modo misto tenta determinar o tipo observando vários campos do objeto (como o PyTypeObject referenciado pelo campo ob_type) que têm tipos de ponteiros de função. Se um desses ponteiros de função referenciar uma função que pode ser resolvida, e essa função tiver um self parâmetro com um tipo mais específico do que PyObject*, então assume-se que esse tipo é o tipo de apoio.

Considere o seguinte exemplo, onde o ob_type->tp_init valor de um dado objeto aponta para a seguinte função:

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

Neste caso, o depurador pode deduzir corretamente que o tipo C do objeto é FobObject. Se o depurador não conseguir determinar um tipo mais preciso a partir de tp_init, avança para outros campos. Se não conseguir deduzir o tipo a partir de nenhum desses campos, o nó [vista C++] apresenta o objeto como uma PyObject instância.

Para obter sempre uma representação útil para tipos personalizados, é melhor registar pelo menos uma função especial ao registar o tipo e usar um parâmetro fortemente tipado self . A maioria dos tipos cumpre esse requisito naturalmente. Para outros tipos, a inspeção tp_init é geralmente a entrada mais conveniente para este propósito. Uma implementação fictícia de tp_init para um tipo que está presente apenas para permitir a inferência de tipos de depurador pode simplesmente devolver zero imediatamente, como no exemplo anterior.

Diferenças de revisão em relação à depuração padrão em Python

O depurador de modo misto é distinto do depurador padrão em Python. Introduz algumas funcionalidades extra, mas carecem de algumas capacidades relacionadas com Python, como segue:

  • Funcionalidades não suportadas incluem pontos de interrupção condicionais, a janela Interativa de Depuração e depuração remota multiplataforma.
  • A janela Imediata está disponível, mas com um subconjunto limitado da sua funcionalidade, incluindo todas as limitações listadas nesta secção.
  • As versões suportadas para Python incluem apenas o CPython 2.7 e 3.3+.
  • Para usar Python com o Visual Studio Shell (por exemplo, se o instalar com o instalador integrado), o Visual Studio não consegue abrir projetos em C++. Como resultado, a experiência de edição para ficheiros C++ é a de um editor de texto básico. No entanto, a depuração em C/C++ e a depuração em modo misto são totalmente suportadas no Shell com código-fonte, integração em código nativo e avaliação de expressões em C++ em janelas de depuração.
  • Quando visualiza objetos Python nas janelas das ferramentas de depuração Locais e Observar, o depurador de modo misto mostra apenas a estrutura dos objetos. Não avalia automaticamente propriedades nem mostra atributos calculados. Para coleções, mostra apenas elementos para tipos de coleções incorporados (tuple, list, dict, set). Os tipos de coleções personalizadas não são visualizados como coleções, a menos que sejam herdados de algum tipo de coleção incorporado.
  • A avaliação da expressão é tratada conforme descrito na secção seguinte.

Utilização da avaliação de expressões

O depurador Python padrão permite a avaliação de expressões Python arbitrárias nas janelas Watch e Immediate quando o processo depurado está pausado em qualquer ponto do código, desde que não esteja bloqueado numa operação de I/O ou noutra chamada de sistema semelhante. Na depuração em modo misto, expressões arbitrárias só podem ser avaliadas quando paradas em código Python, após um ponto de interrupção ou ao entrar no código. As expressões só podem ser avaliadas no thread onde ocorreu o ponto de interrupção ou a operação de passos.

Quando o depurador para em código nativo, ou em código Python onde as condições descritas não se aplicam, como após uma operação de step-out, ou numa thread diferente). A avaliação de expressões limita-se ao acesso a variáveis locais e globais no âmbito do quadro atualmente selecionado, ao acesso aos seus campos e à indexação dos tipos de coleções incorporados com literais. Por exemplo, a seguinte expressão pode ser avaliada em qualquer contexto (desde que todos os identificadores se refiram a variáveis e campos existentes de tipos apropriados):

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

O depurador de modo misto também resolve tais expressões de forma diferente. Todas as operações de acesso aos membros procuram apenas campos que fazem parte diretamente do objeto (como uma entrada no seu __dict__ ou __slots__, ou um campo de uma struct nativa exposta a Python via tp_members), e ignoram qualquer lógica __getattr__, __getattribute__ ou descritores. De forma semelhante, todas as operações de indexação ignoram __getitem__, e acedem diretamente às estruturas de dados internas das coleções.

Para uma questão de consistência, este esquema de resolução de nomes é usado para todas as expressões que correspondem às restrições para avaliação de expressões limitadas. Este esquema é aplicado independentemente de serem permitidas expressões arbitrárias no ponto de paragem atual. Para forçar a semântica Python correta quando estiver disponível um avaliador completo, inclua a expressão entre parênteses:

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