Vous êtes sur la page 1sur 19

Note technique

Guide pour le développement de logiciels

Référence : nt_ums3365_guide-developpement-logiciels_promise
Auteur : P. Payet, F. Gabarrot

Version du document : b.0

Première édition : 31/07/2014


Dernière révision : 07/12/2015

Nombre de pages : 19
Historique des évolutions
Date Description Section affectée
31/07/2014 Création.
07/12/2015 Nouvelle édition du document par P. Payet.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 2/19


Acronymes

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 3/19


Table des matières
1.Documentation.................................................................................................................................5
1.1 Documents de référence............................................................................................................5
1.2 Documents associés..................................................................................................................5
2.Introduction......................................................................................................................................6
3.Dépôts logiciels................................................................................................................................7
4.Démarrage rapide.............................................................................................................................8
5.Python...............................................................................................................................................9
5.1 Style de codage.........................................................................................................................9
5.1.1 Linter.................................................................................................................................9
5.1.2 Environnement virtuel.......................................................................................................9
5.2 Tests........................................................................................................................................10
5.2.1 Nose................................................................................................................................10
5.2.2 Tox...................................................................................................................................11
6.Organisation des paquets................................................................................................................12
6.1 Organisation standard.............................................................................................................12
6.2 Le versionning........................................................................................................................12
6.3 Commentaires dans les codes et documentation technique de référence...............................13
7.Intégration continue........................................................................................................................15
7.1 Jenkins Continuous Integration..............................................................................................15
8.Licence...........................................................................................................................................21

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 4/19


1. DOCUMENTATION

1.1 Documents de référence


Code Référence Titre Auteurs Date
DRxxx

1.2 Documents associés


Code Référence Titre Auteurs Date
DAxxx

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 5/19


2. INTRODUCTION
Le langage de programmation retenu pour les différents projets est le Python. Ce langage très
répandu et facile à maîtriser permet un développement rapide et multiplate-forme.
A l'heure où ces lignes sont écrites, nous sommes toujours à la transition entre deux versions
majeures de ce langage de script. La version 2 (actuellement 2.7.1) est progressivement remplacée
par la version 3 (3.4.2). Il existe des différences mineures entre ces deux langages mais elles
peuvent changer la manière d'écrire le code.
L'ensemble du code va donc être écrit en Python 2 mais inclura des librairies qui s'occuperont
d'assurer la compatibilité du code avec Python 3.

Le processus de développement suit des conventions, pour faciliter la vie du programmeur et lui
permettre de rester AGILE. Dans la suite du document, nous détaillerons l’environnement de
développement du projet avec les conventions et outils adoptés.

Ce document s'adresse à toutes les personnes de l'équipe projet ou à toute personne souhaitant
apporter sa collaboration.

Contact :
osureunion-informatique@univ-reunion.fr

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 6/19


3. DÉPÔTS LOGICIELS
Les logiciels sont disponibles sur deux forges :
• Le serveur GitLab de l'équipe projet :
http://osur.univ-reunion.fr/services/outils/devspot/
En plus du serveur GitLab vous pourrez trouver sur cette interface web un espace wikis
avec le wiki du projet et le wiki de l'équipe informatique (informations sur l'utilisation de
Git et GitLab).
• Le forge Renater, SourceSup (forge préférée du pôle Aeris):
https://sourcesup.renater.fr/

Un login et un mot de passe d'accès est nécessaire pour l'accès aux projets sur ces deux forges.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 7/19


4. DÉMARRAGE RAPIDE
Dans le cadre du projet Promise, voici comment installer son environnement de développement sur
Ubuntu Linux :

Vérifier que la version de python que vous utiliser est bien la 2.7 par défaut :
$ python --version
Python 2.7.6
Si ce n'est pas le cas, vous devez faire pointer le lien symbolique /usr/bin/python vers
/usr/bin/python2.

Il faut ensuite installer git et python-pip afin de récupérer les sources.


sudo apt-get install git python-pip

On récupère les sources des projets qui nous intéressent :


mkdir ~/code && cd ~/code
git clone git+ssh://git@osur-devspot.univ-reunion.fr/promise/atmdata.git
git clone http://osur-devspot.univ-reunion.fr/promise/miscbox.git
git clone http://osur-devspot.univ-reunion.fr/promise/lidarcommons.git ldc

