Criando um ambiente virtual para seu projeto Python

python-logo

Nesse artigo veremos como usar ambientes virtuais no gerenciamento de projetos Python. Veremos que é possível ter diferentes versões de Python, uma para cada projeto, e também como é feito o controle de pacotes e dependências.

Caso prefira ver em vídeo, clique no player abaixo. O artigo completo se encontra após o vídeo.

Por que utilizar ambientes virtuais?

Assim como outras linguagens de programação, o Python tem sua própria forma de baixar, armazenar e gerenciar pacotes e módulos. De interesse particular no nosso caso é entender como e onde são armazenados esses pacotes.

Boa parte dos sistemas derivados Unix, como o Mac OS e as distribuições Linux mais comuns, já vêm com alguma versão Python pré-instalada visto que há ferramentas do sistema que dependem da linguagem. Como sou usuário de Linux, tendo no momento instalado o Linux Mint 20, os exemplos aqui descritos serão dessa distribuição mas podem ser facilmente replicados em outras e em Mac. O Windows, como não possui ferramentas de sistema que dependam de Python, não costuma vir alguma versão pré-instalada mas é bem fácil instalar conforme se pode verificar com uma simples busca na internet.

Conforme descrito anteriormente, distribuições Linux possuem ferramentas que dependem de Python. Muito embora a versão 3 da linguagem tenha sido lançada em 2008, muitos projetos ainda utilizam alguma micro versão de Python 2, muitas vezes por questões de retrocompatibilidade. A última micro versão de Python 2 foi a 2.7.18, lançada em julho de 2010, e cujo suporte foi encerrado no início de 2020. Inclusive há diversas discussões interessantes sobre esse longo tempo de suporte para uma versão tão antiga, recomendo começar por aqui se tiver interesse no assunto. E caso não esteja entendendo muito bem a forma como número de versões são representados em Python, leia aqui.

É fácil perceber se as duas versões estão presentes em seu sistema Linux. No terminal, digite which python e depois which python3. Os caminhos que retornam são, os caminhos onde se encontram os interpretadores Python 2 e 3, respectivamente.

$ which python3
/usr/bin/python3

No caso de distribuições baseadas no Ubuntu 20.04, como é o caso do Linux Mint 20 que utilizo, o Python 2 foi oficialmente retirado dos repositórios, mas ainda pode ser acessado pelo comando python2 (e não apenas python como costume nas distribuições anteriores).

Por padrão, o shell que se abre ao digitar o comando python no terminal em boa parte das distribuições é da versão 2 da linguagem. Para abrir um shell de Python 3 basta utilizar o comando python3.

Boa parte dos pacotes de sistema são armazenados em pastas dentro de uma pasta de sistema. Para identificar tal pasta, podemos abrir um shell Python no terminal e inserir os seguintes comandos:

>>> import sys
>>> sys.prefix

Usualmente o resultado será a pasta de sistema /usr. Tal pasta apresenta a seguinte estrutura, possuindo pastas para executáveis, bibliotecas e códigos-fonte.

$ tree /usr -L
/usr
├── bin
├── games
├── include
├── lib
├── libexec
├── local
├── sabin
├── share
└── src

Você pode ler a documentação do comando sys.prefix aqui.

Pacotes de terceiros, usualmente instalados com o gerenciador pip, serão instalados em uma das pastas fornecidas pelos comandos:

>>> import site
>>> site.getsitepackages()
['/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.8/dist-packages']

Documentação do pacote site.

Repare que são pastas de sistema, todos os projetos usarão essas mesmas pastas. Vamos considerar o seguinte cenário para facilitar a compreensão de o porquê isso é problemático.

Considere que você tem dois projetos: Projeto A e Projeto B, ambos dependem da biblioteca Pandas, mas o Projeto A é mais antigo e foi estruturado em cima da versão 0.25.3 enquanto que o Projeto B é recém nascido e utiliza a versão 1.1.0, que não é perfeitamente retrocompatível com a 0.25.3, de forma que não é possível atualizar o Projeto A sem ter que reescrever alguns trechos de código, o que não é viável no contexto do projeto para entregá-lo no prazo. Da forma como o sistema está no momento, uma mesma versão do Pandas será instalada para todo o sistema em uma pasta pandas dentro da pasta retornada pelos comandos anteriormente citados, impossibilitando trabalhar em ambos concomitantemente.

