Gerenciamento de ambientes e pacotes no Python

Leonardo Gregianin
7 min readDec 29, 2021

--

Diferente de outras linguagens de programação como Go (mod), Rust (Cargo), Elixir (Hex), Ruby (Bundler), PHP (Composer), Clojure (Lein), Nodejs (Npm) e outras tantas que tem seus sistemas de gerenciamento de pacotes e dependências “padronizados” ou nativos na linguagem, Python não tem um único “padrão”, longe disso: distutils, setuptools, easy_install, PyPI, twine, wheel, pip, requiments.txt, virtualenv, pipx, pipenv, Pipfile, pip-tools, flit, pdm, poetry, conda são algumas das ferramentas (e tem mais) que temos no ecossistema de gerenciamento de ambientes e pacotes no Python.

Como funciona o sistema de módulos

O módulo nativo sys fornece acesso a variáveis e funções ​​que interagem com o interpretador. O sys.path é uma variável dentro do módulo sysque contém uma lista de diretórios que o interpretador irá buscar quando algum módulo por requerido nos imports.

Exemplo do que temos dentro do sys.path:

Vou demonstrar dentro do projeto httpx para explicar o que cada índice da lista representa:

O que está demonstrado à direita da tela é o arquivo _main.pye ao lado esquerdo está a árvore de diretórios do projeto.

Portanto, _main.py faz diversos imports, de arquivos locais, de pasta diferentes dentro do projeto, de módulos nativos do Python como functools, json e sys e de outros projetos como click, httpcore e pygments.

(Index 0) O primeiro índice é uma string em branco (‘ ’) e quer dizer que o Python irá buscar o arquivo do import no mesmo diretório que está sendo executado.

No exemplo _main.py está importando a classe Client do arquivo _client.py que está no mesmo diretório, por isso, utiliza-se o ponto (.), se estivesse em outra pasta, o importe seria: from other._client import Client.

(Index 1) O segundo índice do sys.path busca na biblioteca padrão (Stardard Library) de instalação, ou seja, irá buscar os módulos e funções nativas.

No mesmo exemplo, irá encontrar functools, json, sys e typing nesse diretório porque são funções nativas da linguagem.

(Index 2) O terceiro índice buscará na pasta do usuário, cada usuário tem o seu system path e é nele onde o usuário instala os programas para não causar conflitos com programas instalados por outros usuários do sistema operacional.

E por fim, se não encontrar o módulo nos diretórios anteriores, buscará no (Index 3) diretório de bibliotecas do sistema, onde, geralmente, se encontra a maioria dos programas feitos em Python que o sistema operacional utiliza.

Caso não encontre nesses diretórios de exemplo, o Python mostrará uma exceção dizendo que não encontrou o módulo.

É possível adicionar outros diretórios no sys.path, é possível controlar os diretórios com variáveis de ambiente ou exportar o PYTHONPATH. Modifique, somente se souber exatamente o que está fazendo!

O que precisamos

Precisamos que uma única solução, simples, rápida e eficiente, que faça:

  • Faça o download de um projeto no PyPI.
  • Instale o projeto e todas as suas dependências, sem que ocorra problemas de conflitos de versões das dependências.
  • Tenha a verificação de hashes de cada pacote.
  • Tenha um arquivo de configuração padronizado.
  • Fazer build e upload de um pacote para o PyPI.
  • Seja multiplataforma.
  • Seja sustentável financeiramente. Sugestão: Financiada pela Python Software Foundation (PSF) (veja os patrocinadores da PSF aqui).

Evolução de 1991 a 2021

A linguagem tem seu primeiro release lançado oficialmente em 1991, e a primeira versão de uma ferramenta para distribuição de pacotes foi a distutils, lançada em setembro de 2000 na versão 1.6. O Distutils permite que você estruture seu projeto para que tenha um setup.pye, com isso, facilita a instalação e distribuição do projeto.

Nesse meio tempo, se quisesse instalar o código de outra pessoa, teria que baixar, instalar e adicioná-lo ao PYTHONPATH manualmente. Em 2003 foi criado, o então cheeseshop e agora, PyPI (Python Package Index), que é um catálogo/repositório online de programas para qualquer pessoa baixar e instalar.

Com um repositório centralizado e milhares de projetos, muitos projetos começaram a ter mais e mais dependências e gerenciá-los era um novo desafio por haver a possibilidade de incompatibilidades e conflitos de versões.

Em 2004 e nos anos seguintes, a comunidade começou a desenvolver um conjunto de ferramentas para extender o distutils para gerenciamento de dependências complexas, reconhecimento automatizado de precedência de versão e uma ferramenta de instalação automática chamada easy_install. Embora nunca tenha chegado à biblioteca padrão, setuptoolstornou-se o meio principal para criar e distribuir pacotes.

