Cookbook : pip, virtualenv

le

Je présente dans cet article mon workflow concernant l'utilisation de virtualenv et pip...

Je tiens à préciser qu'il y a d'autres workflows possibles, par exemple l'utilisation de buildout mais ce n'est pas le sujet de cet article.

Méthode « classique »

Voici la méthode que je nomme "classique" car je pense qu'elle est beaucoup utilisée. J'utilisais cette méthode il y a encore quelques mois.

Prérequis sur la machine (je ne précise pas leur installation, ce n'est pas mon sujet) :

Je prépare un dossier de travail et je clone mon projet :

$ mkdir ~/my_project/
$ cd ~/my_project/
$ git clone http://stephane@repos.stephane-klein.info/example1 .
$ ls -1
devel-requirements.txt
example1
requirements.txt
setup.py
tests
unittest.cfg

Je crée un environnement virtuel python dans le dossier courant :

$ virtualenv .

Comme mon projet est un package python, mon dossier contient un fichier setup.py.

Je lance son installation mais avec l'option -e pour qu'il soit éditable.

$ bin/pip install -e .

Je peux maintenant utiliser mon package :

$ bin/python
>>> import example1
>>> example1.hello_world()
Hello world

Voila, mon installation en mode développement est finie... La seule difficulté pour certaines personnes c'est l'installation des dépendances de bases (distribute/setuptools, pip, virtualenv).
La suite de l'article présente une solution plus simple, avec moins de dépendances.

Installation en mode « édition » ?

Voici deux méthodes pour installer un package python à partir du fichier setup.py :

$ python setup.py install

ou

$ pip install .

Ces deux commandes installent le package dans le dossier site-packages de votre environnement python.

Si vous modifiez le code source de votre package, vos modifications ne seront pas "visibles" lors de l'exécution du package dans votre environnement, car une copie des fichiers sources a été faite lors de l'installation.
Par conséquent, à chaque modification, vous allez devoir installer de nouveau votre package.

Ceci est très pénible si vous êtes en train de développer votre application.

C'est là que l'installation en mode "editable" entre en jeu.
Lors de l'installation en mode édition, le code source n'est pas copié vers le dossier site-packages mais un lien symbolique est utilisé.
Vos modifications seront tout le temps pris en compte, sans passer par l'étape d'installation.

Voici deux commandes pour effectuer une installation en mode « édition » :

$ python setup.py develop

ou

$ pip install -e .

Par le passé, python setup.py develop ne permettait pas la désinstallation du package. Maintenant ce n'est plus le cas, par conséquent ces deux commandes semblent être synonymes.

Utilisation des fichiers requirements.txt

L'option --requirement de pip est intéressante, elle permet d'installer des dépendances depuis un fichier texte.

Bon, je pense que je n'apprends rien à personne, cette option est très connue.

Par contre, sa syntaxe est moins bien connue, exemples :

  • -e package permet d'installer un package en mode édition
  • -e . est équivalent à pip install -e . ou python setup.py develop
  • -e https://github.com/pypa/pip.git#egg=pip fait un clone du dépôt dans src/pip/ et installe le package en mode édition
  • -r requirements.txt il est possible d'installer un autre fichier de "requirements"

Il est souvent utile d'avoir des dépendances de packages pour la version de production et des dépendances supplémentaires pour le mode développement.

Par conséquent on trouve souvent deux fichiers requirements :

  • requirements.txt
  • devel-requirements.txt

Exemple de fichier devel-requirements.txt que j'utilise :

-e .
git+https://github.com/nose-devs/nose2.git#egg=nose2
sphinx
Sphinx-PyPI-upload

Explications :

  • j'utilise -e . pour installer le package sur lequel je suis en train de travailler
  • j'installe une version qui n'est pas encore releasé de nose2
  • j'installe des outils de documentation dont j'ai besoin en mode développement

Donc pour installer la version de développement de mon projet, je fais :

$ pip install -r devel-requirements.txt

Installation de requirements.txt depuis setup.py

Il est possible d'avoir un setup.py qui utilise la liste des dépendances de requirements.txt.

Exemple :

from setuptools import setup, find_packages


def parse_requirements(file_name):
    requirements = []
    for line in open(file_name, 'r').read().split('\n'):
        if re.match(r'(\s*#)|(\s*$)', line):
            continue
        if re.match(r'\s*-e\s+', line):
            requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', line))
        elif re.match(r'\s*-f\s+', line):
            pass
        else:
            requirements.append(line)

    return requirements

setup(
    name='my_project',
    version='0.1.0',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    install_requires=parse_requirements("requirements.txt")
)

Ici, parse_requirements lit le fichier requirements.txt et utilise sont contenu avec l'argument install_requires de la fonction setup.

Mais attention ! La fonction setup ne sait pas traiter la syntax spécifique à pip. Par exemple, les lignes du style git+http... ne fonctionneront pas.

Méthode avec bootstrap de virtualenv

Depuis quelques mois, j'utilise la fonctionnalité « Creating Your Own Bootstrap Scripts » de virtualenv.

Cette fonctionnalité permet de générer un fichier python, qui installera automatiquement un environnement virtuel comme la commande virtualenv . mais sans aucune dépendance à installer.
pip sera bien présent dans bin même si vous ne l'avez pas installé avant.

Utilisation de bootstrap.py

Avant de voir comment générer un fichier bootstrap.py, nous allons voir comment l'utiliser :

$ mkdir ~/my_project/
$ cd ~/my_project/
$ git clone http://stephane@repos.stephane-klein.info/example1.1 .
$ python bootstrap.py
$ bin/pip install -r devel-requirements.txt

Voila, rien de plus... on va voir par la suite qu'il est même possible d'installer automatiquement les devel-requirements.txt.

Génération d'un bootstrap.py

La documentation de virtualenv, donne une exemple de génération d'un fichier bootstrap.py.

Personnellement, je place un fichier nommé create-bootstrap.py dans le dossier où je souhaite créer mon fichier bootstrap.py. Ce fichier create-bootstrap.py contient le code suivant :

import virtualenv, textwrap

output = virtualenv.create_bootstrap_script(textwrap.dedent("""
import os, subprocess

def adjust_options(options, args):
    if len(args) == 0:
        args.append('.')

def after_install(options, home_dir):
    subprocess.call([
        os.path.join('bin', 'pip'),
        'install', '-r', 'devel-requirements.txt'
    ])
"""))
f = open('bootstrap.py', 'w').write(output)

La ligne args.append('.') indique qu'au lancement de bootstrap.py l'environnement python sera installé dans le dossier courant.

Un peu plus bas, je lance l'installation de devel-requirements.txt.

J'ai juste à lancer (une fois) python create-bootstrap.py pour générer bootstrap.py :

$ ls -1
create-bootstrap.py
setup.py
devel-requirements.txt
requirements.txt
$ python create-bootstrap.py
$ ls -1
bootstrap.py
create-bootstrap.py
devel-requirements.txt
requirements.txt
setup.py

Maintenant, j'ajoute create-bootstrap.py et bootstrap.py dans mon dépôt.

$ git add create-bootstrap.py bootstrap.py

Par la suite, il n'y a plus besoin d'utiliser create-bootstrap.py à moins de vouloir modifier bootstrap.py pour ajouter/supprimer des actions automatiques.

Pour finir, mon workflow complet

Prérequis:

  • python

Simple niveau prérequis, non ? Un peu comme buildout pour ceux qui connaissent.

Je prépare un dossier avec mon projet :

$ mkdir ~/my_project/
$ cd ~/my_project/
$ git clone http://my-project.org/ .

En une seule commande, j'installe mon environnement python et mon projet en mode développement (éditable).

$ python bootstrap.py

Partager cet article :