Leçon: travail collaboratif dans un projet informatique¶

Le controle de version¶

Le contrôle de version (VCS), également appelé contrôle de source, désigne la pratique consistant à suivre et à gérer les changements apportés au code d'un logiciel

Leur utilisation est d'une pratique très répandue dans les équipes informatiques en général, en particulier dans le courant DevOps

Git¶

Git est de loin le VCS le utilisé aujourd'hui. Il a été à l'origine developpé en 2005 par Linus Torvalds et possède une grosse communauté de contributeurs et d'utilisateurs.
On peut citer certaines de ces propriétés (à retrouver ici pour plus de détail) :

  • il est gratuit et open source
  • c'est un VCS distribué (n'utilise pas un dépot centralisé)
  • fonctionne sur de nombreux systèmes d'exploitation
  • est intégré à beaucoup d'IDE
  • il possède beaucoup de fonctionnalité
  • sa courbe d'apprentissage peut être plus lente que pour d'autre VCS

Installation¶

Très souvent, git est déja pré-installé avec la plupart de vos systèmes d'exploitation (sauf widows ?). Si ca n'est pas le cas vous pouvez suivre les instructions d'installations de la doc officielle

Github¶

Github est un plateforme privée permettant aux developpeurs d'utiliser les fonctionnalités de git (et bien plus) avec une interface graphique et faciliter ainsi la collaboration entre différents membre d'un projet informatique. Bien que Github soit la plateforme la plus utilisée, il existe des plateformes similaires, elle aussi très utilisées comme Gitlab ou Bitbucket

Gitlab¶

Tout comme Github, Gitlab est une plafeforme de gestion de code utilisant git. Elle possède de nombreuses fonctionnalités équivalentes à Github, mais reste un peu moins populaire que celle-ci. Je recommande néanmoins son utilisation car son code est entièrement open-source et ses fonctionnalités pour l'intégration et le deploiement continue meilleure que celle de github

Dans cette leçon, les exemples que nous allons montrer concernent la plateforme Github, mais sont la plupart sont équivalents avec les fonctionnalités de gitlab Gitlab**

Dépot public et dépot privé¶

Depuis peu, les deux plateformes permettent la création illimitée de dépots public ou privés avec un compte gratuits

Les dépots publics sont visible par n'importe qui, alors que les dépots privés ne sont accessibles qu'aux personnes invitées par le créateur du dépot.

Scénario d'utilisation: je commence un projet¶

Créer un dépot local sur votre machine¶

En général, lorsque l'on commence un projet, on crée un dépot local sur son ordinateur. Il suffit pour cela de créer un dossier contenant le code de votre application et de l'initialiser avec git:

git init

Utiliser un template¶

vous pouvez également utiliser un générateur de paquet automatique comme cookiecutter, spécialisé pour la data science

Commencer à versionner¶

Une fois votre dépot local crée et que vous avez terminé une première contribution et que vous souhaiter l'enregistrer pour pouvoir la suivre. Vous pouvez suivre le worflow simple suivant:

git status # pour détecter les fichiers qui ont été modifiés
git add <file1> <file2> ... # pour enregistrer les fichiers que vous souhaitez suivre
git add . # pour enregistrer tous les fichiers qui ont été modifiés
git commit -m "message décrivant le changement apporté" # soumettre votre changement avec un message explicatif

Créer un dépot distant¶

Si vous souhaitez partager votre dépot lou collaborer avec d'autres contributeurs, vous pouvez créer un dépot distant sur Github

Pour interagir avec github, vous pouvez installer et utiliser leur interface en ligne de commande (cli) nommée gh

gh repo create # crée un dépot distant sur github

Vous pouvez aussi utiliser l'interface graphique à partir de votre compte

Vous devriez voir apparaitre votre dépot distant avec la commande:

git remote -v

Connexion entre depot local et distant¶

Afin d'établir la connexion entre votre dépot local et le dépot distant crée, vous devez ajouter un remote que l'on nomme souvent origin, par convention. Par exemple pour le projet project-template:

git remote add origin git@github.com:HerySon/project-template.git

Pousser son premier changement¶

Vous pouvez alors synchroniser votre commit local sur le dépot distant, en le poussant sur la branche master, crée par défaut:

git push origin master

Ajouter des collaborateurs¶

Pour ajouter des collaborateurs à un projet, vous devez les inviter, à partir de l'interface graphique du dépot concerné. Ces derniers recevront une invitation qu'ils devront accepter pour être intégré au projet.

Pour chaque utilisateur, le mainteneur du dépot peut choisir différentes permissions qui définiront ce que celui-ci à le droit de faire (en général récupérer et pousser des changements, ...). Plus de détails sur les permissions ici.

Scénario d'utilisation: je rejoins un projet¶

Cloner le dépot distant¶

Lorsque vous rejoignez un projet ayant un dépot distant déja existant, la première étape pour y contribuer consiste souvent à cloner ce dépot en local sur votre machine. Par exemple pour le depot project-template:

git clone git@github.com:HerySon/project-template.git