Installer un environnement virtuel python pour chaque librairie en cours de développement. Ici nous
utiliserons un environnement virtuel commun pour toutes les librairies de développment.
sudo pip install virtualenvwrapper
printf '\n# Load virtualenvwrapper module\nsource /usr/local/bin/virtualenvwrapper.sh'
>> ~/.bashrc
source ~/.bashrc
mkvirtualenv promise
Pour que l'installation des dépendances de chaque librairie aboutisse, il faut installer plusieurs
paquets au préalable :
sudo apt-get install python-wxgtk2.8 libxml2-dev libxslt1-dev lib32z1-dev cython
python3-dev gfortran libblas3 libblas-dev liblapack3 liblapack-dev libhdf5-dev
libpng12-dev libfreetype6-dev libnetcdf-dev tcl-dev tk-dev python-tk python3-tk

On peut ensuite procéder à l'installation des dépendances :


workon promise
pip install -r ~/code/atmdata/requirements_dev.txt
pip install -r ~/code/miscbox/requirements_dev.txt
pip install -r ~/code/ldc/requirements_dev.txt
add2virtualenv ~/code/atmdata ~/code/miscbox ~/ldc

Vous pouvez maintenant modifier le code avec votre éditeur préféré.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 8/19


5. PYTHON

5.1 Style de codage


Pour que le code reste lisible et soit uniforme nous suivons la norme PEP 8. Elle comportent
beaucoup de règles mais qui peuvent être longues à retenir. Pour cela l'utilisation d'un linter est
fortement recommandée.

5.1.1 Environnement virtuel


Afin de ne pas encombrer votre site-packages personnel et de pouvoir facilement remonter les
librairies nécessaires pour le packaging du projet, on utilisera des environnements virtuels distincts
pour chaque projet de développement.
Le programme virtualenv réponds à ces besoins. Cependant nous utiliserons virtualenvwrapper,
basé sur virtualenv qui fournit une liste de commandes simplifiant la gestion de ces
environnements virtuels.
L'installation se fait de la manière suivante:
sudo pip install virtualenvwrapper
printf '\n# Load virtualenvwrapper module\nsource /usr/local/bin/virtualenvwrapper.sh'
>> ~/.bashrc
source ~/.bashrc

Créer un environnement virtuel


$ mkvirtualenv promise
(promise)$
Une fois l'environnement créer, celui-ci est directement activer. Cela est signalé par le nom de
votre environnement devant le prompt habituel du shell que vous utilisez.
En utilisant le flag -p on peut changer la version de python par défaut.
$ mkvirtualenv -p /usr/bin/python3 toto

Activer/désactiver un environnement
$ workon
toto
promise
ubber_project
$ workon promise
(promise) $ python –version
Python 2.7.6
(promise) $ deactivate
$ workon toto
(toto) $ python –-version
Python 3.5.0

Ajouter un répertoire au PATH de l'environnement


Vous aurez surement besoin d'ajouter des librairies personnels ou temporaire à vos
environnements virtuels. Plutôt que de modifier le PYTHONPATH pour chaque projet, la commande
add2virtualenv permet d'ajouter autant de répertoire que nécessaire au PATH de l'environnement.

La documentation est disponible à l'adresse suivante :


https://virtualenvwrapper.readthedocs.org/en/latest/#

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 9/19


5.2 Tests
Il est essentiel de tester le code produit et de documenter cette phase de tests. On peut, de cette
manière, vérifier la consistance du programme au fil des évolutions des différentes librairies.
A chaque fonction importante doit correspondre une batterie de tests unitaires. Le développeur
doit idéalement prévoir les tests avant de développer sa fonction.

5.2.1 Nose
En programmation agile, il faut tester son code. Pour cela, on utilise un framework de test: nose. Il
se base sur la librairie unittest est se définit lui même comme un “nicer testing for python”. Il
permet d'écrire des tests rapidement avec une syntaxe simplifié par rapport à unittest et surtout de
trouver automatique tous les tests à exécuter. Fini la maintenance du script shell qui doit exécuter
tous les tests. Il suffit de respecter la syntaxe de détection de nose pour que le framework les
exécute.
Si vous êtes habitué à écrire vos tests avec unittest et ne voulez pas apprendre une syntaxe plus
simple et plus lisible, c'est possible! nose est effet capable de lire aussi bien les tests écrit avec la
syntaxe de unittest que ceux écrit avec sa propre syntaxe.

Uniformisation du style des tests

Comme expliqué précédemment, les tests se trouvent dans le répertoire test/ à la racine du projet.
noseva chercher tous les fichiers ou répertoire détecter par cette expression régulière (?:^|
[\\b_\\.-])[Tt]est
$ tree test/
test/
├── test_module1
│ └── test_module1.py
├── test_module2
│ └── test_module2.py
└── test_package.py

