Pesquisa de site

Gerenciando pacotes Python da maneira certa


Não seja vítima dos perigos do gerenciamento de pacotes Python.

O Python Package Index (PyPI) indexa uma incrível variedade de bibliotecas e aplicativos cobrindo todos os casos de uso imagináveis. No entanto, quando se trata de instalar e usar esses pacotes, os recém-chegados muitas vezes se deparam com problemas com permissões ausentes, dependências de bibliotecas incompatíveis e instalações que quebram de maneiras surpreendentes.

O Zen do Python afirma: "Deve haver uma - e de preferência apenas uma - maneira óbvia de fazer isso." Certamente nem sempre é esse o caso quando se trata de instalar pacotes Python. No entanto, existem algumas ferramentas e métodos que podem ser considerados práticas recomendadas. Saber disso pode ajudá-lo a escolher a ferramenta certa para a situação certa.

Instalando aplicativos em todo o sistema

pip é o gerenciador de pacotes de fato no mundo Python. Ele pode instalar pacotes de várias fontes, mas PyPI é a principal fonte de pacotes onde é usado. Ao instalar pacotes, o pip irá primeiro resolver as dependências, verificar se elas já estão instaladas no sistema e, caso não estejam, instalá-las. Depois que todas as dependências forem satisfeitas, ele prossegue com a instalação do(s) pacote(s) solicitado(s). Tudo isso acontece globalmente, por padrão, instalando tudo na máquina em um único local, dependente do sistema operacional.

Python 3.7 procura pacotes em um sistema Arch Linux nos seguintes locais:

$ python3.7 -c "import sys; print('\n'.join(sys.path))"

/usr/lib/python37.zip
/usr/lib/python3.7
/usr/lib/python3.7/lib-dynload
/usr/lib/python3.7/site-packages

Um problema com instalações globais é que apenas uma única versão de um pacote pode ser instalada por vez para um determinado interpretador Python. Isso pode causar problemas quando um pacote é uma dependência de diversas bibliotecas ou aplicativos, mas eles exigem versões diferentes dessa dependência. Mesmo que tudo pareça estar funcionando bem, é possível que a atualização da dependência (mesmo acidentalmente durante a instalação de outro pacote) quebre esses aplicativos ou bibliotecas no futuro.

Outro problema potencial é que a maioria das distribuições do tipo Unix gerenciam pacotes Python com o gerenciador de pacotes integrado (dnf, apt, pacman, apt, pacman, >brew e assim por diante), e algumas dessas ferramentas são instaladas em um local não gravável pelo usuário.

$ python3.7 -m pip install pytest
Collecting pytest
Downloading...
[...]
Installing collected packages: atomicwrites, pluggy, py, more-itertools, pytest
Could not install packages due to an EnvironmentError: [Error 13] Permission denied:
'/usr/lib/python3.7/site-packages/site-packages/atomicwrites-x.y.z.dist-info'
Consider using '--user' option or check the permissions.
$

Isso falha porque estamos executando pip install como um usuário não root e não temos permissão de gravação no diretório site-packages.

Você pode tecnicamente contornar isso executando pip como root (usando o comando sudo) ou como usuário administrativo. No entanto, um problema é que acabamos de instalar vários pacotes Python em um local de propriedade do gerenciador de pacotes da distribuição Linux, tornando seu banco de dados interno e a instalação inconsistentes. Isso provavelmente causará problemas sempre que tentarmos instalar, atualizar ou remover qualquer uma dessas dependências usando o gerenciador de pacotes.

Como exemplo, vamos tentar instalar o pytest novamente, mas agora usando o gerenciador de pacotes do meu sistema, pacman:

$ sudo pacman -S community/python-pytest
resolving dependencies...
looking for conflicting packages...
[...]
python-py: /usr/lib/site-packages/py/_pycache_/_metainfo.cpython-37.pyc exists in filesystem
python-py: /usr/lib/site-packages/py/_pycache_/_builtin.cpython-37.pyc exists in filesystem
python-py: /usr/lib/site-packages/py/_pycache_/_error.cpython-37.pyc exists in filesystem

Outro problema potencial é que um sistema operacional pode usar Python para ferramentas de sistema, e podemos facilmente quebrá-los modificando pacotes Python fora do gerenciador de pacotes do sistema. Isso pode resultar em um sistema inoperante, onde a restauração a partir de um backup ou uma reinstalação completa é a única maneira de corrigi-lo.

instalação do sudo pip: uma má ideia

Há outra razão pela qual executar pip install como root é uma má ideia. Para explicar isso, primeiro precisamos ver como as bibliotecas e aplicativos Python são empacotados.