Aqui então entra em cena os ambientes virtuais.

O que é um ambiente virtual?

Em essência, ambientes virtuais permitem criar um ambiente isolado para cada projeto Python. Assim, cada projeto pode ter suas próprias dependências, independentemente das dependências de outros projetos.

No nosso exemplo, basta criar um ambiente para o Projeto A, no qual se instalará a versão 0.25.3 do Pandas, e outro ambiente para o Projeto B, no qual se instalará a versão 1.1.0.

Não há limites para quantos ambientes se podem criar, a não ser obviamente o limite de espaço físico no HD, já que são “apenas diretórios”. Vamos ver as principais ferramentas disponíveis para criação e gerenciamento de ambientes virtuais.

venv

A primeira ferramenta que veremos para criar ambientes virtuais é o venv. Ele faz parte da biblioteca padrão do Python 3, de forma que não é necessário instalar. Nesse artigo, focaremos apenas em Python 3, tendo em vista a descontinuidade de suporte para Python 2.

Primeiro, precisamos ter uma pasta para cada projeto:

$ mkdir projetos
$ mkdir projetos/projeto_{A,B}

Estrutura de um ambiente venv

Agora, dentro de cada pasta, criaremos um ambiente virtual que, num primeiro momento, entenderemos como uma simples pasta dentro do projeto. É comum utilizar como nome dessa pasta env ou .env (lembre que ponto no início é indicação de pasta oculta). Pode-se utilizar qualquer nome, mas manteremos a convenção aqui utilizando .env. Assim, dentro da pasta de cada projeto, executaremos o comando

$ python3 -m venv .env

Repare que o comando já deixa explícito que o interpretador desses ambientes será o interpretador de Python 3. Vejamos a estrutura desses diretórios .env criados em cada projeto:

$ tree .env -L 3
.env
├── bin
    ├── activate
    ├── easy_install
    ├── pip
    ├── python -> python3
    ├── python3 -> /usr/bin/python3
├── include
├── lib
    ├── python3.8
        ├── site-packages
├── lib64 -> lib
├── pyvenv.cfg
└── share
    └── python-wheels

Dentre essas pastas, podemos destacar:

  • bin: arquivos que interagem com o ambiente virtual;
  • include: uma cópia da versão Python juntamente com a pasta site-packages onde cada dependência será instalada.

Também há cópias e links simbólicos (symlinks) para algumas ferramentas e os executáveis dos interpretadores. Tudo para garantir que os comandos e códigos Python sejam executados dentro do contexto do ambiente atual, garantindo o isolamento. Ainda veremos mais detalhes.

Ainda na pasta bin temos também scripts activate que, como o nome indica, são utilizados para efetivamente ativar o ambiente de forma que o shell utilize o executável do Python e a pasta site-packages do ambiente e não do sistema.

Ativando um ambiente venv

Para ativar o ambiente, dentro da pasta do projeto, faça

$ source .env/bin/activate
(.env) $

Repare que agora o prompt apresenta o nome do ambiente, .env, como prefixo, indicando que o mesmo está ativo.

Entendendo o funcionamento do venv

Agora ao executarmos o comando which python o caminho que aparece é referente ao ambiente virtual e não mais ao sistema (compare com o output do início do artigo). Então, estando na pasta do nosso Projeto A:

$ which python
/home/chico/projetos/projeto_A/.env/bin/python

Vamos agora desativar o ambiente virtual. Para isso basta apenas o seguinte comando

$ deactivate

Com o ambiente desativado, vamos observar a variável de ambiente $PATH. Lembrando, o $PATH indica ao shell quais diretórios procurar por executáveis em resposta a comandos do usuário. Ativemos novamente o ambiente para observar como o $PATH se alterou:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

(.env) $ echo $PATH
/home/chico/projetos/projeto_A/.env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Repare que, com o ambiente ativado, o diretório bin do ambiente é o primeiro caminho do $PATH. Na prática isso significa que será o primeiro diretório onde o sistema irá procurar por executáveis, de forma que o shell usará a instância Python do ambiente e não a disponível a todo o sistema.

