Entendendo virtualenvs
Autor: Vinicius Assef | Publicado: 2016-11-03T16:41:00-02:00



Se você é iniciante em Python, provavelmente já ouviu falar em _virtualenv_. Todo mundo aconselha usar, mas para que serve um _virtualenv_? Que problema ele resolve, afinal?



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.

Suponhamos que você tenha 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 o que for instalado por último vai sobrepor o primeiro. Um dos seus projetos não vai conseguir rodar.

O virtualenv resolve exatamente esse problema. Você consegue ter os dois projetos rodando no mesmo computador, sem conflito entre as versões das bibliotecas (Django, no nosso exemplo). E tem outra vantagem: você não precisa de acesso root para instalar as bibliotecas.

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



Como o virtualenv funciona?
---------------------------

Explicando de forma simplificada, o virtualenv trabalha com a variável de ambiente [PYTHONPATH](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH) para modificar o [module search path](https://docs.python.org/3/tutorial/modules.html#the-module-search-path). Na prática ele diz para o interpretador do Python onde (em qual diretório) buscar as bibliotecas para dar `import`.


Para usar um virtualenv você precisa:

1. criar o virtualenv
1. atualizar o `pip` (comando para instalar bibliotecas Python)
1. ativá-lo
1. instalar nele as bibliotecas desejadas

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.


Eu costumo criar um diretório sob o meu `$HOME` onde guardo todos os virtualenvs: `$ mkdir ~/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)

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

    (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)


**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.*


Vale lembrar que você precisa ativar o virtualenv sempre antes de usá-lo com `$ source path/para/o/virtualenv/bin/activate`. É assim que você diz ao Python qual virtualenv está ativo no nomento.

Quando você terminar de trabalhar com o virtualenv, basta desativá-lo com `$ deactivate` ou simplesmente desligar o computador ou 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 o `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. Viu? O diretório `site-packages` do sistema (último da lista) não é reconhecido no virtualenv. No lugar dele, o `site-packages` do virtualenv está lá.

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 essa situação existe outra ferramenta muito legal, que é o [pyenv](https://github.com/yyuu/pyenv). 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 para versões de Python. Com ele você pode ter, por exemplo, Python 3.4 e Python 3.5 instalados, sem conflito.

O [pyenv](https://github.com/yyuu/pyenv) usa um princípio parecido ao 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 `$ pyenv install 2.7` e voilà!

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


Quando devo usar um virtualenv?
-------------------------------

Uma pergunta que sempre ouvimos sobre virtualenv é:

> Quando devo usar o 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 usar virtualenvs. 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 exclusivo.


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](https://packaging.python.org/installing/#use-pip-for-installing). É ele quem instala bibliotecas no Python.

É recomendável manter um arquivo, que por convenção chamamos de `requirements.txt` ou `requirements.pip` (mas pode ter qualquer nome), que lista as dependências do seu projeto. Dou mais detalhes sobre o pip no artigo [Como manter as dependências de pacotes garantindo as correções de bugs](http://aprenda-python.blogspot.com/2016/02/como-manter-as-dependencias-de-pacotes.html).

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

    (meu_venv) $ pip install -r requirements.txt


Finalizando, use virtualenv. É simples e vai economizar problemas de conflito entre as bibliotecas dos seus projetos.


-------------------


Agradecimento: quero agradecer ao [@cassiobotaro](http://twitter.com/cassiobotaro) e ao [@filipecifali](http://twitter.com/filipecifali) por me ajudarem com revisão, experiência e ajustes nesse texto.