A maioria das bibliotecas e aplicativos Python hoje usam setuptools como sistema de compilação. setuptools requer um arquivo setup.py na raiz do projeto, que descreve os metadados do pacote e pode conter código Python arbitrário para personalizar o processo de construção. Quando um pacote é instalado a partir da distribuição fonte, este arquivo é executado para realizar a instalação e executar tarefas como inspecionar o sistema, construir o pacote, etc.

Executar setup.py com permissões de root significa que podemos efetivamente abrir o sistema para códigos maliciosos ou bugs. Isso é muito mais provável do que você imagina. Por exemplo, em 2017, vários pacotes foram carregados no PyPI com nomes que lembram bibliotecas Python populares. O código carregado coletou informações do sistema e do usuário e as carregou em um servidor remoto. Esses pacotes foram retirados logo em seguida. No entanto, esses tipos de incidentes de "apropriação de erros de digitação" podem acontecer a qualquer momento, pois qualquer pessoa pode fazer upload de pacotes para o PyPI e não há processo de revisão para garantir que o código não cause nenhum dano.

A Python Software Foundation (PSF) anunciou recentemente que patrocinará trabalhos para melhorar a segurança do PyPI. Isso deve tornar mais difícil a realização de ataques como o "pytosquatting" e, esperançosamente, tornar isso menos problemático no futuro.

Deixando de lado as questões de segurança, sudo pip install não resolverá todos os problemas de dependência: você ainda pode instalar apenas uma única versão de qualquer biblioteca, o que significa que ainda é fácil quebrar aplicativos dessa maneira.

Vejamos algumas alternativas melhores.

Gerenciadores de pacotes de sistema operacional

É muito provável que o gerenciador de pacotes "nativo" que usamos em nosso sistema operacional preferido também possa instalar pacotes Python. A questão é: devemos usar pip, ou apt, dnf, pacman e assim por diante?

A resposta é: depende.

pip geralmente é usado para instalar pacotes diretamente do PyPI, e os autores de pacotes Python geralmente carregam seus pacotes lá. No entanto, a maioria dos mantenedores de pacotes não usará PyPI, mas em vez disso pegará o código-fonte da distribuição fonte (sdist) criada pelo autor ou de um sistema de controle de versão (por exemplo, GitHub), aplicará patches se necessário, e testar e lançar o pacote para suas respectivas plataformas. Comparado ao modelo de distribuição PyPI, isso tem prós e contras:

  • O software mantido por gerenciadores de pacotes nativos é geralmente mais estável e geralmente funciona melhor em uma determinada plataforma (embora nem sempre seja esse o caso).
  • Isso também significa que é necessário um trabalho extra para empacotar e testar o código Python upstream:

    1. A seleção de pacotes geralmente é muito menor do que a que o PyPI oferece.
    2. As atualizações são mais lentas e os gerenciadores de pacotes geralmente enviam versões muito mais antigas.

Se o pacote que queremos usar estiver disponível e não nos importarmos com versões um pouco mais antigas, o gerenciador de pacotes oferece uma maneira conveniente e segura de instalar pacotes Python. E, como esses pacotes são instalados em todo o sistema, eles ficam disponíveis para todos os usuários do sistema. Isso também significa que só podemos usá-los se tivermos as permissões necessárias para instalar pacotes no sistema.

Se quisermos usar algo que não está disponível na seleção do gerenciador de pacotes ou é muito antigo, ou simplesmente não temos as permissões necessárias para instalar pacotes, podemos usar pip.

Instalações de esquema de usuário

pip suporta o modo "esquema de usuário" introduzido no Python 2.6. Isso permite que os pacotes sejam instalados em um local de propriedade do usuário. No Linux, normalmente é ~/.local. Colocar ~/.local/bin/ em nosso PATH tornará possível ter ferramentas e scripts Python disponíveis ao nosso alcance e gerenciá-los sem privilégios de root.

$ python3.7 -m pip install --user black
Collecting black
 Using cached
[...]
Installing collected packages: click, toml, black
 The scripts black and blackd are installed in '/home/tux/.local/bin' which is not on PATH.
 Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed black-x.y click-x.y toml-x.y.z
$

No entanto, esta solução não resolve o problema se e quando precisarmos de versões diferentes do mesmo pacote.

Entre em ambientes virtuais