Resolvendo nosso cenário

No nosso cenário, temos dois projetos que dependem do pacote Pandas, mas de versões diferentes de tais pacote. Na pasta do Projeto A, após ativar o ambiente, precisamos executar o seguinte comando para instalar a versão específica do pacote em questão:

(.env) $ pip install pandas==0.25.3

O comando instalará o pacote Pandas na versão solicitada e as dependências necessárias como, por exemplo, o pacote Numpy. Após a instalação, podemos verificar os pacotes no ambiente com o comando

(.env) $ pip list
Package           Version
----------------- ----------
numpy             1.19.1
pandas            0.25.3
pip               20.0.2
pkg-resources     0.0.0
python-dateutil   2.8.1
pytz              2020.1
setuptools        44.0.0
six               1.15.0

Vamos agora desativar o ambiente do Projeto A e ir ao diretório do Projeto B ativando o ambiente deste último projeto. No caso desse projeto, por ter recém começado, se deseja começar com a versão Pandas mais recente de forma que não é necessário especificar a versão, será instalada a mais recente que, momento que escrevo é a 1.1.0.

(.env) $ pip install pandas

Executando novamente o pip list vemos que foi realmente instalada a versão mais recente. Assim, os dois projetos estão isolados e com as versões necessárias.

(.env) $ pip list
Package           Version
----------------- ----------
numpy             1.19.1
pandas            1.1.0
pip               20.0.2
pkg-resources     0.0.0
python-dateutil   2.8.1
pytz              2020.1
setuptools        44.0.0
six               1.15.0

Compartilhando um ambiente venv

Em um cenário ainda mais real, os projetos teriam ainda mais dependências e certamente instalá-las individualmente, ainda mais quando se tem um projeto já estruturado como o caso do Projeto A, é improdutivo. Assim, é bastante comum em projetos onde se usa venv a existência de um arquivo requirements.txt. Para gerar o arquivo a partir de um ambiente já estruturado:

(.env) $ pip freeze > requirements.txt

No nosso simplificado caso do Projeto A, eis a estrutura do arquivo:

(.env) $ cat requirements.txt
numpy==1.19.1
pandas==0.25.3
python-dateutil==2.8.1
pytz==2020.1
six==1.15.0

É uma estrutura bem simples, mostrando as dependências do projeto e suas versões.

Para criar um ambiente a partir do arquivo, cria-se uma pasta para o projeto com um ambiente conforme as instruções passadas anteriormente, copia-se o arquivo requirements.txt para a pasta, ativa-se o ambiente e então se executa o comando:

(.env) $ pip install -r requirements.txt

Assim, todos os colaboradores do projeto podem ter o mesmo ambiente de desenvolvimento, de forma rápida e fácil.

Para o ambiente aparecer no Jupyter Notebook

Se projeto utilizar Jupyter Notebook, para que o interpretador e os pacotes do ambiente sejam carregados é preciso instalar e rodar no ambiente:

(.env) $ pip install ipykernel
(.env) $ ipython kernel install --user --name=nome_projeto

Removendo o ambiente

Como um ambiente criado com o venv se estrutura ao redor da pasta do ambiente virtual, para excluir o ambiente basta desativá-lo e remover a pasta:

(.env) $ deactivate
$ rm -r .env

conda

Muitos começam na linguagem Python através de distribuições como a Anaconda, que visam simplificar o gerenciamento e desenvolvimento de projetos em Python, especialmente os voltados para a área científica. Assim, aqueles que começam por esse caminho acabam por ter contato com o gerenciador conda, que, inclusive, serve para qualquer linguagem, não apenas para Python. O conda permite também gerenciar ambientes virtuais.

Essas últimas frases já ressaltam importantes distinções entre o conda e pip, que muitos iniciantes consideram erroneamente ser equivalentes, mas ainda há outras que vão além do escopo desse artigo de forma que recomendo fortemente esse artigo do site do projeto Anaconda. Cabe também destacar que a abordagem e estrutura de ambientes virtuais do conda diferem significativamente daquelas do venv, de forma que aconselho a leitura da documentação do conda que apresenta detalhadamente tais diferenças e descreve contextos onde uma ou outra abordagem são mais adequadas.

