Admito que no começo fui cético em relação ao Pipfile. Pode ser a idade, mas questionei a real necessidade do mesmo. Para mim era muito óbvio! Eu já tinha o pip, o virtualenv e até mesmo o pyenv. Hoje, após ler alguns artigos e debater com alguns colegas, fico até envergonhado de não ter visto o potencial da ferramenta no seu primeiro momento.
Se você está na dúvida, assim como eu estive, vem comigo que te mostro as principais vantagens do Pipfile (e do Pipenv).
Esse foi o meu "turning point" em relação ao Pipfile. E foi o @rcmachado quem respondeu essa pergunta:
O build não é determinístico.
Vamos supor que estamos desenvolvendo um projeto em Django. Instalamos via pip install Django
, e jogamos a dependência no requirements.txt
:
# requirements.txt
Django
Como você garante que a mesma versão do Django está instalada nos diferentes ambientes do projeto (dev, qa, staging, production, etc)? Da forma acima, você não garante. O pip
instalará a última versão do pacote disponível no PyPI.
Para minimizar esse problema, podemos "pinar" a versão do Django:
# requirements.txt
Django==2.0.6
Agora garantimos a versão do framework em todos os ambientes do projeto. Sabemos que o Django tem uma dependência, o pytz, o que acontece com ela?
Bem... se o framework estabelecer exatamente qual versão da dependência ele utiliza, não será um problema. Mas nem sempre isso acontece:
# django/setup.py
(...)
setup(
(...)
install_requires=['pytz'],
(...)
)
(...)
Para garantir que a mesma versão do pytz está sendo instalada nos diferentes ambientes do projeto, podemos "pinar" a sua versão no requirements.txt
:
# requirements.txt
Django==2.0.6
pytz==2018.4
E por fim, adicionar um --no-deps
ao comando pip
, assim ele se manterá fiel às dependências descritas no requirements.txt
:
$ pip install -r requirements.txt --no-deps
Note que você virou responsável por uma função que deveria ser desempenhada pelo package manager. Com o passar do tempo, esse tipo de gestão passa a ser árdua e suscetível a erros.
Segundo o repositório oficial, o Pipfile é:
(...) a replacement for the existing standard pip's requirements.txt file.
Como esperado, o padrão traz melhorias em relação ao requirements.txt
. O já citado "ambientes determinísticos" é alcançado através do Pipfile.lock
, um conceito usado em gerenciadores de pacotes de outras linguagens, como Bundler, Cargo ou Yarn, onde as versões das dependências são "congeladas", garantindo que você obtenha o mesmo resultado em qualquer ambiente, e executando quantas vezes quiser.
Outra vantagem é o uso de um único arquivo para diferentes ambientes. Com o uso de requirements, geralmente acabamos com um arquivo de texto específico para cada ambiente:
# requirements.txt
Django
Mais um para dependências exclusivas do ambiente de desenvolvimento:
# requirements-dev.txt
-r requirements.txt
django-debug-toolbar
pytest
Com o Pipfile, é possível utilizar o mesmo arquivo:
# Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
pytest = "*"
[packages]
Django = "*"
[requires]
python_version = "3.6"
Grande parte dos artigos que lí sobre Pipfile começavam com algum comando como o abaixo:
$ pipenv install django
Uma coisa que tem que ficar clara é: O Pipenv está para o pip, assim como o Pipfile está para o requirements.
Até o momento da publicação desse artigo, o pip
não suporta o formato do Pipfile (ou pelo menos a versão que tenho instalada aqui). Portanto, a maneira mais conveniente de utilizar o Pipfile hoje é através do utilitário de linha de comando pipenv
.
Segundo o repositório oficial, o Pipenv é:
(...) a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world. Windows is a first–class citizen, in our world.
Através do parsing do Pipfile, a ferramenta resolve dependências do projeto (através dos parâmetros install
, uninstall
ou update
), exibe a árvore de dependências (através do parâmetro graph
) e analisa as mesmas (através do check
). Mas além de atuar como um gerenciador de pacotes, o Pipenv tem uma funcionalidade fantástica: Cria e gerencia virtualenvs de forma automática.
Já escrevi sobre virtualização com virtualenv e pyenv. Com o Pipenv, a coisa fica significativamente mais simples.
Ao executar o comando pipenv install
, por exemplo, a ferramenta automaticamente criará um virtualenv (caso não exista) para o projeto no qual você está trabalhando. Por exemplo:
$ mkdir -p ~/workspace/test-pipenv
$ cd ~/workspace/test-pipenv/
$ pipenv install django
Creating a virtualenv for this project…
Using /home/klaus/.pyenv/versions/2.7.14/bin/python2.7 (2.7.14) to create virtualenv…
Caso você esteja curioso para saber aonde o seu virtualenv foi parar, o comando abaixo pode ajudar:
$ pipenv --venv
/home/klaus/.local/share/virtualenvs/test-pipenv-BzNa0eOE
Mas você não precisa se preocupar em saber onde o activate.sh
está. Para ativar o virtualenv criado, o parâmetro shell
é a melhor saída:
$ pipenv shell
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
. /home/klaus/.local/share/virtualenvs/test-pipenv-BzNa0eOE/bin/activate
Frustrado com o Python 2.7? Sem problemas! Vamos transformar o projeto em Python 3:
$ pipenv install --three
O comando criará um novo virtualenv, dessa vez com a versão mais recente da linguagem. Não esqueça de atualizar o python_version
do Pipfile
.
Com o Heroku adotando Pipfile, me senti mais motivado a migrar alguns projetos para o padrão.
Como resultado, tenho simplificado a stack dos mesmos. Antes, eu gerenciava os ambientes virtuais com um mix de virtualenvwrapper e pyenv. Hoje, com apenas um utilitário de linha de comando, sou capaz de gerenciar dependências e virtualização. Além disso, acredito que o setup de um projeto Python fique mais homogêneo, uma vez que não há necessidade de um combo pip + {virtualenv,virtualenvwrapper,pyenv}
.
Até a próxima.