Um traceback aparece quando um comando de gestão de VPN arranca, e o primeiro instinto corre para instalar outro Python ou apontar o padrão do sistema para outro sítio. Esse instinto mistura dois runtimes que não têm nada a ver um com o outro. Um pertence ao sistema operativo e às suas ferramentas empacotadas; o outro pertence a qualquer projeto que esteja a construir. Mudar o primeiro para satisfazer o segundo é como um ambiente de trabalho funcional perde o gestor de pacotes.

A comparação que vale a pena fazer é entre o componente que falhou de facto e o runtime que tem o direito de mudar. Quase nunca são o mesmo objeto.

Duas perguntas sobre Python que parecem uma

A primeira pergunta é: que interpretador executou o programa que imprimiu o traceback? A segunda é: que interpretador deve o meu projeto usar? Num terminal parecem idênticas, porque ambas se respondem escrevendo python3, e têm donos diferentes, caminhos de atualização diferentes e raios de explosão diferentes.

Uma ferramenta do sistema está ligada ao interpretador com que a distribuição a forneceu, juntamente com os módulos compilados criados contra a ABI desse interpretador. Um projeto está ligado a qualquer ambiente que crie para ele. Mover o padrão do sistema altera o primeiro para todas as ferramentas da máquina ao mesmo tempo; criar um ambiente altera o segundo para uma só pasta.

Só uma dessas ações é reversível num minuto. Não é a primeira.

Que componente produziu o erro

Leia a primeira linha do traceback e o executável que o produziu antes de ler qualquer outra coisa. O frontend de linha de comandos, o serviço privilegiado e as ligações da API são peças separadas, e só algumas delas são Python.

command -v openvpn3
head -n 1 "$(command -v openvpn3)"
dpkg -S "$(command -v openvpn3)" 2>/dev/null
systemctl status openvpn3-service-client --no-pager

A primeira linha de um script nomeia o interpretador que vai usar, e é desse interpretador que veio o traceback. A consulta de pacote diz-lhe quem é dono do ficheiro, o que decide quem pode alterá-lo. O estado do serviço responde a uma pergunta completamente diferente: se o backend privilegiado que de facto estabelece sessões está a correr.

Um erro de importação de módulo, uma recusa de ativação D-Bus e uma credencial rejeitada são três fases diferentes. Um erro de importação acontece antes de existir qualquer sessão, por isso nenhuma configuração de VPN o corrige, e um serviço a funcionar também não o corrige. Classificar a mensagem numa dessas três fases é o verdadeiro primeiro passo.

O que um ambiente de projeto pode e não pode corrigir

Um ambiente de projeto resolve um problema de projeto: as versões exatas das bibliotecas de que uma aplicação precisa, isoladas de todo o resto. Isso é genuinamente útil, e é a ferramenta certa quando o programa que falha é um script seu.

uv run --python 3.11 app.py
python3 -c 'import openvpn3; print(openvpn3.__file__)'

O segundo comando mostra que cópia das ligações um dado interpretador consegue ver, e é a forma mais rápida de distinguir um pacote em falta de um pacote sombreado. Execute-o com o interpretador que o script em falha usa, não com outro que por acaso funciona.

O que um ambiente de projeto não consegue fazer é reparar um frontend empacotado pela distribuição. As dependências desse programa foram criadas para o interpretador que o gestor de pacotes escolheu, e executá-lo dentro do ambiente do seu projeto ou falha ou produz uma mistura que ninguém suporta. Injetar caminhos nele com uma variável de ambiente é o mesmo erro com passos extra: mudou o que a ferramenta carrega sem mudar aquilo contra o que foi testada. Essa troca parece uma correção exatamente durante o tempo que leva a chegar à importação seguinte.

Porque redirecionar o interpretador do sistema é uma armadilha

Mudar o interpretador predefinido é atrativo porque parece corrigir o traceback com um único comando. Também quebra a suposição em que foi criada cada ferramenta Python empacotada no anfitrião, e as falhas aparecem mais tarde e em locais não relacionados.

Os mecanismos de seleção de alternativas foram concebidos para alternar entre implementações equivalentes, não para instalar uma linguagem mais recente por baixo de módulos compilados mais antigos. As extensões compiladas são criadas para uma ABI específica, pelo que um módulo que importava ontem pode hoje não carregar sem que um único pacote tenha sido removido.

Há também uma armadilha de nomenclatura nos conselhos que as pessoas encontram. O comando python sem versão não tem um significado universal entre distribuições e ambientes, e tratá-lo como sinónimo fixo de uma determinada versão principal produz correções que só funcionam na máquina onde foram escritas. Leia a política do sistema que tem à frente, não a que está no artigo do blogue.

Testes de aceitação separados

No fim ficam dois testes, e passar um não diz nada sobre o outro. O primeiro é que o comando de cliente suportado funciona com o interpretador para o qual foi empacotado. O segundo é que o serviço privilegiado estabelece uma sessão e o túnel transporta tráfego.

openvpn3 sessions-list
openvpn3-service-client --help 2>/dev/null | head -n 3
ip -4 addr show tun0

Mantenha um caminho independente para a máquina enquanto trabalha, sobretudo se a VPN transporta o seu único acesso administrativo. Uma alteração a um interpretador do sistema é exatamente o tipo de edição que pode tirar esse acesso a meio.

Se a falha for um módulo em falta para uma ferramenta do sistema, a correção é uma operação de pacote pelo fluxo de trabalho suportado. Se a falha for um conflito de versões no seu próprio projeto, a correção é um ambiente. Escolher entre os dois é toda a decisão, e é decidida por qual programa imprimiu o traceback.

Artigos relacionados