Entendendo virtualenv

Publicado em 2016-03-11 por Vinicius Assef

Se você tem dúvidas sobre o que é, como funciona um virtualenv e que problema ele resolve, leia esse texto. É muito fácil.

O que é um virtualenv?

Em poucas palavras, o virtualenv permite que várias versões de uma mesma biblioteca possam conviver no mesmo computador, sem conflitos. Vou exemplificar.

Imagine que você trabalha em 2 projetos: um usando Django 1.4 e outro usando Django 1.10. Se instalarmos o Django direto no Python do sistema, não vai dar certo porque a versão instalada por último vai sobrepor a anterior. Um dos seus projetos não vai conseguir rodar.

O virtualenv resolve exatamente esse problema. Você consegue ter as duas versões da mesma biblioteca (Django, no nosso exemplo) instaladas no computador, sem conflito entre elas. E tem outra vantagem: você não precisa de acesso root para instalá-las.

Ao contrário do que o nome induz a pensar, um virtualenv não é uma VM (máquina virtual). Ele é uma solução muito mais simples, muito mais leve e muito mais fácil de usar.

Como o virtualenv funciona?

Parece magia, mas o princípio é muito simples: ele modifica o path que o Python procura pelas bibliotecas. Só isso.

Durante a inicialização do Python, o interpretador usa uma variável de ambiente chamada PYTHONPATH para saber onde procurar pelas bibliotecas instaladas. Isso é o module search path. Na prática, a PYTHONPATH diz para o Python onde (em qual diretório) buscar as bibliotecas para dar import.

Na verdade, o virtualenv é apenas uma estrutura de diretórios e links. Nada mais.

Para usar um virtualenv você precisa:

  1. criar o virtualenv
  2. atualizar o pip (comando para instalar bibliotecas Python)
  3. ativar o virtualenv
  4. instalar as bibliotecas desejadas dentro dele

A partir do Python 3.3 existe o módulo venv na biblioteca padrão. Os exemplos desse artigo foram testados em Python 3.5 e 3.6.

Eu costumo gravar meus virtualenvs no diretório ~/virtualenvs. Segue abaixo um exemplo dos passos descritos acima:

$ python3 -m venv ~/virtualenvs/projeto_dj1_4
$ source ~/virtualenvs/projeto_dj1_4/bin/activate
(projeto_dj1_4) $ pip install --upgrade pip setuptools
(projeto_dj1_4) $ pip install django==1.4.*
(projeto_dj1_4) $ cd path/para/o/diretorio/fonte/do/projeto
(projeto_dj1_4) $ python manage.py shell
Python 3.5.2 (default, Jul 20 2016, 16:13:40) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 4, 22, 'final', 1)
>>> exit()
(projeto_dj1_4) $ deactivate
$ 

Agora, criando outro virtualenv para o Django 1.10:

Repetindo os passos acima para o outro projeto, com Django 1.10, o resultado do último comando seria:

$ python3 -m venv ~/virtualenvs/projeto_dj1_10
$ source ~/virtualenvs/projeto_dj1_10/bin/activate
(projeto_dj1_10) $ pip install --upgrade pip setuptools
(projeto_dj1_10) $ pip install django==1.10.*
(projeto_dj1_10) $ cd path/para/o/diretorio/fonte/do/projeto
(projeto_dj1_10) $ python manage.py shell
Python 3.5.2 (default, Jul 20 2016, 16:13:40) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 10, 3, 'final', 1)
>>> exit()
(projeto_dj1_10) $ deactivate
$

Obs.: o Ubuntu 16.04 não traz o pip junto com o virtualenv. Portanto, nele os comandos seriam:

$ python3 -m venv ~/virtualenvs/meu_proj --without-pip
$ source ~/virtualenvs/meu_proj/bin/activate
(meu_proj) $ curl https://bootstrap.pypa.io/get-pip.py | python -
(meu_proj) $ pip install django==1.4.*

Nunca esqueça de ativar o virtualenv antes de usá-lo:

$ source path/para/o/virtualenv/bin/activate