Mesmo com todas as ferramentas úteis feitas com o setuptools e easy_install, o problemas de compartilhamento de pacotes e o conflitos de versões ainda ocorriam. Em 2006 começou a ser discutido a possibilidade de construção de ambientes virtuais e em 2007 foi lançado o virtualenv que permitia construir ambientes isolados (ou virtuais) de um projeto a partir de uma instalação de sistema central de Python, com isso, o easy_install instala as dependências do projeto somente naquele ambiente isolado.

Em 2008, o mesmo criador do virtualenv, em alternativa e correções ao easy_install, cria o pip para fácil instalação de pacotes e dependências. O pip utiliza o arquivo requirements.txt para gerenciar as dependências do pacotes.

Em 2011, A Python Software Foundation cria um grupo de trabalho chamado Python Packaging Authority (PyPA) responsável por padronizar o meio de distribuição de pacotes, manter o pip e o virtualenv e também, por manter a infraestrutura do PyPI. A linha do tempo da história do PyPA encontra-se aqui.

Em 2012, a empresa Anaconda Inc. cria o gerenciador de pacote chamado Conda, voltado para a comunidade científica de desenvolvimento, criando uma infraestrutura totalmente separada do pip, virtualenv e PyPI.

Também em 2012, foi criado o pip-tools que é um conjunto de ferramentas para gerenciar e manter compilações determinísticas especificando as dependências por meio de hash ( — require-hashes que o pip implementou em 2016).

Em 2013, com a PEP 427, define-se que o formato binário padrão para distribuição dos pacotes é o formato wheel e é criado o twine para interagir e realizar upload seguros de pacotes para o PyPI.

Em 2014, a PEP 440 descreve como identificar versões de pacotes com um novo schema de versionamento.

Em 2015, é aprovadas a PEP 503 e PEP 508 que descrevem padrões para pacotes e para distribuição no PyPI.

Ainda em 2015, começa o desenvolvimento do flit, que pretendia ser o substituto de todo o ecossistema.

Em 2016 a PEP 518 especifica um novo arquivo de configuração de pacotes chamado pyproject.toml que centraliza e padroniza todos os arquivos de configuração em um só.

Ainda em 2016, o pip lança uma nova versão com a implementação do hashes para cada dependência dentro do requirements.txt.

Em 2017 a PEP 517 aprova builds independentes. Devido problemas antigos do distutils, e a tentativa de se fazer um distutils2, abrindo a possibilidade de não necessariamente utilizar distutils, ou seja, se você quiser usar distutils, ótimo; se você quiser usar outra coisa, isso deve ser fácil de fazer usando métodos padronizados.

Ainda em 2017 é lançado o pipx, ferramenta para instalar e rodar programas sem causar conflitos de dependências com outros pacotes instalados no sistema.

Estamos em 2017, ou seja, 26 anos desde a criação da linguagem e ainda não temos uma solução única e completa para gerenciar e distribuir pacotes.

De acordo com a sátira do xkcd, como está nosso ambiente:

source: xkcd

Pipenv nasce ainda em 2017 como um projeto que visa agregar o pip, virtualenv e o Pipfile em um único conjunto de ferramentas para gerenciar ambientes, dependências e pacotes, cria e verifica hashes de arquivo para garantir a conformidade com dependências com os arquivos Pipfile e Pipfile.lock.

Em 2018 nasce o Poetry com a proposta de substituir todas as ferramentas já existentes de empacotamento e distribuição existentes (mesmo que, por debaixo dos panos, utiliza o pip e o virtualenv para instalar e criar ambientes isolados), ou seja, supre a necessidade do distutils, setuptools, virtualenv, pip, faz builds determinísticos igual o pip-tools e pip faz, segue o padrão da PEP 440 sobre semântica de versões, segue os padrão do PyPI da PEP 503 e PEP 508, utiliza o padrão do pyproject.toml da PEP 518 e cria builds independentes da PEP 517.

Portanto, o Poetry (em 2021), é o único projeto que abrange todos as ferramentas do ecossistema e segue todos os padrões definidos nas PEPs.

Em 2020, é lançada uma nova versão do pip, com um novo resolvedor de dependências.

Ainda em 2020, é criado o projeto pdm, que atende a PEP 582, ou seja, não há de se criar ambientes virtuais, cria-se um diretório local de pacotes python para cada projeto chamado __pypackages__ para incluir todos lá dentro. Seguindo a mesma ideia do node_modules do Nodejs.

Em 2021, ainda não temos uma ferramenta oficial, única e completa para todo o ecossistema (o poetry está perto disso) e que atenda todos os requisitos que escrevi no tópico logo acima chamado “O que precisamos”.

Conclusão

A organização PyPA no Github mantém a maioria dos projetos de empacotamento e distribuição, na minha humilde opinião, são vários projetos muito parecidos e para um mesmo objetivo final que são propostos, não vale tal esforço humano e financeiro. Poderíamos aprender com as linguagens citadas na introdução.

--

--