Já foi abordada a instalação do Anaconda aqui no site. Sendo o foco do presente artigo a criação e gerenciamento de ambientes virtuais, vamos direto ao ponto.

Por padrão, ambientes criados pelo conda são instalados na pasta envs dentro do diretório conda. Dessa forma, os comandos a seguir podem ser executados em qualquer diretório pois não o afetam. Você pode alterar esse comportamento para ter o ambiente dentro de uma pasta no seu projeto, de forma similar ao venv, mas há algumas considerações sobre essa mudança que vão além do escopo desse artigo, de forma que recomendo a leitura da documentação a respeito.

Entendendo o ambiente base

Quando finalizada a instalação do Anaconda, automaticamente é criado um ambiente virtual com o interpretador Python e os pacotes da instalação nomeado de base. As configurações padrão da instalação fazem com que o ambiente seja automaticamente ativado, de forma que ao abrir o terminal o mesmo deve indicar o ambiente no prefixo do prompt. Podemos verificar qual o interpretador Python que será utilizado:

(base) $ which python
/opt/anaconda/bin/python

Como esperado, o caminho indicado é o diretório de instalação do Ananconda.

Caso queira listar todos os pacotes instalados pelo Anaconda:

(base) $ conda list

O resultado deve ser uma extensa lista de pacotes.

Criando e ativando um ambiente conda

Considerando nosso cenário de dois projetos, vamos criar um ambiente para o Projeto A. Para criar um ambiente:

(base) $ conda create --name env_projeto_A

O ambiente criado dessa maneira estará completamente vazio, sem pacotes e sem interpretador. Depois de ter um ambiente criado, para ativar o mesmo basta:

$ conda activate env_projeto_A

Com o ambiente ativado, podemos verificar que realmente o mesmo se encontra vazio:

(env_projeto_A) $ which python
(env_projeto_A) $ conda list

Ambos os comandos não possuem retorno.

Instalando um interpretador Python em ambiente conda

Para instalar um interpretador Python no ambiente criado:

(env_projeto_A) $ conda install python

A instalação feita dessa forma instalará a versão mais recente do Python disponível nos repositórios do conda. Caso queira uma versão específica, basta especificá-la:

(env_projeto_A) $ conda install python=3.7

Com um interpretador instalado, podemos verificar o caminho do mesmo:

(env_projeto_A) $ which python
/home/chico/.conda/envs/env_projeto_A/bin/python

Instalando dependências em ambiente conda

Para instalar pacotes no ambiente, basta passar o nome do ambiente e do pacote. Se apenas o nome for passado, a versão mais recente do pacote disponível no repositório do conda será instalada. Caso você queira uma versão específica, basta passar o número da versão. Veja:

(env_projeto_A) $ conda install -n env_projeto_A scipy
(env_projeto_A) $ conda install -n env_projeto_A pandas=0.25.3

Criando um ambiente conda com interpretador e dependências

Tudo que fizemos até agora poderia ser feito em um único comando no momento da criação do ambiente. Ou seja, você pode criar um ambiente e já passar para o mesmo a versão Python a ser utilizada e pacotes que deseja:

$ conda create --name env_projeto_A python=3.7 scipy pandas=0.25.3

Utilize o comando conda list para verificar os pacotes instaldos e suas respectivas versões.

Aqui já vemos uma grande vantagem do conda: a possibilidade de instalar ambientes com diferentes versões de Python. Outra vantagem é que o conda verifica automaticamente se há conflitos de dependência entre os pacotes a serem instalados o que é muito útil em projetos grandes com muitas dependências, especialmente no processo de atualização das mesmas.

Verificando ambientes conda existentes

Para verificar quais os ambientes gerenciados pelo conda existentes:

(base) $ conda info --envs
# conda environments
#
env_projeto _A          /home/chico/.conda/envs/env_projeto_A
env_projeto _B          /home/chico/.conda/envs/env_projeto_B
base                 *  /opt/anaconda

O ambiente marcado com * é o ativo no momento.

Compartilhando um ambiente conda