A l'intérieur des ces fichiers, il va ensuite chercher toutes les fonctions ou classes à l'intérieur de ces
fichiers ayant une correspondance avec l'expression régulière précédante.

Voici un exemple du fichier test_module1.py:


from module1 import function_to_test1, function_to_test2
from Exception import IOError

def test_lower_bound_function_to_test1():
expected_value = None
assert test_function_to_test1(-1) == expected_value

def test_wrong_file_function_to_test2():
expected_value = IOError
filepath = '/wrong_path_to_test/file.txt'
try:
function_to_test2(filepath)
except Exception as e:
assert (e.__cause__, IOError)

Lorsque tous les tests sont écrits, on utilise la commande nosetests pour les exécuter.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 10/19


$ pwd
/home/dev/mymodule/
$ nosetests
Si on veut exécuter uniquement un fichier de test, il suffit de la préciser dans la ligne de
commande.
$ nosetests test/test_module1/test_module1.py
De nombreuses options sont disponibles, comme les test de couverture, la sélection des tests à
inclure ou exclure, de préciser l'environnement (python 2 ou 3). Plus d'information en utilisant le
flag -h ou en se rendant à l'adresse suivante http://nose.readthedocs.org/.

5.2.2 Tox
Ce module python sert à automatiser les tests. Il est compatible avec de nombreux framework de
test python (nose, unittest, pytest). Son installation est très simple:
$ pip install tox
Pour l'utiliser il faut configurer un fichier tox.ini à la racine du projet.
Un avantage de ce module est qu'il peut s'exécuter localement pour lancer la batterie de tests et
d'environnement virtuel et peut aussi être utiliser par un serveur d'intégration (ex: Jenkins). Le
développeur peut donc lancer les mêmes tests qui vont être éxécuter par le serveur d'intégration
continue à chacun de ses push. Une bonne pratique à mettre place par chaque développeur afin de
ne pas être innondé de mail d'erreurs par la suite.

On lance l'éxécution du du fichier tox.ini avec la commande tox.

Voici un exemple de fichier tox.ini:


[tox]
envlist = py27, py34 # select which python you want to test

[testenv] # environment declaration


deps= # dependencies installed in the venv
nose
future
lxml
natsort
setenv= # Env Var to be active in the venv
PYTHONDONTWRITEBYTECODE=1 # deactivate bytecode generation
commands= # command to execute in each venv
nosetests -I test_form

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 11/19


6. ORGANISATION DES PAQUETS

6.1 Organisation standard


PaquetLogiciel/ ←- nom du logiciel
paquetlogiciel/ ←- fichiers sources (en miniscule) dont __init__.py
utils/ ←- utilitaires générés ou associés
test/ ←- sources et executables de test
doc/ ←- documentation
algorithms/ ←- doc algo de référence
development_manual/ ←- doc de conception, de développement, schémas, etc
reference_guide/ ←- doc technique de référence (Doxygen)
html/ ←- dans sa version html
user_guide/ ←- doc utilisateur
validation/ ←- doc de validation et de suivi des anomalies
AUTHOR ←- fichier contenant le ou les noms des auteurs
CHANGELOG - fichier traçant les modifications
LICENSE ←- fichier contenant la licence (+ LICENSE-fr)
README.md ←- description du paquet logiciel (format ReST).
TODO ←- qu'est-ce qu'on doit encore faire et/ou prévu de faire
VERSION ←- version du paquet x.x.x
setup.py ←- fichier d'installation Python
MANIFEST.in ←- fichier texte qui liste les fichiers non python à inclure dans
l'installation
Pour faciliter l'étape de création d'un nouveau paquet, des templates sont à votre disposition. Le
dépôt cookie-python contient les informations nécessaires pour générer un nouveau paquet python.

6.2 Le versionning
Format X.Y.Z :
• X : nombre entier correspondant à l'édition principale du logiciel. On l'incrémente lorsqu'on
réalise une évolution majeure du logiciel (révolution!). X=0 est réservé à des versions
prototypes ou encore instables et/ou non finalisées. X=1 est la première version stable.
• Y : nombre entier correspondant au numéro de révision. On l'incrémente quand on
implémente des nouvelles fonctionnalités ou bien quand on les améliore, ou encore quand
on corrige des grosses boulettes.
• Z : nombre entier correspondant à un numéro de correctif. C'est pour de la petite boulette.

On pourra rajouter à la version

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 12/19