Os ambientes virtuais oferecem instalações isoladas de pacotes Python que podem coexistir de forma independente no mesmo sistema. Isso oferece os mesmos benefícios que as instalações de esquema de usuário, mas também permite a criação de instalações independentes do Python, nas quais um aplicativo não compartilha dependências com nenhum outro aplicativo. Virtualenv cria um diretório que contém uma instalação independente do Python, incluindo o binário do Python e ferramentas essenciais para gerenciamento de pacotes: setuptools, pip e roda.

Criando ambientes virtuais

virtualenv é um pacote de terceiros, mas o Python 3.3 adicionou o pacote venv à biblioteca padrão. Como resultado, não precisamos instalar nada para usar ambientes virtuais em versões modernas do Python. Podemos simplesmente usar python3.7 -m venv para criar um novo ambiente virtual.

Depois de criar um novo ambiente virtual, devemos ativá-lo fornecendo o script activate no diretório bin do ambiente recém-criado. O script de ativação cria um novo subshell e adiciona o diretório bin à variável de ambiente PATH, permitindo-nos executar binários e scripts a partir deste local. Isso significa que este subshell usará python, pip ou qualquer outra ferramenta instalada neste local em vez daquelas instaladas globalmente no sistema.

$ python3.7 -m venv test-env
$ . ./test-env/bin/activate
(test-env) $

Depois disso, qualquer comando que executarmos utilizará a instalação do Python dentro do ambiente virtual. Vamos instalar alguns pacotes.

(test-env)$ python3.7 -m pip install --user black
Collecting black
 Using cached
[...]
Installing collected packages: click, toml, black
Successfully installed black-x.y click-x.y toml-x.y.z
(test-env) $

Podemos usar black dentro do ambiente virtual sem nenhuma alteração manual nas variáveis de ambiente como PATH ou PYTHONPATH.

(test-env) $ black --version
black, version x.y
(test-env) $ which black
/home/tux/test-env/bin/black
(test-env) $

Quando terminarmos o ambiente virtual, podemos simplesmente desativá-lo com a função desativar.

(test-env) $ deactivate
$ 

Ambientes virtuais também podem ser usados sem o script de ativação. Scripts instalados em um venv terão sua linha shebang reescrita para usar o interpretador Python dentro do ambiente virtual. Dessa forma, podemos executar o script de qualquer lugar do sistema usando o caminho completo do script.

(test-env) $ head /home/tux/test-env/bin/black
#!/home/tux/test-env/bin/python3.7

# -*- coding: utf-8 -*-
import re
import sys

from black import main

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
(test-env) $ 

Podemos simplesmente executar ~/test-env/bin/black de qualquer lugar do sistema e tudo funcionará perfeitamente.

Pode ser útil adicionar certos ambientes virtuais comumente usados à variável de ambiente PATH para que possamos usar os scripts neles de forma rápida e fácil, sem digitar o caminho completo:

export PATH=$PATH:~/test-env/bin

Agora, quando executarmos black, ele será retirado do ambiente virtual (a menos que apareça em algum outro lugar anteriormente no PATH). Adicione esta linha ao arquivo de inicialização do seu shell (por exemplo, ~/.bashrc) para que ela seja configurada automaticamente em todos os novos shells.

Ambientes virtuais são muito comumente usados para desenvolvimento em Python porque cada projeto obtém seu próprio ambiente onde todas as dependências da biblioteca podem ser instaladas sem interferir na instalação do sistema.

Recomendo verificar o projeto virtualenvwrapper, que pode ajudar a simplificar fluxos de trabalho comuns baseados em virtualenv.

E a Conda?

Conda é uma ferramenta de gerenciamento de pacotes que pode instalar pacotes fornecidos pelo Anaconda no repositório repo.continuum.io. Tornou-se muito popular, especialmente para ciência de dados. Ele oferece uma maneira fácil de criar e gerenciar ambientes e instalar pacotes neles. Uma desvantagem comparado ao pip é que a seleção de pacotes é muito menor.

Uma receita para um gerenciamento de pacotes bem-sucedido

  • Nunca execute sudo pip install.
  • Se você deseja disponibilizar um pacote para todos os usuários da máquina, você tem as permissões corretas e o pacote está disponível, então use o gerenciador de pacotes da sua distribuição (apt, yum , pacman, brew, etc.).
  • Se você não tiver permissões de root ou o gerenciador de pacotes do SO não tiver o pacote que você precisa, use pip install --user e adicione o diretório de instalação do usuário ao PATH > variável de ambiente.
  • Se você deseja que várias versões da mesma biblioteca coexistam, para fazer desenvolvimento em Python ou apenas para isolar dependências por qualquer outro motivo, use ambientes virtuais.

Este artigo foi publicado originalmente em abril de 2019 e atualizado pelo editor.