Depois de configurar o ambiente com os pacotes de seu interesse, é interessante exportar essas configurações para que outros envolvidos no projeto possam ter um ambiente de desenvolvimento igual ou para que você mesmo possa replicar o ambiente em algum momento futuro.

O conda possui uma lógica similar ao venv e seu arquivo requirements.txt, sendo que o no caso do conda o formato é YAML e a estrutura do arquivo é um pouco diferente para se conformar a forma de atuação do conda. Para gerar esse arquivo o ambiente precisa estar ativo e, usualmente, o arquivo é nomeado como environment.yml, muito embora possa ter qualquer nome:

(env_projeto_A) $ conda env export > environment.yml

Vamos olhar a estrutura desse arquivo:

name: env_projeto_A
channels:
  - defaults
dependencies:
  - _libgcc_mutex=0.1=main
  - blas=1.0=mkl
  - ca-certificates=2020.6.24=0
  - certifi=2020.6.20=py37_0
  - intel-openmp=2020.1=217
  - ld_impl_linux-64=2.33.1=h53a641e_7
  - libedit=3.1.20191231=h14c3975_1
  - libffi=3.3=he6710b0_2
  - libgcc-ng=9.1.0=hdf63c60_0
  - libstdcxx-ng=9.1.0=hdf63c60_0
  - mkl=2020.1=217
  - mkl-service=2.3.0=py37he904b0f_0
  - mkl_fft=1.1.0=py37h23d657b_0
  - mkl_random=1.1.1=py37h0573a6f_0
  - ncurses=6.2=he6710b0_1
  - numpy=1.19.1=py37hbc911f0_0
  - numpy-base=1.19.1=py37hfa32c7d_0
  - openssl=1.1.1g=h7b6447c_0
  - pandas=0.25.3=py37he6710b0_0
  - pip=20.2.1=py37_0
  - python=3.7.7=hcff3b4d_5
  - python-dateutil=2.8.1=py_0
  - pytz=2020.1=py_0
  - readline=8.0=h7b6447c_0
  - setuptools=49.2.1=py37_0
  - six=1.15.0=py_0
  - sqlite=3.32.3=h62c20be_0
  - tk=8.6.10=hbc83047_0
  - wheel=0.34.2=py37_0
  - xz=5.2.5=h7b6447c_0
  - zlib=1.2.11=h7b6447c_3
prefix: /home/chico/.conda/envs/env_projeto_A

É uma estrutura um pouco mais complexa que a de um requirements.txt que vimos anteriormente no venv. Apresenta o nome do ambiente, o canal de onde vai baixar pacotes, a versão do interpretador Python para o ambiente e o caminho de onde o arquivo foi exportado.

Para criar um ambiente a partir de um arquivo de ambiente previamente criado:

$ conda env create -f environment.yml

Desativando e removendo um ambiente conda

Para desativar o ambiente em uso:

(env_projeto_A) $ conda deactivate

Para excluir por completo um ambiente e os pacotes nele instalados:

$ conda remove --name your_env_name --all

pyenv

Muito embora tenhamos visto que o sistema operacional já vem com Python, muitas vezes a versão disponível não é a mais recente. E nunca é uma boa ideia atualizar diretamente a versão do sistema operacional, pois algumas aplicações podem apresentar problemas, especialmente se dependem de alguma funcionalidade da linguagem que, em uma atualização posterior, foi removida sem opção de retrocompatibilidade. Já vimos que o conda permite instalar versões diferentes de Python por ambiente, mas ele não é a única opção. Falaremos agora sobre o pyenv

O repositório do projeto é muito didático, inclusive explicando algumas coisas que já foram abordadas aqui nesse artigo como, por exemplo, o funcionamento do $PATH.

Instalação do pyenv

É recomendável ver o repositório para as instruções de instalação pois o projeto precisa de algumas dependências para funcionar. Irei colocar abaixo as que são necessárias no momento que escrevo, mas recomendo que veja o repositório para se certificar que não ocorreram mudanças:

$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev libffi-dev liblzma-dev python-openssl git

$ curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

Ao final do comando anterior aparecem instruções na tela para que sejam colocadas no arquivo de configuração do bash (ou de qualquer outro shell que você utilize, como o ZSH por exemplo). Assim, ao final de seu arquivo .bashrc, ou equivalente, adicione o que aparecer na tela, que deve ser algo similar ao seguinte:

