Utilisation de blocs-notes IPython sous contrôle de version

569

Quelle est la bonne stratégie pour garder les blocs-notes IPython sous contrôle de version?

Le format du notebook est tout à fait adapté au contrôle de version: si l'on veut contrôler la version du notebook et des sorties, cela fonctionne très bien. L'ennui vient quand on veut seulement contrôler la version de l'entrée, à l'exclusion des sorties de cellule (aka. "Construire des produits") 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 une sortie si je ne le veux 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 (c'est-à-dire si je ne contrôle que la version des entrées mais que mon fichier local a des sorties, alors je voudrais pouvoir voir si les entrées ont changé (nécessitant une validation 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 j'ai choisi d'inclure les sorties (ce qui est souhaitable lors de l'utilisation de nbviewer par exemple), alors tout va bien. Le problème est quand je ne veux pas contrôler la version de la sortie. Il existe des outils et des scripts pour supprimer la sortie du bloc-notes, mais je rencontre fréquemment les problèmes suivants:

  1. Je valide accidentellement 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ère vraiment garder la sortie dans ma copie locale (parfois cela prend un certain temps pour se reproduire par exemple).
  3. Certains des scripts qui suppriment la sortie modifient légèrement le format par rapport à l' Cell/All Output/Clearoption de menu, créant ainsi du bruit indésirable dans les diffs. Ceci est résolu par certaines des réponses.
  4. Lors de l'extraction de modifications dans une version propre du fichier, je dois trouver un moyen d'incorporer ces modifications dans mon cahier de travail sans avoir à tout relancer. (mise à jour)

J'ai examiné plusieurs options que j'examinerai ci-dessous, mais je n'ai pas encore trouvé de bonne solution globale. Une solution complète peut nécessiter certaines modifications d'IPython, ou peut s'appuyer sur des scripts externes simples. J'utilise actuellement mercurial , mais j'aimerais une solution qui fonctionne également avec git : une solution idéale serait l'agnostic de contrôle de version.

Ce problème a été discuté à plusieurs reprises, mais il n'y a 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. C'est bien si cela nécessite une version récente (même de développement) d' IPython ou une extension facilement installée.

Mise à jour: j'ai joué avec ma version de bloc-notes modifiée qui enregistre éventuellement une .cleanversion à chaque sauvegarde en utilisant les suggestions de Gregory Crosswhite . Cela satisfait la plupart de mes contraintes mais laisse les éléments suivants non résolus:

  1. Ce n'est pas encore une solution standard (nécessite une modification de la source ipython. Existe-t-il un moyen d'obtenir ce comportement avec une simple extension? A besoin d'une sorte de hook de sauvegarde.
  2. Un problème que j'ai avec le flux de travail actuel tire des modifications. Ceux-ci viendront dans le .cleanfichier, et devront 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 bloc-notes, mais cela peut être pénible, surtout si certains des résultats dépendent de longs calculs, de calculs parallèles, etc.) Je n'ai pas encore une bonne idée de la façon de résoudre ce problème . Peut-être qu'un flux de travail impliquant une extension comme ipycache pourrait fonctionner, mais cela semble un peu trop compliqué.

Remarques

Suppression (suppression) de sortie

  • Lorsque le portable est en cours d'exécution, on peut utiliser l' Cell/All Output/Clearoption de menu pour supprimer la sortie.
  • Il existe certains scripts pour supprimer la sortie, tels que le script nbstripout.py qui supprime la sortie, mais ne produit pas la même sortie que l'utilisation de l'interface du bloc-notes. Cela a finalement été inclus dans le dépôt ipython / nbconvert , mais cela a été fermé indiquant que les modifications sont maintenant incluses dans ipython / ipython , mais la fonctionnalité correspondante ne semble pas encore avoir été incluse. (mise à jour) Cela étant dit, la solution de Gregory Crosswhite montre que c'est assez facile à faire, même sans invoquer ipython / nbconvert, donc cette approche est probablement réalisable si elle peut être correctement connectée.

Groupes de discussion

Problèmes

Demandes de tirage