É assim que você diz ao Python qual virtualenv está ativo no nomento. E, por consequência, em qual diretório buscar pelas bibliotecas instaladas.

Quando você terminar de trabalhar com o virtualenv, desative-o com o comando

(meu_proj) $ deactivate

ou simplesmente fechar a janela do shell.

Como é aquele lance de PYTHONPATH?

Voltando um pouco ao início, agora que você já entendeu para que serve e como funciona um virtualenv, lembra que eu falei sobre a variável PYTHONPATH? Quer ver a diferença? Primeiro, vamos ver o Python do sistema, sem virtualenv:

$ python
Python 3.5.2 (default, Jul 20 2016, 16:13:40) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print('\n'.join(sys.path))

/home/viniciusban/.pyenv/versions/3.5.2/lib/python35.zip
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5/plat-linux
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5/lib-dynload
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5/site-packages
>>> 

Agora veja o path do Python com o virtualenv ativo:

$ source ~/virtualenvs/meu_venv/bin/activate
(meu_venv) $ python
Python 3.5.2 (default, Jul 20 2016, 16:13:40) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print('\n'.join(sys.path))

/home/viniciusban/.pyenv/versions/3.5.2/lib/python35.zip
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5/plat-linux
/home/viniciusban/.pyenv/versions/3.5.2/lib/python3.5/lib-dynload
/home/viniciusban/virtualenvs/meu_venv/lib/python3.5/site-packages
>>> 

Note a última linha de cada uma das listas acima. O diretório site-packages do sistema (último da primeira lista) é substituído pelo equivalente do virtualenv.

Por isso os pacotes instalados no seu virtualenv serão reconhecidos, sem interferir nos outros virtualenvs existentes no seu computador. Simples e eficiente!

E se eu precisar usar mais de uma versão de Python?

Para isso o virtualenv não serve.

Uma outra ferramenta muito útil, pyenv, resolve essa questão muito bem. Ele permite ter várias versões de Python no mesmo computador. Ativando cada uma no momento que você desejar. É como se fosse virtualenv, mas em um nível mais alto, para versões de Python. Com ele você pode ter, por exemplo, Python 3.4 e Python 3.5 instalados, sem conflito.

O pyenv usa um princípio parecido com o do virtualenv, mas com uma vantagem: ele mantém um repositório das versões de Python. Então, para instalar uma nova versão, basta você digitar o comando $ pyenv install <versao> na linha de comandos e voilà!

E é claro, mesmo usando o pyenv, o virtualenv continua necessário e recomendado.

Quando devo usar um virtualenv?

Uma pergunta que eu ouço com frequência sobre virtualenv é:

Quando devo usar um virtualenv?

A resposta que dou:

Sempre!

Crie um virtualenv para cada projeto. Mesmo em ambiente de produção.

Não existe motivo para não usá-los. Eles são simples, leves e podem ser recriados facilmente com todas as dependências.

Além disso, facilitam muito quando você precisa fazer teste de uma nova versão do seu sistema ou de alguma biblioteca usada, sem ter que criar uma VM ou provisionar um novo servidor.

Como assim, recriados facilmente?

Se você mantiver um arquivo com as dependências do seu projeto, você pode recriar seu virtualenv com um comando.

Quem faz isso é outro programa, o pip. É ele quem instala bibliotecas no Python.

É recomendável manter um arquivo, que por convenção chamamos de requirements.txt (mas pode ter qualquer nome), que relaciona as dependências do seu projeto. Dou mais detalhes sobre o pip no artigo Como manter as dependências de bibliotecas garantindo as correções de bugs.

Para instalar todas as dependências de seu projeto a partir de um arquivo, use:

(meu_venv) $ pip install -r requirements.txt

Finalizando, não há motivo para não usar virtualenv. É simples e vai economizar problemas de conflito entre as bibliotecas dos seus projetos.


Agradecimento: quero agradecer ao @cassiobotaro e ao @filipecifali por me ajudarem com revisão, experiência e ajustes nesse texto.

Vinicius Assef

Eu sou apaixonado por Python e shell script.

Aprenda com seus erros e dê nome certo às coisas.