export PATH="~/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

Reinicie seu shell para que as mudanças tenham efeito:

$ exec $SHELL

Verificando se foi instalado:

$ pyenv

Se tudo der certo, aparecerá as instruções na tela de utilização e os principais comandos disponíveis.

Utilizando o pyenv

Para verificar todas as versões disponíveis dos interpretadores que podem ser instalados:

$ pyenv install -l

Repare que são diversas versões, havendo inclusive versões ainda em desenvolvimento (com sufixo -dev)

Para instalar uma versão específica:

$ pyenv install 3.8.5

É possível verificar as versões disponíveis no sistema:

$ pyenv versions
* system
3.8.5

Repare que ele destaca qual versão é a original do sistema (system). A versão marcada com asterisco é a que está ativa no momento.

Para não correr o risco de modificar a versão do sistema, você pode definir o Python padrão de sua escolha:

$ pyenv global 3.8.5

Talvez você precise reiniciar o terminal para ver o efeito do comando acima. Confirmando qual Python é o que está ativo:

$ which python
/home/chico/.pyenv/shims/python

$ python -V
Python 3.8.5

É interessante verificar também se o pip está usando o mesmo caminho apresentado pelo comando which python:

$ pip --version

Assim, nos certificamos que os pacotes instalados realmente estão atrelados ao Python de interesse e não ao Python do sistema.

Agora você pode juntar essa funcionalidade do pyenv, para ter diversas versões de Python se isso for necessário, com o venv para ter ambientes com essas versões.

pipenv

Por fim, mas não menos importante, temos o pipenv. Como pode se ver no repositório do projeto, trata-se de uma ferramenta bem ambiciosa que busca:

  • unificar o comportamento do pip e do venv;
  • corrigir os problemas que podem ocorrer em projetos com o requirements.txt;
  • aumentar a segurança por utilizar hashes (assinaturas criptográficas);
  • detalhar as dependências do projeto de forma clara;
  • separar dependências de desenvolvimento das dependências de utilização.

Instalação do pipenv

A instalação é bastante simples:

$ pip install pipenv

Para confirmar que a instalação foi feita corretamente, basta utilizar o comando pipenv. Será apresentada uma lista com os principais comandos.

Deixando a pasta de ambiente pipenv na pasta do projeto

Assim como o conda, o pipenv por padrão configura todos os ambientes em uma pasta do sistema. Alguns podem preferir que cada ambiente fique na pasta de seu respectivo projeto. Para isso, adicione ao ~/.bashrc (ou equivalente do seu shell) a seguinte linha:

export PIPENV_VENV_IN_PROJECT=1

Reinicie o shell ou faça source ~/.bashrc para que a alteração tenha efeito. No prosseguimento desse artigo, irei considerar que esse procedimento foi feito e, portanto, a pasta do ambiente está na pasta do projeto.

Criando um ambiente virtual com o pipenv

Para utilizar entre na pasta do projeto. Agora, o seguinte comando irá já criar um ambiente virtual para o projeto:

$ pipenv install

Um ambiente será criado com o interpretador Python ativo no momento da criação. Podemos verificar que o ambiente foi criado observando os novos integrantes da pasta do projeto:

$ ls -l -a
Pipfile
Pipfile.lock
.venv

Estrutura de um arquivo Pipfile

Há uma basta .venv, que possui estrutura similar a que já descrevemos ao falar sobre o venv, e dois arquivos Pipfile. Podemos entender esses arquivos como similares aos arquivos requirements.txt ou environment.yml já citados em outras seções. No entanto, a forma de gerenciar dependências do pipenv é mais robusta.

Primeiro vamos investigar o arquivo Pipfile:

[[Source]]
name = "pypi"
url = "https://pypi.org/simple"
verify ssl = true

[dev-packages]

[packages]

[requires]
python_version="3.8"

Vemos que nele há a fonte do repositório, o PyPi, o parâmetro que diz que é para utilizar SSL e a versão do Python. Repare que há seções, ainda em branco por ser um projeto novo, que separam os pacotes de desenvolvimento dos pacotes efetivos do projeto.

