Fabric + VirtualEnv: uma combinação explosiva (no bom sentido)

Escreveu uma super-hiper-biblioteca ou aplicação em Python mas quer se certificar de que ela funciona em diferentes versões do interpretador? Embora isso possa ser resolvido através da execução manual dos testes com os vários interpretadores, isso é chato e improdutivo. Além do mais, cada versão do Python pode ter diferentes bibliotecas instaladas, o que pode acabar influenciando no resultado dos testes.

Temos portanto dois problemas a serem resolvidos. O primeiro problema, relacionado a automatização de tarefas repetitivas, pode ser facilmente solucionado com Fabric, uma ferramenta muito útil para criação de scripts de build e deployment de aplicações. Já escrevemos um texto introdutório sobre a mesma, não deixe de conferir.

O segundo problema, entretanto, é um pouco mais complicado de se resolver, mas não é nenhum bicho de sete cabeças. Com VirtualEnv, é possível criar ambientes Python isolados com o objetivo de testar e executar aplicações em uma espécie de sandbox onde temos total controle sobre as bibliotecas ali instaladas.

Instalando as diferentes versões do Python

O primeiro passo é instalar as versões do Python a serem usadas nos testes. Rode o comando abaixo para instalar os diferentes interpretadores, supondo que você utilize Debian e sua aplicação precise funcionar nas versões 2.4, 2.5 e 2.6 do Python:

$ sudo apt-get install python2.4 python2.4-dev \
    python2.5 python2.5-dev \
    python2.6 python2.6-dev

A versão atual do Ubuntu já inclui o Python 2.6 por padrão. Veja também que aproveitamos para instalar os arquivos de desenvolvimento para cada versão, pois eventualmente precisamos deles para compilar bibliotecas com extensões escritas em C.

Instalando Fabric e VirtualEnv

Da mesma forma:

$ sudo apt-get install python-setuptools

Após a instalação do SetupTools, execute o seguinte comando para instalar o Fabric e o VirtualEnv:

$ sudo easy_install Fabric virtualenv

Configurando ambientes isolados para sua aplicação

O próximo passo é criar ambientes de execução isolados para sua aplicação, sendo que cada ambiente deve usar uma versão diferente do interpretador do Python. Para fazer isso, execute os comandos abaixo, substituindo o APP pelo nome da sua aplicação ou biblioteca:

$ mkdir ~/.virtualenvs
$ cd ~/.virtualenvs
$ virtualenv --python=/usr/bin/python2.6 --no-site-packages APP-py2.6
$ virtualenv --python=/usr/bin/python2.5 --no-site-packages APP-py2.5
$ virtualenv --python=/usr/bin/python2.4 --no-site-packages APP-py2.4

Primeiramente criamos o diretório ~/.virtualenvs, que é por convenção o diretório padrão para armazenamento dos ambientes. Em seguida, criamos três ambientes para nossa aplicação APP. A flag --no-site-packages é opcional e indica que as bibliotecas instaladas no diretório site-packages do Python em questão não devem ser visíveis ao ambiente sendo criado.

Finalmente, não esqueça de instalar, em cada um dos ambientes, as dependências exigidas pela sua aplicação:

$ source ~/.virtualenvs/APP-py2.6/bin/activate
(APP-py2.6) $ easy_install biblioteca1 biblioteca2 ... bibliotecaN
(APP-py2.6) $ source ~/.virtualenvs/APP-py2.5/bin/activate
(APP-py2.5) $ easy_install biblioteca1 biblioteca2 ... bibliotecaN
(APP-py2.5) $ source ~/.virtualenvs/APP-py2.4/bin/activate
(APP-py2.4) $ easy_install biblioteca1 biblioteca2 ... bibliotecaN

Supondo que sua aplicação utilize o SetupTools para a configuração dos metadados do projeto (através do script setup.py), os comandos abaixo devem indicar se a aplicação funciona nos ambientes que criamos:

$ source ~/.virtualenvs/APP-py2.6/bin/activate
(APP-py2.6) $ python setup.py test
...
(APP-py2.6) $ source ~/.virtualenvs/APP-py2.5/bin/activate
(APP-py2.5) $ python setup.py test
...
(APP-py2.5) $ source ~/.virtualenvs/APP-py2.4/bin/activate
(APP-py2.4) $ python setup.py test
...

Automatizando!

Se tudo funcionou até aqui, estamos prontos para criar um script que nos permita rodar os testes de forma automática, nos diferentes ambientes.

Supondo que sua aplicação utilize o SetupTools para a configuração dos metadados do projeto, o exemplo abaixo resolve o problema:

# -*- coding: utf-8 -*-
 
# Environment info
config.project = 'APP'
config.virtualenv_dir = '~/.virtualenvs'
 
# Supported Python versions
config.versions = ('2.4', '2.5', '2.6',)
config.default_version = '2.6'
 
def setup(command, version=config.default_version):
    """Executes the given setup command with a virtual Python installation.
    """
    local('%s/%s-py%s/bin/python setup.py %s' % (config.virtualenv_dir, config.project, version, command))
 
def test():
    """Runs all tests in different Python versions.
    """
    for version in config.versions:
        setup('test', version)

Agora é só alegria:

$ fab test
Fabric v. 0.1.1.
Running test...
[localhost] run: ~/.virtualenvs/APP-py2.4/bin/python setup.py test
...
[localhost] run: ~/.virtualenvs/APP-py2.5/bin/python setup.py test
...
[localhost] run: ~/.virtualenvs/APP-py2.6/bin/python setup.py test
...

Devo lembrar que o script mostrado é só um exemplo. Você pode (e deve) modificá-lo para que este atenda às suas necessidades.

(Bônus!) Distribuindo sua aplicação para diferentes versões do Python

Com mais algumas linhas de código, podemos criar tasks que nos permitam gerar arquivos para distribuição da aplicação para diferentes versões do Python:

def dist_src():
    """Create source archive.
    """
    setup('sdist')
 
def dist_eggs():
    """Create binary archives, one for each Python version.
    """
    for version in config.versions:
        setup('bdist_egg', version)

(Bônus!) Automatizando geração de documentação e upload para o PyPI

Ainda acha pouco? Este outro exemplo de script vai além e mostra, numa aplicação real, como automatizar o registro e upload dos pacotes para o PyPI, atualizar site de documentação, entre outras coisas.

Leave a Reply