mforbes
la source
Cela semble être une bonne chose à ajouter en tant que problème sur github.com/ipython/ipython ou à soumettre une demande d'extraction qui vous aide à atteindre cet objectif.
Kyle Kelley
4
Une fois que vous avez un script de travail pour supprimer la sortie, vous pouvez utiliser un filtre "propre" Git pour l'appliquer automatiquement avant de valider (voir filtres de nettoyage / maculage).
Matthias
1
@foobar barbecue La question contient des contournements insatisfaisants: chacun a au moins une limitation. Maintenant que le PR 4175 a été fusionné, une solution complète peut probablement être formulée, mais cela reste à faire. Dès que j'aurai du temps, je le ferai (comme réponse) si quelqu'un d'autre ne fournit pas de solution satisfaisante entre-temps.
mforbes
1
@saroele Je n'ai pas encore trouvé de solution recommandée: j'allais opter pour l' --scriptoption, mais elle a été supprimée. J'attends jusqu'à ce que les hooks post-sauvegarde soient implémentés ( qui sont prévus ) à quel point je pense que je serai en mesure de fournir une solution acceptable combinant plusieurs des techniques.
mforbes
1
@mforbes On dirait que le PR vient d'être fusionné quelques jours après votre commentaire. Pourriez-vous, vous ou quelqu'un de mieux informé que moi, publier ici une réponse qui montre comment utiliser la nouvelle fonctionnalité?
KobeJohn

Réponses:

124

Voici ma solution avec git. Il vous permet simplement d'ajouter et de valider (et de différer) comme d'habitude: ces opérations ne modifieront pas votre arborescence de travail, et en même temps (re) exécuter un notebook ne modifiera pas votre historique git.

Bien que cela puisse probablement être adapté à d'autres VCS, je sais que cela ne répond pas à vos exigences (au moins l'agnosticité VSC). Pourtant, il est parfait pour moi, et bien que ce ne soit rien de particulièrement brillant, et que beaucoup de gens l'utilisent probablement déjà, je n'ai pas trouvé d'instructions claires sur la façon de le mettre en œuvre en parcourant Google. Cela peut donc être utile à d'autres personnes.

  1. Enregistrez un fichier avec ce contenu quelque part (pour ce qui suit, supposons ~/bin/ipynb_output_filter.py)
  2. Rendez-le exécutable ( chmod +x ~/bin/ipynb_output_filter.py)
  3. Créez le fichier ~/.gitattributes, avec le contenu suivant

    *.ipynb    filter=dropoutput_ipynb
    
  4. Exécutez les commandes suivantes:

    git config --global core.attributesfile ~/.gitattributes
    git config --global filter.dropoutput_ipynb.clean ~/bin/ipynb_output_filter.py
    git config --global filter.dropoutput_ipynb.smudge cat
    

Terminé!

Limites:

  • ça marche seulement avec git
  • dans git, si vous êtes en branche somebranchet que vous le faites git checkout otherbranch; git checkout somebranch, vous vous attendez généralement à ce que l'arbre de travail soit inchangé. Ici, vous aurez perdu la sortie et la numérotation des cellules des blocs-notes dont la source diffère entre les deux branches.
  • plus généralement, la sortie n'est pas du tout versionnée, comme avec la solution de Gregory. Afin de ne pas simplement le jeter à chaque fois que vous effectuez une opération impliquant une extraction, l'approche peut être modifiée en la stockant dans des fichiers séparés (mais notez qu'au moment où le code ci-dessus est exécuté, l'ID de validation n'est pas connu!), et éventuellement les versionner (mais notez que cela nécessiterait quelque chose de plus qu'un git commit notebook_file.ipynb, même si cela permettrait au moins de se git diff notebook_file.ipynbdébarrasser des ordures de base64).
  • cela dit, incidemment, si vous tirez du code (c'est-à-dire commis par quelqu'un d'autre n'utilisant pas cette approche) qui contient une sortie, la sortie est extraite normalement. Seule la production produite localement est perdue.

Ma solution reflète le fait que personnellement je n'aime pas garder les éléments générés versionnés - notez que faire des fusions impliquant la sortie est presque garanti d'invalider la sortie ou votre productivité ou les deux.

ÉDITER:

  • si vous adoptez la solution telle que je l'ai suggérée - c'est-à-dire, globalement - vous aurez des problèmes au cas où vous auriez besoin d'une version git repo . Donc, si vous souhaitez désactiver le filtrage de sortie pour un référentiel git spécifique, créez simplement à l'intérieur un fichier .git / info / attributes , avec

    **. filtre ipynb =

comme contenu. En clair, de la même manière il est possible de faire l'inverse: activer le filtrage uniquement pour un référentiel spécifique.

  • le code est maintenant conservé dans son propre dépôt git

  • si les instructions ci-dessus aboutissent à ImportErrors, essayez d'ajouter "ipython" avant le chemin du script:

    git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
    

EDIT : mai 2016 (mis à jour en février 2017): il existe plusieurs alternatives à mon script - pour être complet, voici une liste de celles que je connais: nbstripout ( autres variantes ), nbstrip , jq .