• -alpha (1.0.0-alpha : on est sur la bonne voix de sortir la v1, il faut juste corriger les bugs et
ajouter la carrosserie) ;
• -beta (1.0.0-beta : il faut tester à fond mais on est tout proche de la v1) ;
• -rc (1.0.0-rc, c'est la realease candidate, on est en v1 mais ça peut montrer qu'elle est toute
jeune quand même). Perso je ne suis pas fan de la nomenclature -rc, je la zappe.

Autre type de versionning :


• Pour les modules : ça peut être utile d'avoir un versionning particulier pour les modules
(test si cassure de rétro-compatibilité, etc). Par contre la nommenclature x.y.z ça embrouille
avec le version du logiciel et c'est pas pratique à tester, donc pour les modules j'utilise plutôt
W avec W un nombre entier de 1 à 54646465645644445.
• Pour les documents : c'est mieux d'avoir une nomenclature du document en plus de celle du
logiciel : on associe la doc à une version X.Y du soft ou X.* mais entre temps elle peut
aussi changer, donc elle va avoir une version L.Y avec L une lettre de a à z correspondant à
l'édition de la doc et Y un numéro de révision classique.

6.3 Commentaires dans les codes et documentation technique de référence


• Langage utilisé : anglais.
• Générateur de document à partir des commentaires : DOxygen
NB :
1/ installer graphviz pour faire des jolies diagrammes (sudo apt-get install graphviz)
2/ en python utiliser le filtre doxypy (sudo apt-get install doxypy), dans le fichier de configuration
doxygen du projet :
FILTER_SOURCE_FILES = YES
INPUT_FILTER = "python /usr/bin/doxypy.py"
OPTIMIZE_OUTPUT_JAVA = YES
EXTRACT_ALL = YES

Un exemple d'utilisation de DOxygen dans du code python :


# -*- coding: utf-8 -*-

#- ---------------------------------------------------------------------
## @file logerr.py
## @defgroup logerr logerr ←- pratique pour définir un module
## @brief Manage log, log file, traceback and exception
## @author Franck GABARROT,
## @date 2014
## @copyright CECILL-B
## @note
##- 2014/04/04: creation.
#- ---------------------------------------------------------------------

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 13/19


## @brief Available functions list to import
## @ingroup logerr ←- et pour inclure dans le module
__all__=["genexcep","format_trace_from_sys","format_trace_from_genexcep",\
"createLogger","debugmodeLogger","defaultmodeLogger",\
"closeLogger","closeallLogger","changefileLogger",\
"print2Logger"]

import os, sys, logging, logging.handlers, traceback

## @brief Module version (it is not the toolbox version).


## Integer value. Increment when you add an evolution, usefull to check compatibility.
## @ingroup logerr
__version__=1

# ---------------------------------------------------------------------
# @brief Create logger and create or open (if already exists) log file.
# @param[in] directory Log directory.
# @param[in] modulename Module name.
# @param[in] datetime String date and time information to add to the
# filename : modulename_datetime.log. Default: no datetime string.
# @param[in] rotate Log file automatic rotating file greating than maxsize,
# default: no rotating mode.
# @param[in] maxbytes Max file size for automatic rotating file (byte),
# default: 1 Gbyte.
# @param[in] backupcount Max number of log files archived.
# @return Logger object from python-logging module. Return None if an
# error occurs.
# @ingroup logerr
#- ---------------------------------------------------------------------
def createLogger(directory, modulename, datetime=None, rotate=False, maxbytes=1000000,
backupcount=10):
"""Create logger and create or open (if already exists) log file.
"""
logger=None

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 14/19


7. INTÉGRATION CONTINUE

Le développeur se doit de tester son code au niveau unitaire mais aussi la manière dont son code
s'intègre dans l'ensemble du projet. De même, la qualité du code doit être la plus constante possible
mais. L'éxecution de tous ces tests peut prendre un certain temps et c'est là qu'intervient la
plateforme d'intégration continue. Elle va s'occuper d'exécuter tous l'ensemble des tests permettant
de vérifier la qualité, les fonctions et l'intégration du code.
Nous utilisons ici Jenkins CI accessible à l'adresse suivante (serveur interne) :
http://10.82.61.249:8080

7.1 Jenkins Continuous Integration


Ce logiciel fonctionne sur le principe de tâches ou job. Un job est un ensemble d'instructions à
exécuter, déclenché par un événement (push sur dépôt, build). Le job porte le même nom que le
projet qu'il s'occupe de tester.

Figure 1: Vue principale de Jenkins CI résumant l'ensemble des jobs et leur état.

