641 votes

Utilisation d'IPython / Jupyter Notebooks sous contrôle de version

Quelle est une bonne stratégie pour garder IPython des carnets de notes sous contrôle de version ?

Le format du carnet de notes se prête bien au contrôle de version : si l'on veut contrôler la version du carnet de notes et des résultats, cela fonctionne très bien. L'ennui vient lorsque l'on veut seulement contrôler la version de l'entrée, à l'exclusion des sorties de la cellule (alias "produits de construction") qui peuvent être de gros blobs binaires, en particulier pour les films et les intrigues. En particulier, j'essaie de trouver un bon flux de travail qui :

  • me permet de choisir entre inclure ou exclure la sortie,
  • m'empêche de commettre accidentellement des sorties si je ne le souhaite pas,
  • me permet de conserver la sortie dans ma version locale,
  • me permet de voir quand j'ai des changements dans les entrées en utilisant mon système de contrôle de version (i.e. si je ne contrôle que les entrées mais que mon fichier local a des sorties, alors j'aimerais pouvoir voir si les entrées ont changé (ce qui nécessite un commit). L'utilisation de la commande d'état du contrôle de version enregistrera toujours une différence puisque le fichier local a des sorties).
  • me permet de mettre à jour mon cahier de travail (qui contient la sortie) à partir d'un cahier propre mis à jour. (mise à jour)

Comme mentionné, si je choisis d'inclure les sorties (ce qui est souhaitable lorsque l'on utilise des nbviewer par exemple), alors tout va bien. Le problème est que lorsque je ne pas vous voulez contrôler la version de la sortie. Il existe quelques outils et scripts pour dépouiller la sortie du carnet de notes, mais je rencontre fréquemment les problèmes suivants :

  1. J'ai accidentellement commit une version avec la sortie, polluant ainsi mon référentiel.
  2. J'efface la sortie pour utiliser le contrôle de version, mais je préférerais vraiment garder la sortie dans ma copie locale (il faut parfois un certain temps pour reproduire par exemple).
  3. Certains des scripts qui dépouillent la sortie modifient légèrement le format par rapport à l'option Cell/All Output/Clear ce qui crée un bruit indésirable dans les diffs. Ce problème est résolu par certaines des réponses.
  4. Lorsque j'apporte des modifications à une version propre du fichier, je dois trouver un moyen d'incorporer ces modifications dans mon carnet de travail sans avoir à tout réexécuter. (mise à jour)

J'ai envisagé plusieurs options dont je parlerai ci-dessous, mais je n'ai pas encore trouvé de bonne solution globale. Une solution complète pourrait nécessiter quelques changements dans IPython, ou pourrait s'appuyer sur quelques simples scripts externes. J'utilise actuellement mercuriel mais j'aimerais une solution qui fonctionne aussi avec git Une solution idéale serait agnostique au contrôle de version.

Cette question a été discutée à de nombreuses reprises, mais il n'existe pas de solution définitive ou claire du point de vue de l'utilisateur. La réponse à cette question devrait fournir la stratégie définitive. Ce n'est pas grave si cela nécessite une version récente (même de développement) de IPython ou une extension facile à installer.