Au moment ou vous cloner le depot distant sur votre dépot local, vous serez momentanément à jour du projet. Mais dès que de nouvelles modifications seront poussées sur le dépot distant (que vous pourrez répérer par git status), il vous faudra mettre à jour ces modfications en local.

Récupérer les derniers changements¶

Par exemple pour récupérer les changements depuis la branche master:

git fetch origin master
git merge origin/master

ou bien directement avec git pull:

git pull origin master # correspond a fetch + merge ou fetch + rebase

Ensuite, vous pouvez suivre le worflow classique de travail (vu plus haut):

  • git status
  • git add
  • git commit -m
  • git push

Travail en équipe¶

Séparer le workflow en travaux parallélisable: Divide and conquer¶

Dans tous les projets informatiques et en particulier en data science, il faudra paralléliser votre chaine de traitement en travaux. Cela peut concerner des travaux comme:

  • Fabriquer une baseline avec un pre-traitement minimal et un modèle simple et stupide (par exemple répondre au hasard)
  • Faire une analyse exploratoire du dataset: utiliser indicateurs statistiques et des graphiques
  • Faire un pré-traitement plus avancé du dataset
  • Entraîner un modèle et mesurer ces performances
  • Faire de la sélection de features et du feature engineering
  • Preparer l'environnement de production

Découper chaque job en tâche élémentaire¶

Ensuite, vous allez découper chacun des travaux en tâches élementaires (sur laquelle une seule personne travaillera pour maximiser la distribution des tâches)

Par exemple pour la fabrication de la baseline, cela implique les tâches suivantes:

  • charger le dataset dans une structure de donnée (array, dataframe, graphe, ...)
  • calculer des indicateurs statistiques univariés pour ce faire une idée du dataset (moyenne, variance)
  • faire des graphiques adaptés pour visualiser ces indicateurs
  • ...

Utiliser un outil de gestionnaire de tâche¶

Afin de matérialiser et assurer le suivi de ces tâches, je vous recommande fortement d'utiliser un logiciel de gestion de tâche (par exemple Trello et de vous inspirer des principes de base des méthodes agile et scrum pour la gestion de projet

--> Exemple avec le projet de data science sur Open Food Fact

1 environnement virtuel par projet !¶

Afin d'isoler le code de votre projet et de n'y inclure que les librairies nécessaires à son fonctionnement, je vous recommande de créer 1 environnement virtuel par projet

1 tâche = 1 branche¶

Une fois les tâches réparties entre les différents collaborateurs,la bonne pratique recommande que chaque développeur crée une branche pour developper le code de la tâche en cours

La branche principale, souvent nommée master, est destinée à ne contenir que le code testé et fonctionnel pour votre logiciel, il ne faut pas pousser vos changements dessus avant qu'il n'aient été vérifiés !

Demander une revue de son travail : pull request¶

Une fois que le développeur à terminé le code correspondant à une tâche, il va demander a faire évaluer son code par un mainteneur, afin que celui-ci évalue le bon fonctionnement du code, avant de l'intégrer au travail de la branche principale.

Le développeur peut alors utiliser l'interface de github pour faire une pull request pouvant contenir:

  • titre et description de ce qu'il a fait
  • des sollicitations d'un utilisateur: @heryson, ...
  • des tags

Le mainteneur peuvent ensuite continuer à échanger des messages avant d'obtenir la version définitive des changements pour la tâche en cours de fusion.

La plupart des projets open source fonctionnent de cette façon: vous pouvez developper du code localement pour répondre à une tâche mais ne pouvez pas l'intégrer directement, il faudra "discuter" avec ses mainteneurs en faisant une pull request.
Voici par exemple les pull request de la librairie scikit-learn

Fusionner son travail : merge¶

Une fois votre travail accepté par un mainteneur, celui-ci fusionnera vos changement à la branche principale

Supprimer la branche pour votre tâche terminée¶

Une fois votre travail fusionné, vos changements ainsi que son historique ont été inclus dans la branche principale, vous n'avez alors plus besoin de votre branche courante

Les bonnes pratiques recommandent, pour ne pas surcharger le projet de *supprimer votre branche courante !

Itérer !¶

Le processus de travail de groupe continue ensuite par itération successive de chacun des contributeurs pour différentes tâches.

git status # examiner les changements
git checkout master # se placer sur la branche master
git pull origin master # recuperer ses changements sur master et les inclure en local

En particulier, dans la data science, la chaîne de traitement etant très itérative, il est fréquent d'avoir des tâches qui reviennent sur un même travail afin de l'affiner (par exemple le pré-traitement)

Vérifier les changements distants et les integrer à votre branche courante:¶

# 1/ Faire un commit des changement sur votre branche
(my-task) git add .
(my-task) git commit -m 'a meaningful message'
(my-task) git status # le status doit être clean !

# 2/ Récupérer dans ma branche my-task les changements depuis master
(my-task) git checkout master
(master)  git pull origin master

# 3/ Fusionner ces changements dans ma branche my-task
(master)  git checkout my-task
(my-task) git merge master

Continuer à travailler sur vos changements¶

(my-task) git add .
(my-task) git commit -m 'a meaningful message'
(my-task) git status # le status doit être clean !
(my-task) git push origin my-task #

Gérer un conflit¶

Dans certain cas, plusieurs développeurs vont modifier les même lignes d'un fichier en même temps ! Lors de la fusion de ces changements, git va automatiquement les détecter et vous signaler qu'il y a un conflit et vous ne pourrez pas faire de pull request et devez le résoudre d'abord !

git status 
git checkout master
git pull origin master          # fusionner les derniers changements depuis la branche master distante
git checkout unmergeable-branch # revenir sur votre branche
git merge master                # fusionner les changements de votre master local a votre branche courante

# 😱 Github vous signale un conflit
# Solution:
# ouvrez un editeur de texte sur le fichier en conflit er réprerer les lignes conflictuelles annotées par des chevrons `<<<<<` et `>>>>` 
# arbitrez pour garder les changements pertinents 
# resolvez le conflit

git add .                           # ajouter les fichiers avec le conflit résolu
git commit --no-edit                # faites un commit sans message
git push origin unmergeable-branch  # pousser les changements sur votre branche distante

Vous pouvez également utiliser l'interace graphique de github pour résoudre votre conflit (ce qui est généralement plus lisible)

Cas des notebook¶

Difficile de comparer les changements sur des notebooks¶

Les notebooks sont des généralement sauvegardés au format json, de fait il sera plus compliqué de visualiser les changements qu'on leur a apporté (surtout s'il sont volumineux):

  • l'interface de Github fera un rendu appauvri et souvent lent de vos notebook
  • la diff sera difficiement compréhensible (car elle sera affichée sur le json)

Solutions¶

  • Créer un notebook par développeur et lui ajoutant un suffixe: dataviz_heryson.ipynb
  • utiliser un package dédié comme ndime pour visualiser la diff: nbdime diff-web base_notebook.ipynb updated_notebook.ipynb

Cas des gros dataset¶

Pour des raisons d'efficacité, github limite la taille des fichiers versionables à 100Mb. Vous devrez donc utiliser d'autres outils pour le stockage de vos gros data set

En conséquence, si votre dépot contient de tes fichiers, il faudra indiquer à git que de ne pas les versionner en l'ajoutant à la liste des fichiers à ignorer, le .gitignore

touch .gitignore                     # crée le fichier .gitignore
echo big_dataset.csv >> .gitignore   # ajouter big_dataset.csv au .gitignore
git status                           # vérifie que big_dataset.csv n'est pas pris en compte

Gérer ses clé d'indentification¶

Les écrire en dur ?¶

Parfois, vous serez amenés a utiliser dans votre code des clés d'identification à ne pas divulguer, par exemple les clés pour une API:

In [ ]:
import geocoder

def geocode(address):
    api_key = 'pk.eqdlqkjflmsdkjfsddfkmlkam83MjVrbWkwbWNoM3FwN2VhMm81eGRzIn0.yM3wkq5LJd8Nsdflmksfnvn'
    g = geocoder.mapbox(address, key=api_key)
    return (g.json['lat'], g.json['lng'])import geocoder

Problème: si vous poussez ce code sur un dépot public, la clé sera visible par tous

Solution: utiliser le package python-dotenv¶

Ce package permet de stocker vos clés d'identification dans un fichier suivant une logique clé/valeur:

In [2]:
# stocke cette variable d'environnement dans le fichier ./.env (à mettre à la racine de votre projet)
API_KEY='pk.eqdlqkjflmsdkjfsddfkmlkam83MjVrbWkwbWNoM3FwN2VhMm81eGRzIn0.yM3wkq5LJd8Nsdflmksfnvn'

Créer un fichier .env à partager à vos collaborateurs:

echo 'python-dotenv' >> requirements.txt # ajoute le package à la liste des dépendances  
touch .env                               # crée un fichier .env vide dans lequel vous copiez votre clé
echo '.env' >> .gitignore                # n'oubliez pas de l'ajouter à la liste de fichiers a ignorer par Github !!!

Ensuite votre collaborateur peut utiliser le package pour charger votre fichier .env contenant votre clé :

In [ ]:
from os.path import join, dirname
from dotenv import load_dotenv
import your_package

# récupère le path de votre fichier .env depuis la racine de your_package
env_path = join(dirname(dirname(your_parckage.__file__)),'.env') 

# charge le fichier .env contenant votre variable d'environnement
load_dotenv(dotenv_path=env_path)                                

Et peux utiliser la variable d'environnement contenant la clé:

In [ ]:
import geocoder

def geocode(address):
    api_key = os.getenv(API_KEY)
    g = geocoder.mapbox(address, key=api_key)
    return (g.json['lat'], g.json['lng'])import geocoder

Sources¶

  • Le tutoriel de Git par Atlassian: https://www.atlassian.com/git/tutorials/setting-up-a-repository
  • Explication du contrôle de version par Atlassian : https://www.atlassian.com/fr/git/tutorials/what-is-version-control
  • Explication de Git par Atlassian: https://www.atlassian.com/fr/git/tutorials/what-is-git
  • la documentation officielle de Git: https://git-scm.com/doc