Dans un soucis d'économie de ressources physiques, les builds générés par Jenkins sont exécutés
au sein de conteneurs Docker (REST API 10.82.61.249:4243). L'ensemble de la sortie standard est
conservé dans la section « Console Output » de chaque build.
Actuellement, un e-mail de notification sera envoyé à chaque fois qu'un build aura échoué ou
qu'un job passe du statut instable ou statut stable à tous les développeurs du code pushé.
Lorsqu'on ajoute un nouveau job à Jenkins, il faut créer un environnement de test correspondant.
Si l'environnement est sur windows il faudra créer une machine virtuelle mais si celui ci est sur
Linux, on utilisera un conteneur Docker. Ce conteneur doit refleter l'installation réelle du projet sur
un environnement vierge.
Ajouter une image slave dans Docker
Pour cela vous devez créer un Dockerfile. Vous pourrez trouver des exemples de ce fichier dans
les dépôts.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 15/19


Tout d'abord créez un Dockerfile ou prenez en un disponible sur devspot.
git clone http://osur-devspot.univ-reunion.fr/ppayet/atmdata_slave.git

Nous allons maintenant ajouter cette image au serveur Docker de l'OSU-R :


$ cd atmdata_slave/atmdata_slave
$ docker -H=tcp://10.82.61.249:4243 build -t promise/atmdata_slave .
[…]
$ docker -H=tcp://10.82.61.249:4243 images
REPOSITORY TAG IMAGE ID CREATED
VIRTUAL SIZE
promise/atmdata_slave latest 84302898503e 4 weeks ago
960.9 MB
promise/miscbox_slave latest e519c83739a4 4 weeks ago
850.9 MB
jenkins latest 5da1f62066bc 5 weeks ago
707.4 MB
ubuntu latest 1d073211c498 7 weeks ago
187.9 MB
debian latest d1f66aef36c9 7 weeks ago
125.1 MB

Renseigner une image docker dans Jenkins


A partir de la vue principale allez dans Manage Jenkins > Configure System > Cloud > Docker et
ajouter un nouveau template.
Il faut remplir le champs Docker Image avec le même nom de l'image que vous venez
d'enregistrer dans le serveur Docker.
Le champs Remote Filing System Root doit renseigner le chemin où l'utilisateur jenkins va
travailler, dans notre cas /home/jenkins. Le champ labels permet de donner un nom qui sera utiliser
pour désigner l'image à utiliser dans les différents jobs.
Finalement, il faut lui fournir les identifiants qui vont permettre à jenkins de se connecter par SSH
au conteneur. Ces identifiants sont standard pour tous les conteneur : Jenkins slave builder.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 16/19


Créer un nouveau job
Depuis la vue principale de Jenkins, cliquez sur New Item. Donnez lui le même nom que le projet
sur lequel vous travaillez et sélectionnez Freestyle project. Vous arrivez alors sur la page de
configuration du job.
Dans la première section, il faut préciser la conteneur dans lequel jenkins va s'exécuter. Vous
utilisez ici le label que vous avez enregistrer au préalable :

Jenkins a besoin de savoir comment récupérer le code dans la section Source Code Management.
Les identifiants qui permettent à Jenkins de se connecter au gitlab portent le label « git » :

Pour que l'ensemble des tests soient exécuter à chaque push sur le dépôt, configurer les triggers :

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 17/19


Parallèlement, il faut se connecter au gitlab et configurer un web hook pour que Jenkins recoivent
les évènement de push.
Dans Settings > Web Hooks du dépôt correspondant, ajouter un nouveau web hook en copiant
l'adresse sur laquelle le gitlab va poster les évènements, ici
http://10.82.61.249:8080/project/lidarcommons.
La section Build comporte la liste des commandes a éxécuter lors du dit build. Il ne devrait y
avoir qu'une commande ici : tox. En effet, l'ensemble des tests et déjà configurer dans le tox.ini à la
racine du projet si vous avez suivi les recommandations précédentes (cf : #4.2.2.Tox|outline).

Finalement, ajouter une notification par e-mail dans les actions post-build :

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 18/19


8. LICENCE
Par défaut nous nous sommes actuellement orienté vers l'ensemble des licences CECILL
élaborées par le CNRS, l'INRIA et le CEA afin de développer des logiciels libres de droit français
dans l'esprit GNU GPL.
Voir http://www.cecill.info/.

OSU Réunion nt_ums3365_guide-developpement_promise_vb0 19/19

Vous aimerez peut-être aussi