Mise à jour : J'ai joué avec mon carnet de notes modifié qui enregistre éventuellement une .clean avec chaque sauvegarde utilisant Les suggestions de Gregory Crosswhite . Cela satisfait la plupart de mes contraintes mais laisse les suivantes non résolues :

  1. Cette solution n'est pas encore standard (elle nécessite une modification du code source d'ipython). Existe-t-il un moyen d'obtenir ce comportement avec une simple extension ? Il faut une sorte de crochet on-save.
  2. Un problème que je rencontre avec le flux de travail actuel est de tirer les modifications. Ceux-ci arrivent dans le .clean et doivent ensuite être intégrés d'une manière ou d'une autre dans ma version de travail. (Bien sûr, je peux toujours réexécuter le notebook, mais cela peut être pénible, surtout si certains des résultats dépendent de longs calculs, de calculs en parallèle, etc. Peut-être qu'un flux de travail impliquant une extension comme ipycache pourrait fonctionner, mais cela semble un peu trop compliqué.

Notes

Retrait (stripping) Sortie

  • Lorsque le notebook est en cours d'exécution, on peut utiliser la fonction Cell/All Output/Clear pour supprimer la sortie.
  • Il existe quelques scripts pour supprimer la sortie, comme le scripts. nbstripout.py qui supprime la sortie, mais ne produit pas la même sortie qu'en utilisant l'interface du notebook. Ceci a finalement été inclus dans la version ipython/nbconvert mais il a été clôturé en indiquant que les modifications sont maintenant incluses dans le répertoire de l'entreprise. ipython/ipython mais la fonctionnalité correspondante ne semble pas encore avoir été incluse. (mise à jour) Ceci étant dit, La solution de Gregory Crosswhite montre que cela est assez facile à faire, même sans invoquer ipython/nbconvert Cette approche est donc probablement réalisable si elle peut être connectée correctement. (L'attacher à chaque système de contrôle de version, cependant, ne semble pas être une bonne idée - cela devrait d'une manière ou d'une autre s'accrocher au mécanisme du carnet de notes).

Groupes de discussion

Questions

Pull Requests

0 votes

Cela semble être une bonne chose à ajouter en tant que problème sur le site Web de la Commission européenne. github.com/ipython/ipython ou soumettez une demande de retrait qui vous aide à atteindre cet objectif.

0 votes

Comme vous pouvez le constater, il existe déjà une pléthore de relations publiques et de questions relatives à cet objectif. Une fois ceux-ci résolus (à savoir PR 4175 ), alors une réponse définitive devrait être disponible, mais elle impliquera probablement des scripts supplémentaires en dehors d'IPython (des hooks git ou hg par exemple). Par conséquent, je ne pense pas qu'il y aura quoi que ce soit à gagner en ajoutant un nouveau PR ou problème.

0 votes

Oui, leur développement progresse rapidement et régulièrement chaque jour. Mais les développeurs sont de bonnes personnes (et ont probablement lu ce message). Je sais que je veux un flux de travail facile pour travailler avec git.

4voto

amirathi Points 86

Nous sommes en avril-2020 et il existe de nombreuses stratégies et outils pour le contrôle de version du carnet Jupyter. Voici un aperçu rapide de tous les outils que vous pouvez utiliser,

  • nbdime - Sympathique pour la diff'erence locale et la fusion de carnets de notes

  • nbstripout - Un filtre git pour supprimer automatiquement les sorties de notebook avant chaque commit

  • jupytext - Conserve un fichier compagnon .py synchronisé avec chaque ordinateur portable. Vous ne commettez que des fichiers .py

  • nbconvert - Convertissez les carnets de notes en un script python ou HTML (ou les deux) et engagez ces types de fichiers alternatifs

  • CritiqueNB - Affiche le diff du carnet de notes (avec la sortie) pour tout commit ou pull request sur GitHub. On peut également écrire des commentaires sur les cellules du bloc-notes pour discuter des changements (capture d'écran ci-dessous).

enter image description here

Disclaimer : J'ai construit ReviewNB.

3voto

Guillaume Dumas Points 31

Pour faire suite à l'excellent script de Pietro Battiston, si vous obtenez une erreur d'analyse Unicode comme celle-ci :

Traceback (most recent call last):
  File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
  File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)

Vous pouvez ajouter au début du script :

reload(sys)
sys.setdefaultencoding('utf8')

3voto

Brian Funt Points 99

Après avoir creusé un peu, j'ai finalement trouvé ce hook pré-save relativement simple sur les docs Jupyter . Il dépouille les données de sortie de la cellule. Vous devez le coller dans le champ jupyter_notebook_config.py (voir ci-dessous pour les instructions).

def scrub_output_pre_save(model, **kwargs):
    """scrub output before saving notebooks"""
    # only run on notebooks
    if model['type'] != 'notebook':
        return
    # only run on nbformat v4
    if model['content']['nbformat'] != 4:
        return

    for cell in model['content']['cells']:
        if cell['cell_type'] != 'code':
            continue
        cell['outputs'] = []
        cell['execution_count'] = None
        # Added by binaryfunt:
        if 'collapsed' in cell['metadata']:
            cell['metadata'].pop('collapsed', 0)

c.FileContentsManager.pre_save_hook = scrub_output_pre_save

De La réponse de Rich Signell :

Si vous n'êtes pas sûr dans quel répertoire trouver votre jupyter_notebook_config.py vous pouvez taper jupyter --config-dir (dans l'invite de commande/terminal), et si vous ne trouvez pas le fichier à cet endroit, vous pouvez le créer en tapant jupyter notebook --generate-config .

1voto

Peter Points 121

J'ai fait ce qu'Albert et Rich ont fait - Ne faites pas de version des fichiers .ipynb (car ils peuvent contenir des images, ce qui est désordonné). A la place, soit vous exécutez toujours ipython notebook --script ou mettre c.FileNotebookManager.save_script = True dans votre fichier de configuration, de manière à ce qu'une (versionable) .py est toujours créé lorsque vous enregistrez votre cahier.

Pour régénérer les carnets (après avoir vérifié un repo ou changé de branche), je mets le script. py_file_to_notebooks.py dans le répertoire où je stocke mes carnets.

Maintenant, après avoir vérifié un repo, il suffit d'exécuter python py_file_to_notebooks.py pour générer les fichiers ipynb. Après avoir changé de branche, vous devrez peut-être exécuter python py_file_to_notebooks.py -ov pour écraser les fichiers ipynb existants.

Juste pour être sûr, il est bon d'ajouter également *.ipynb à votre .gitignore fichier.

Edit : Je ne le fais plus car (A) il faut régénérer les notebooks à partir des fichiers py à chaque fois que l'on extrait une branche et (B) on perd d'autres éléments comme le markdown dans les notebooks. A la place, j'enlève la sortie des notebooks en utilisant un filtre git. La discussion sur la façon de faire ceci est aquí .

1voto

Peter Points 121

Ok, donc il semble que la meilleure solution actuelle, selon une discussion aquí est de créer un filtre git pour retirer automatiquement la sortie des fichiers ipynb lors de la validation.

Voici ce que j'ai fait pour le faire fonctionner (copié de cette discussion) :

J'ai modifié légèrement le fichier nbstripout de cfriedline pour donner une erreur informative quand on ne peut pas importer la dernière version d'IPython : https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output Et je l'ai ajouté à mon repo, disons dans ./relative/path/to/strip_notebook_output

J'ai également ajouté le fichier .gitattributes à la racine du repo, contenant :

*.ipynb filter=stripoutput

Et a créé un setup_git_filters.sh contenant

git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output" 
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true

Et a couru source setup_git_filters.sh . Le truc de $(git rev-parse...) est de trouver le chemin local de votre repo sur n'importe quelle machine (Unix).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X