Pietro Battiston
la source
2
Comment gérez-vous la question de l'intégration des changements que vous tirez? Vivez-vous simplement d'avoir à régénérer toute la sortie? (Je pense que c'est une manifestation de votre deuxième limitation.)
mforbes
1
@zhermes: cette version étendue devrait être OK
Pietro Battiston
1
Existe-t-il un moyen d'utiliser cette méthode de filtres git avec un outil de diff externe? Le filtre est appliqué si j'utilise l'outil de ligne de commande normal mais pas si j'utilise meld comme outil de diff. stackoverflow.com/q/30329615/578770
FA
1
Pour éviter de ImportErrorme faire, j'ai dû modifier ce qui précède pour exécuter en utilisant ipython:git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
chris838
1
Super solution Pietro, merci :) J'ai changé 2 choses lors de l'utilisation de votre script dans mon cas: 1) J'ai préféré déclarer le filtre en .gitattributes à la racine du dépôt par opposition à ~/.gitattributes, les autres personnes ont les mêmes filtres que moi 2 ) J'ai défini l'expression rationnelle comme workdir/**/*.ipynb filter=dropoutput_ipynb, et je mets la plupart de mes blocs-notes dans workdir / => si je veux toujours pousser un bloc-notes avec la sortie et profiter du rendu pouvant être marqué dans github, je le mets juste en dehors de ce dossier.
Svend
63

Nous avons un projet collaboratif où le produit est Jupyter Notebooks, et nous utilisons une approche pour les six derniers mois qui fonctionne très bien: nous activons l' .pyenregistrement automatique des fichiers et suivons les .ipynbfichiers et les .pyfichiers.

De cette façon, si quelqu'un veut afficher / télécharger le dernier bloc-notes, il peut le faire via github ou nbviewer, et si quelqu'un veut voir comment le code du bloc-notes a changé, il peut simplement regarder les modifications apportées aux .pyfichiers.

Pour les Jupyterserveurs d'ordinateurs portables , cela peut être accompli en ajoutant les lignes

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

dans le jupyter_notebook_config.pyfichier et redémarrer le serveur de bloc-notes.

Si vous ne savez pas dans quel répertoire trouver votre jupyter_notebook_config.pyfichier, vous pouvez taper jupyter --config-diret si vous n'y trouvez pas le fichier, vous pouvez le créer en tapant jupyter notebook --generate-config.

Pour les Ipython 3serveurs d'ordinateurs portables , cela peut être accompli en ajoutant les lignes

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

dans le ipython_notebook_config.pyfichier et redémarrer le serveur de bloc-notes. Ces lignes proviennent d'une réponse aux problèmes github @minrk fournie et @dror les inclut également dans sa réponse SO.

Pour les Ipython 2serveurs d'ordinateurs portables , cela peut être accompli en démarrant le serveur en utilisant:

ipython notebook --script

ou en ajoutant la ligne

c.FileNotebookManager.save_script = True

dans le ipython_notebook_config.pyfichier et redémarrer le serveur de bloc-notes.

Si vous ne savez pas dans quel répertoire trouver votre ipython_notebook_config.pyfichier, vous pouvez taper ipython locate profile defaultet si vous n'y trouvez pas le fichier, vous pouvez le créer en tapant ipython profile create.

Voici notre projet sur github qui utilise cette approche : et voici un exemple github d'exploration des modifications récentes d'un bloc-notes .

Nous en sommes très satisfaits.

Rich Signell
la source
1
Merci pour la preuve supplémentaire que l'utilisation --scripta fonctionné dans la pratique. Le problème est que les cahiers réels peuvent être énormes si les images sont conservées. Une solution idéale dans ce sens pourrait utiliser quelque chose comme git-annex pour garder une trace uniquement du dernier bloc-notes complet.
mforbes
Dans Ipython 3.x, le --scriptest obsolète. ipython.org/ipython-doc/3/whatsnew/version3.html
Dror
Merci @dror, j'ai mis à jour ma réponse pour fournir la solution ipython 3.x de minrk comme vous l'avez également fournie ici.
Rich Signell
10
Mise à jour: Cette solution est cassée dans iPython version 4, à cause de "The Big Split" de Jupyter d'iPython. Pour ajuster cette solution à la version 4, utilisez la commande jupyter notebook --generate-configpour créer un fichier de configuration. La commande jupyter --config-dirdécouvre quel répertoire contient les fichiers de configuration. Et l'extrait de code donné par @Rich doit être ajouté au fichier nommé jupyter_notebook_config.py. Le reste fonctionne comme avant.
mobius boulette
2
En plus du point par @mobiusdumpling, remplacez le check_call(['ipython'par check_call(['jupyter', sinon vous obtiendrez un avertissement ipython nbconvertobsolète et vous devriez utiliser à la jupyter nbconvertplace. (Jupyter v4.1.0, iPython v4.1.2)
cutculus
36

J'ai créé nbstripout, basé sur MinRKs gist , qui prend en charge Git et Mercurial (merci à mforbes). Il est destiné à être utilisé de manière autonome sur la ligne de commande ou comme filtre, qui est facilement (dés) installé dans le référentiel actuel via nbstripout install/ nbstripout uninstall.

Obtenez-le de PyPI ou simplement

pip install nbstripout
kynan
la source
J'envisage un workflow dans lequel je conserve à la fois .ipynb et .py correspondant créés automatiquement à l'aide des hooks post-sauvegarde décrits ci-dessus. Je voudrais utiliser .py pour les différences - nbstripout pourrait-il effacer le fichier .py des compteurs d'exécution de cellule (# In [1] changé en In [*]), afin qu'ils n'encombrent pas les différences ou devrais-je créer un script simple pour le faire?
Krzysztof Słowiński
1
@ KrzysztofSłowiński Non, nbstripoutne prend pas facilement en charge ce cas d'utilisation car il repose sur le format JSON du Notebook. Vous feriez probablement mieux d'écrire un script spécialisé dans votre cas d'utilisation.
kynan
13

Voici une nouvelle solution de Cyrille Rossant pour IPython 3.0, qui persiste pour démarquer les fichiers plutôt que les fichiers ipymd basés sur json:

https://github.com/rossant/ipymd

Spencer Boucher
la source
Il ne soutient pas encore Jupyter, semble-t-il.
K.-Michael Aye
J'utilise ipymd avec succès avec le dernier Jupyter - obtenez-vous un problème spécifique ou un message d'erreur?
Cyrille Rossant
13

Après quelques années de suppression des sorties dans les ordinateurs portables, j'ai essayé de trouver une meilleure solution. J'utilise maintenant Jupytext , une extension pour Jupyter Notebook et Jupyter Lab que j'ai conçue.

Jupytext peut convertir les blocs-notes Jupyter en différents formats de texte (scripts, Markdown et R Markdown). Et inversement. Il offre également la possibilité d' associer un bloc-notes à l'un de ces formats et de synchroniser automatiquement les deux représentations du bloc-notes (un .ipynbet un .md/.py/.Rfichier).

Permettez-moi d'expliquer comment Jupytext répond aux questions ci-dessus:

me permet de choisir entre inclure ou exclure la sortie,

Le .md/.py/.Rfichier contient uniquement les cellules d'entrée. Vous devez toujours suivre ce fichier. Versionnez le .ipynbfichier uniquement si vous souhaitez suivre les sorties.

m'empêche de commettre accidentellement une sortie si je ne le veux pas,

Ajouter *.ipynbà.gitignore

me permet de conserver la sortie dans ma version locale,

Les sorties sont conservées dans le .ipynbfichier (local)

me permet de voir quand j'ai des changements dans les entrées en utilisant mon système de contrôle de version (c'est-à-dire si je contrôle uniquement la version des entrées mais que mon fichier local a des sorties, alors je voudrais pouvoir voir si les entrées ont changé (nécessitant une validation) L'utilisation de la commande d'état du contrôle de version enregistrera toujours une différence car le fichier local a des sorties.)

Le diff sur le fichier .py/.Rou .mdest ce que vous recherchez

me permet de mettre à jour mon cahier de travail (qui contient la sortie) à partir d'un cahier propre mis à jour. (mise à jour)

Tirez la dernière révision du fichier .py/.Rou .mdet actualisez votre bloc-notes dans Jupyter (Ctrl + R). Vous obtiendrez les dernières cellules d'entrée du fichier texte, avec les sorties correspondantes du .ipynbfichier. Le noyau n'est pas affecté, ce qui signifie que vos variables locales sont préservées - vous pouvez continuer à travailler là où vous l'avez laissé.

Ce que j'aime avec Jupytext, c'est que le cahier (sous la forme d'un .py/.Rou d' un .mdfichier) peut être édité dans votre IDE préféré. Avec cette approche, la refactorisation d'un ordinateur portable devient facile. Une fois que vous avez terminé, il vous suffit de rafraîchir le bloc-notes dans Jupyter.

Si vous voulez l'essayer: installez Jupytext avec pip install jupytextet redémarrez votre Jupyter Notebook ou Lab Editor. Ouvrez le bloc-notes dont vous souhaitez contrôler la version et associez-le à un fichier Markdown (ou à un script) à l'aide du menu Jupytext du bloc-notes Jupyter (ou des commandes Jupytext de Jupyter Lab). Enregistrez votre bloc-notes et vous obtiendrez les deux fichiers: l'original .ipynb, plus la représentation textuelle promise du bloc-notes, qui convient parfaitement au contrôle de version!

Pour ceux qui pourraient être intéressés: Jupytext est également disponible sur la ligne de commande .

Marc Wouts
la source
13

Mise à jour : vous pouvez désormais modifier les fichiers du bloc-notes Jupyter directement dans Visual Studio Code. Vous pouvez choisir de modifier le bloc-notes ou le fichier python converti.

J'ai finalement trouvé un moyen simple et productif de faire en sorte que Jupyter et Git jouent bien ensemble. J'en suis encore aux premiers pas, mais je pense déjà que c'est beaucoup mieux que toutes les autres solutions alambiquées.

Visual Studio Code est un éditeur de code open source sympa de Microsoft. Il a une excellente extension Python qui vous permet désormais d' importer un bloc-notes Jupyter en tant que code python. Maintenant, vous pouvez également modifier directement les blocs-notes Jupyter .

Après avoir importé votre bloc-notes dans un fichier python, tout le code et le démarquage seront réunis dans un fichier python ordinaire, avec des marqueurs spéciaux dans les commentaires. Vous pouvez voir dans l'image ci-dessous:

Éditeur VSCode avec un bloc-notes converti en python

Votre fichier python n'a que le contenu des cellules d'entrée du bloc-notes. La sortie sera générée dans une fenêtre divisée. Vous avez du code pur dans le cahier, il ne change pas pendant que vous l'exécutez. Aucune sortie mélangée avec votre code. Pas de format JSON étrange et incompréhensible pour analyser vos différences.

Juste du code python pur où vous pouvez facilement identifier chaque diff.

Je n'ai même plus besoin de versionner mes .ipynbfichiers. Je peux mettre une *.ipynbligne dedans .gitignore.

Besoin de générer un cahier à publier ou à partager avec quelqu'un? Pas de problème, cliquez simplement sur le bouton d'exportation dans la fenêtre interactive de python

Exportation d'un fichier python au format Notebook

Si vous modifiez directement le bloc-notes, il y a maintenant une icône Convert and save to a python script. Icônes Jupyter dans Visual Studio Code

Voici une capture d'écran d'un bloc-notes dans Visual Studio Code:

Modification du bloc-notes dans VSCode

Je ne l'utilise que depuis une journée, mais je peux enfin utiliser Jupyter avec Git.

PS: l'achèvement du code VSCode est bien meilleur que Jupyter.

neves
la source
12

(2017-02)

stratégies

  • on_commit ():
    • supprimer la sortie> name.ipynb ( nbstripout,)
    • supprimer la sortie> name.clean.ipynb ( nbstripout,)
    • toujours nbconverten python: name.ipynb.py ( nbconvert)
    • toujours convertir en markdown: name.ipynb.md ( nbconvert, ipymd)
  • vcs.configure ():
    • git difftool, mergetool: nbdiff et nbmerge de nbdime

outils

Wes Turner
la source
11

Les réponses très populaires de 2016 ci-dessus sont des hacks incohérents par rapport à la meilleure façon de le faire en 2019.

Plusieurs options existent, la meilleure qui répond à la question est Jupytext.

Jupytext

Catch the Towards Data Science article on Jupytext

La façon dont cela fonctionne avec le contrôle de version consiste à placer les fichiers .py et .ipynb dans le contrôle de version. Regardez le .py si vous voulez le diff d'entrée, regardez le .ipynb si vous voulez la dernière sortie rendue.

Mentions notables: VS studio, nbconvert, nbdime, hydrogène

Je pense qu'avec un peu plus de travail, VS studio et / ou l'hydrogène (ou similaire) deviendront les acteurs dominants de la solution à ce workflow.

SwimBikeRun
la source
9

Il suffit de tomber sur "jupytext" qui ressemble à une solution parfaite. Il génère un fichier .py à partir du bloc-notes, puis les synchronise. Vous pouvez contrôler la version, diff et fusionner les entrées via le fichier .py sans perdre les sorties. Lorsque vous ouvrez le bloc-notes, il utilise le .py pour les cellules d'entrée et le .ipynb pour la sortie. Et si vous souhaitez inclure la sortie dans git, vous pouvez simplement ajouter l'ipynb.

https://github.com/mwouts/jupytext

Simon
la source
9

Puisqu'il existe tellement de stratégies et d'outils pour gérer le contrôle de version pour les ordinateurs portables, j'ai essayé de créer un organigramme pour choisir une stratégie appropriée (créé en avril 2019)

Flux de décision pour choisir la stratégie de contrôle de version

nik
la source
8

Comme indiqué par, le --scriptest déconseillé dans 3.x. Cette approche peut être utilisée en appliquant un hook post-sauvegarde. En particulier, ajoutez ce qui suit à ipython_notebook_config.py:

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

Le code est tiré de # 8009 .

Dror
la source
Merci d'avoir démontré l'utilisation d'un hook post-sauvegarde. Malheureusement, comme mentionné ailleurs, le retour du .pyfichier vers un ordinateur portable est problématique, donc ce n'est malheureusement pas une solution complète. (Je souhaite en quelque sorte que ce soit car il est très agréable de différencier les .pyfichiers au lieu des cahiers. Peut-être que la nouvelle fonction de différenciation des cahiers sera utile.
mforbes
1
Merci! J'utilise maintenant cette astuce pour reproduire le --scriptcomportement, indépendamment du contrôle de version. J'ai eu quelques problèmes au début, donc au cas où je pourrais faire gagner du temps à quelqu'un: 1) Si le ipython_notebook_config.pyest absent du dossier de profil, lancez-le ipython profile createpour le générer. 2) S'il semble que le post-save-hook soit ignoré, exécutez ipython avec --debugpour diagnostiquer le problème. 3) Si le script échoue avec une erreur ImportError: No module named mistune- installation simple minstue:pip install mistune .
Joe
7

Malheureusement, je ne sais pas grand-chose sur Mercurial, mais je peux vous donner une solution possible qui fonctionne avec Git, dans l'espoir que vous puissiez traduire mes commandes Git en leurs équivalents Mercurial.

Pour l'arrière-plan, dans Git, la addcommande stocke les modifications apportées à un fichier dans une zone de transfert. Une fois que vous avez fait cela, toutes les modifications ultérieures du fichier sont ignorées par Git, sauf si vous lui demandez de les mettre en scène également. Par conséquent, le script suivant, qui, pour chacun des fichiers donnés, supprime tous les outputset prompt_number sections, met en scène le fichier supprimé, puis restaure l'original:

REMARQUE: si vous exécutez cette opération, vous obtenez un message d'erreur comme ImportError: No module named IPython.nbformat, puis utilisez ipythonpour exécuter le script à la place de python.

from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv

for filename in argv[1:]:
    # Backup the current file
    backup_filename = filename + ".backup"
    copyfile(filename,backup_filename)

    try:
        # Read in the notebook
        with io.open(filename,'r',encoding='utf-8') as f:
            notebook = current.reads(f.read(),format="ipynb")

        # Strip out all of the output and prompt_number sections
        for worksheet in notebook["worksheets"]:
            for cell in worksheet["cells"]:
               cell.outputs = []
               if "prompt_number" in cell:
                    del cell["prompt_number"]

        # Write the stripped file
        with io.open(filename, 'w', encoding='utf-8') as f:
            current.write(notebook,f,format='ipynb')

        # Run git add to stage the non-output changes
        print("git add",filename)
        Popen(["git","add",filename]).wait()

    finally:
        # Restore the original file;  remove is needed in case
        # we are running in windows.
        remove(filename)
        rename(backup_filename,filename)

Une fois le script exécuté sur les fichiers dont vous souhaitez valider les modifications, lancez-le git commit.

Gregory Crosswhite
la source
Merci pour la suggestion. Mercurial n'a pas vraiment de zone de transit comme git (bien que l'on puisse utiliser des files d' attente mercurial à cet effet). En attendant, j'ai essayé d'ajouter ce code à un crochet de sauvegarde qui enregistre une version propre avec une .cleanextension. Malheureusement, je ne pouvais pas voir comment faire cela sans modifier directement IPython (bien que ce changement ait été assez trivial). Je vais jouer avec cela pendant un certain temps et voir si cela convient à tous mes besoins.
mforbes
6

J'utilise une approche très pragmatique; qui fonctionnent bien pour plusieurs cahiers, sur plusieurs côtés. Et cela me permet même de «transférer» des cahiers. Il fonctionne aussi bien pour Windows que Unix / MacOS.
Al pensé que c'est simple, c'est résoudre les problèmes ci-dessus ...

Concept

Fondamentalement, ne suivez pas les .ipnybfichiers -fichiers, seulement les .pyfichiers- correspondants .
En démarrant le notebook-server avec l' --scriptoption, ce fichier est automatiquement créé / enregistré lors de l'enregistrement du notebook.

Ces .pyfichiers contiennent toutes les entrées; le non-code est enregistré dans les commentaires, tout comme les bordures de cellule. Ces fichiers peuvent être lus / importés (et glissés) dans le notebook-server pour (re) créer un notebook. Seule la sortie a disparu; jusqu'à ce qu'il soit réexécuté.

Personnellement, j'utilise mercurial pour suivre la version des .pyfichiers; et utilisez les commandes normales (ligne de commande) pour ajouter, archiver (ect) pour cela. La plupart des autres (D) VCS le permettront.

C'est simple de suivre l'histoire maintenant; ils .pysont petits, textuels et simples à différencier. De temps en temps, nous avons besoin d'un clone (il suffit de créer une branche; lancez un deuxième ordinateur portable là-bas), ou une version plus ancienne (vérifiez-le et importez-le dans un ordinateur portable-serveur), etc.

Conseils & Astuces

  • Ajoutez * .ipynb à ' .hgignore ', afin que Mercurial sache qu'il peut ignorer ces fichiers
  • Créer un script (bash) pour démarrer le serveur (avec l' --scriptoption) et faire un suivi de version
  • L'enregistrement d'un bloc-notes enregistre le .pyfichier, mais ne l' archive pas .
    • Ceci est un inconvénient : on peut oublier que
    • C'est aussi une fonctionnalité : il est possible d'enregistrer un bloc-notes (et de continuer plus tard) sans regrouper l'historique du référentiel.

Vœux

  • Ce serait bien d'avoir des boutons pour l'enregistrement / ajouter / etc dans le tableau de bord du notebook
  • Un paiement à (par exemple) file@date+rev.py) devrait être utile. Il serait beaucoup de travail d'ajouter cela; et peut-être que je le ferai une fois. Jusqu'à présent, je fais juste ça à la main.
Albert
la source
Comment passez-vous du .pyfichier à un ordinateur portable? J'aime cette approche, mais parce que .ipynb-> .py-> .ipynbest potentiellement avec perte, je n'y ai pas pensé sérieusement.
mforbes
C'est simple: chargez-le, par exemple en le déposant sur de Notebook-dashboard. À l'exception des "données de sortie", rien n'est perdu
Albert
Si cela est vrai, alors je pense que ce serait proche idée, mais je crois me souvenir que IPython n'a pris aucun engagement à préserver les données complètement dans la transition de .pyla .ipynbformats. Il y a un problème à ce sujet - alors peut-être que cela constituera la base d'une solution complète.
mforbes
J'ai du mal à convertir des .pyfichiers en .ipynbfichiers. nbconvertne semble pas encore prendre en charge cela, et je n'ai pas de tableau de bord de bloc-notes car je lance ipython notebookmanuellement. Avez-vous des suggestions générales sur la façon de mettre en œuvre cette conversion en amont?
mforbes
Certes, la .pytransformation en ordinateur portable n'est pas destinée à un aller-retour. Donc, cela ne peut pas vraiment être une solution générale, mais c'est agréable, cela fonctionne pour vous.
holdenweb
3

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')
Guillaume Dumas
la source
3

J'ai construit un package python qui résout ce problème

https://github.com/brookisme/gitnb

Il fournit à une CLI une syntaxe inspirée de git pour suivre / mettre à jour / diff les cahiers à l'intérieur de votre dépôt git.

Voici un exemple

# add a notebook to be tracked
gitnb add SomeNotebook.ipynb

# check the changes before commiting
gitnb diff SomeNotebook.ipynb

# commit your changes (to your git repo)
gitnb commit -am "I fixed a bug"

Notez que la dernière étape, où j'utilise "gitnb commit" est de valider votre dépôt git. C'est essentiellement un emballage pour

# get the latest changes from your python notebooks
gitnb update

# commit your changes ** this time with the native git commit **
git commit -am "I fixed a bug"

Il existe plusieurs autres méthodes, et peut être configuré de sorte qu'il nécessite plus ou moins d'entrée utilisateur à chaque étape, mais c'est l'idée générale.

ruisseau
la source
3

Après avoir fouillé, j'ai finalement trouvé ce crochet de pré-sauvegarde relativement simple sur les documents Jupyter . Il supprime les données de sortie de cellule. Vous devez le coller dans le jupyter_notebook_config.pyfichier (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 ne savez pas dans quel répertoire trouver votre jupyter_notebook_config.pyfichier, vous pouvez taper jupyter --config-dir[dans l'invite de commande / terminal], et si vous n'y trouvez pas le fichier, vous pouvez le créer en tapant jupyter notebook --generate-config.

binaryfunt
la source
1
Je voudrais noter que cette solution n'enregistrerait jamais aucune sortie sur le disque et est quelque peu indépendante du problème de contrôle de version.
bdforbes
2

J'ai fait ce qu'Albert & Rich a fait - Ne pas versionner les fichiers .ipynb (car ceux-ci peuvent contenir des images, ce qui devient désordonné). Au lieu de cela, exécutez ipython notebook --scriptou placez toujours c.FileNotebookManager.save_script = Truevotre fichier de configuration, de sorte qu'un (versionnable).py fichier soit toujours créé lorsque vous enregistrez votre bloc-notes.

Pour régénérer des cahiers (après avoir vérifié un repo ou changé de branche) j'ai mis le script py_file_to_notebooks.py dans le répertoire où je stocke mes cahiers.

Maintenant, après avoir vérifié un dépôt, il suffit de lancer python py_file_to_notebooks.pypour générer les fichiers ipynb. Après avoir changé de branche, vous devrez peut-être exécuterpython py_file_to_notebooks.py -ov pour remplacer les fichiers ipynb existants.

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

Edit: je ne fais plus cela parce que (A) vous devez régénérer vos cahiers à partir de fichiers py à chaque fois que vous extrayez une branche et (B) il y a d'autres choses comme le démarque dans les cahiers que vous perdez. Au lieu de cela, je supprime la sortie des ordinateurs portables à l'aide d'un filtre git. La discussion sur la façon de procéder est ici .

Peter
la source
J'ai aimé cette idée, mais après avoir testé, j'ai constaté que la conversion des .pyfichiers en arrière .ipynbétait problématique, en particulier avec les ordinateurs portables de la version 4 pour lesquels il n'y a pas encore de convertisseur. Il faudrait actuellement utiliser l'importateur v3 puis convertir en v4 et je suis un peu préoccupé par ce voyage compliqué. De plus, un .pyfichier n'est pas un très bon choix si le cahier est principalement du code Julia! Enfin, --scriptest obsolète, donc je pense que les crochets sont la voie à suivre.
mforbes
La solution de filtre git dans votre lien est bonne, vous devriez copier votre réponse à partir d'ici :-)
mcarans
2

Ok, donc cela ressemble à la meilleure solution actuelle, selon une discussion ici , consiste à créer un filtre git pour supprimer 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 légèrement modifié le fichier nbstripout de cfriedline pour donner une erreur informative lorsque vous ne pouvez pas importer la dernière IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output et l'a ajouté à dire./relative/path/to/strip_notebook_output

A également ajouté le fichier .gitattributes à la racine du dépôt, contenant:

*.ipynb filter=stripoutput

Et créé un setup_git_filters.shcontenant

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. La fantaisie $ (git rev-parse ...) est de trouver le chemin local de votre dépôt sur n'importe quelle machine (Unix).

Peter
la source
1

Cette extension jupyter permet aux utilisateurs de pousser les cahiers jupyter directement vers github.

Veuillez regarder ici

https://github.com/sat28/githubcommit

Sam
la source
pouvez-vous expliquer ce que cela fait? La doumentation n'est pas particulièrement claire.
Alex Monras
@AlexMonras Cela ajoutera directement un bouton dans le cahier jupyter à partir duquel vous pouvez envoyer des cahiers à votre dépôt GitHub avec un message de validation
sam
1

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

  • nbdime - Agréable pour la diff'ing locale et la fusion de cahiers

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

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

  • nbconvert - Convertit des blocs-notes en script python ou HTML (ou les deux) et valide ces autres types de fichiers

  • ReviewNB - Affiche la différence de bloc-notes (avec la sortie) pour toute demande de validation ou d'extraction sur GitHub. On peut également écrire des commentaires sur les cellules du carnet pour discuter des changements (capture d'écran ci-dessous).

entrez la description de l'image ici

Avertissement: j'ai créé ReviewNB.

amirathi
la source
0

Que diriez-vous de l'idée discutée dans le post ci-dessous, où la sortie du bloc-notes devrait être conservée, avec l'argument que cela pourrait prendre beaucoup de temps pour le générer, et c'est pratique car GitHub peut maintenant rendre les blocs-notes. Des crochets d'enregistrement automatique ont été ajoutés pour exporter le fichier .py, utilisés pour les différences et .html pour le partage avec les membres de l'équipe qui n'utilisent pas de bloc-notes ou de git.

https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d

Krzysztof Słowiński
la source