Instalando uma dependência com pipenv

Para instalar uma dependência, que aqui usaremos de exemplo a Pandas no nosso Projeto A, fazemos:

$ pipenv install pandas==0.25.3

Caso uma versão específica não fosse utilizada, a versão mais recente seria instalada.

Vamos novamente olhar o arquivo Pipfile:

[[Source]]
name = "pypi"
url = "https://pypi.org/simple"
verify ssl = true

[dev-packages]

[packages]
pandas = "==0.25.3"

[requires]
python_version="3.8"

Vemos que ele cita o pacote Pandas com a versão solicitada. Se não fosse passada uma versão, um “*” seria exibido ao lado do nome do pacote.

Sabemos que o Pandas possui suas dependências e que estas também certamente foram instaladas. Para ver como essas dependências se relacionam entre si e com o Pandas:

$ pipenv graph
pandas==0.25.3
  - numpy [required: >=1.13.3, instaled: 1.19.1]
  - python-dateutil [required: >=2.6.1, installed: 2.8.1]
    - six [required: >=1.5, installed: 1.15.0]
  - pytz [required: >=2017.2, installed: 2020.1]

Repare que agora vemos todas as dependências do Pandas e quais os intervalos de versão que são compatíveis. Isso permite o chamado build determinístico, quando se sabe exatamente todas as versões utilizadas no projeto. Esse gerenciamento é muito importante quando se tem um desenvolvimento local e se planeja subir para produção exatamente o mesmo que foi desenvolvido localmente. Para ter esse controle pormenorizado, temos o arquivo Pipfile.lock, um extenso arquivo onde são descritas todas as dependências diretas e indiretas, com suas assinaturas criptográficas, fontes e versões.

Instalando dependências de desenvolvimento

No decorrer de um projeto, teremos algumas dependências que são úteis apenas no desenvolvimento e que não precisam ir para produção. Como exemplo, temos bibliotecas de teste como o pytest, ou bibliotecas que usamos para manter o estilo do código em concordância com os padrões da linguagem como a flake8. Para instalar uma dependência apenas para desenvolvimento:

$ pipenv install --dev pytest
$ pipenv install --dev flake8

Tais dependências estão na seção [dev-packages] do Pipfile.

[[Source]]
name = "pypi"
url = "https://pypi.org/simple"
verify ssl = true

[dev-packages]
pytest = "*"
flake8 = "*"

[packages]
pandas = "==0.25.3"

[requires]
python_version="3.8"

Compartilhando um ambiente pipenv

Para começar um projeto a partir de um arquivo Pipfile como quando, por exemplo, clonamos um projeto do GitHub, fazemos:

$ pipenv install -d

Repare que foi passado a flag -d, indicando que as dependências de desenvolvimento também serão instaladas. Caso isso não seja necessário, basta tirar essa flag.

Quando fazemos o comando anterior, todo o grafo de dependências será reconstruído. Caso se queira um build determinístico com as exatas versões presentes do arquivo Pipfile.lock usamos o comando sync, que possui a flag -d para também instalar as dependências de desenvolvimento:

$ pipenv sync -d

Ativando um ambiente pipenv

O pipenv possui um shell próprio que pode ser ativado:

$ pipenv shell
(projeto A) $

Agora podemos utilizar as bibliotecas instaladas

(projeto A) $ pytest .

Para desativar, basta o comando exit.

Caso queira executar um comando sem ter que entrar e sair do shell, podemos usar o run:

$ pipenv run pytest .

Conclusão

Nesse artigo vimos como dependências Python são armazenadas e geridas por diferentes ferramentas, assim como podemos ter diferentes versões de Python para cada projeto.

Tentei colocar aqui o que considero um mínimo necessário para compreender ambientes virtuais, sua importância e o básico de utilização. O assunto é muito extenso e há ainda mais ferramentas e abordagens que não citei aqui, como o Poetry, pois busquei escrever sobre as que possuo mais familiaridade por já ter utilizado em projetos. Faz parte do seu progresso ir conhecendo as ferramentas, suas limitações e os contextos onde são mais adequadas. É muito importante também consultar as documentações de cada ferramenta.

Compartilhe:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima