Utilisation d’ordinateurs portables IPython sous contrôle de version

Quelle est la bonne stratégie pour garder les ordinateurs portables IPython sous le contrôle de la version?

Le format du cahier est assez adapté au contrôle de version: si l’on veut contrôler la version du cahier et des sorties, cela fonctionne plutôt bien. La gêne survient lorsque l’on veut uniquement contrôler la version de l’entrée, à l’exclusion des sorties de la cellule (aka “produits de construction”) qui peuvent être de gros blobs binarys, en particulier pour les films et les tracés. En particulier, j’essaie de trouver un bon workflow qui:

  • me permet de choisir entre inclure ou exclure la sortie,
  • m’empêche de commettre accidentellement la sortie si je ne le veux pas,
  • me permet de garder 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.-à-d. si seulement la version contrôle les entrées mais mon fichier local a des sorties, alors je voudrais pouvoir voir si les entrées ont changé ) L’utilisation de la commande status control de commande enregistre 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 bloc-notes propre mis à jour. (mettre à 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 lorsque 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 commets accidentellement une version avec la sortie, polluant ainsi mon référentiel.
  2. Je supprime la sortie pour utiliser le contrôle de version, mais préfère vraiment conserver la sortie dans ma copie locale (il faut parfois du temps pour la reproduire, par exemple).
  3. Certains scripts qui suppriment la sortie modifient légèrement le format par rapport à l’option de menu Cell/All Output/Clear , créant ainsi un bruit indésirable dans les diffs. Ceci est résolu par certaines des réponses.
  4. Lorsque vous apportez des modifications à une version propre du fichier, je dois trouver un moyen d’intégrer ces modifications dans mon ordinateur de travail sans avoir à tout réexécuter. (mettre à jour)

J’ai examiné plusieurs options que je discuterai ci-dessous, mais je n’ai pas encore trouvé de solution complète. Une solution complète peut nécessiter des modifications sur IPython ou peut s’appuyer sur des scripts externes simples. J’utilise actuellement mercurial , mais je voudrais une solution qui fonctionne également avec git : une solution idéale serait une solution indépendante du contrôle de version.

Ce problème a été discuté à plusieurs resockets, mais il n’y a pas de solution définitive ou claire du sharepoint vue de l’utilisateur. La réponse à cette question devrait fournir la stratégie définitive. C’est bien s’il nécessite une version récente (même en développement) d’ IPython ou une extension facile à installer.

Mise à jour: je joue avec ma version modifiée de portable qui enregistre en option une version .clean à chaque sauvegarde en utilisant les suggestions de Gregory Crosswhite . Cela satisfait la plupart de mes contraintes mais laisse les solutions non résolues suivantes:

  1. Ce n’est pas encore une solution standard (nécessite une modification de la source d’ipython. Existe-t-il un moyen de réaliser ce comportement avec une simple extension? Besoin d’une sorte de hook de sauvegarde).
  2. Un problème que j’ai avec le stream de travail en cours tire des modifications. Ceux-ci viendront dans le fichier .clean , 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 cahier, mais cela peut être pénible, surtout si certains résultats dépendent de longs calculs, de calculs parallèles, etc.) Je ne sais pas encore comment résoudre ce problème. . Peut-être qu’un workflow impliquant une extension comme ipycache pourrait fonctionner, mais cela semble un peu trop compliqué.

Remarques

Suppression (suppression) de la sortie

  • Lorsque l’ordinateur portable est en cours d’exécution, vous pouvez utiliser l’option de menu Cell/All Output/Clear pour supprimer la sortie.
  • Certains scripts de suppression de sortie, tels que le script nbssortingpout.py, suppriment la sortie, mais ne génèrent pas le même résultat que l’utilisation de l’interface du notebook. Cela a finalement été inclus dans le référentiel ipython / nbconvert , mais celui-ci a été fermé en indiquant que les modifications sont désormais incluses dans ipython / ipython , mais la fonctionnalité correspondante ne semble pas avoir encore été incluse. (mise à jour) Cela étant dit, la solution de Gregory Crosswhite montre que cela est assez facile à faire, même sans invoquer ipython / nbconvert , donc cette approche est probablement réalisable si elle peut être connectée correctement. , ne semble pas être une bonne idée – cela devrait en quelque sorte être lié au mécanisme du cahier.)

Groupes de discussion

  • Réflexions sur le format du bloc-notes pour le contrôle de version .

Problèmes

  • 977: Demandes de fonctionnalités du bloc-notes (Ouvrir) .
  • 1280: Clear-all sur l’option de sauvegarde (Open) . (Suit cette discussion .)
  • 3295: ordinateurs portables autoexportés: exportez uniquement les cellules explicitement marquées (Closed) . Résolu par l’extension 11 Ajouter writeandexecute magic (Merged) .

Pull Demandes

  • 1621: désactivez les numéros d’invite [] sur “Effacer toutes les sorties” (fusionné) . (Voir aussi 2519 (fusionné) .)
  • 1563: améliorations de clear_output (fusionné) .
  • 3065: diff-capacité des cahiers (fermés) .
  • 3291: Ajoutez l’option pour ignorer les cellules de sortie lors de l’enregistrement. (Fermé) Cela semble extrêmement pertinent, mais a été fermé avec la suggestion d’utiliser un filtre “propre / bavure”. Une question pertinente que pouvez-vous utiliser si vous souhaitez supprimer la sortie avant de lancer git diff? ne semble pas avoir reçu de réponse.
  • 3312: WIP: crochets de sauvegarde pour ordinateur portable (fermé) .
  • 3747: ipynb -> ipynb transformer (Closed) . Ceci est rebasé en 4175 .
  • 4175: nbconvert: base de l’exportateur de Jinjaless (fusionné) .
  • 142: Utilisez STDIN dans nbssortingpout si aucune entrée n’est donnée (Open) .

Voici ma solution avec git. Il vous permet simplement d’append et de valider (et de différer) comme d’habitude: ces opérations ne modifieront pas votre arbre de travail, et en même temps, (ré) exécuter un bloc-notes 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 (du moins, à l’agnosticité du VSC). Pourtant, c’est parfait pour moi, et bien que ce ne soit pas particulièrement shiny, et que beaucoup de gens l’utilisent probablement déjà, je n’ai pas trouvé d’instructions claires sur la manière de le mettre en œuvre. 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 ~/.gitatsortingbutes , avec le contenu suivant

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

     git config --global core.atsortingbutesfile ~/.gitatsortingbutes 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
  • à Git, si vous êtes dans une twig de la twig et que vous git checkout otherbranch; git checkout somebranch git checkout otherbranch; git checkout somebranch , vous vous attendez généralement à ce que l’arbre de travail rest inchangé. Ici, vous aurez perdu la sortie et la numérotation des cellules des blocs-notes dont la source diffère entre les deux twigs.
  • plus généralement, la sortie n’est pas du tout versionnée, comme avec la solution de Gregory. Afin de ne pas le jeter à chaque fois que vous faites quelque chose impliquant une extraction, l’approche peut être modifiée en la stockant dans des fichiers séparés (mais notez qu’au moment de l’exécution du code ci-dessus, l’identifiant de validation n’est pas connu!), et peut-être les mettre à jour (mais notez que cela nécessiterait quelque chose de plus qu’un git commit notebook_file.ipynb , même si git diff notebook_file.ipynb serait au moins git diff notebook_file.ipynb base64).
  • Cela dit, accessoirement, si vous tirez du code (c.-à-d. commis par quelqu’un d’autre qui n’utilise pas cette approche) qui contient une sortie, la sortie est extraite normalement. Seule la sortie produite localement est perdue.

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

MODIFIER:

  • Si vous adoptez la solution telle que je l’ai suggérée – c’est-à-dire globalement – vous aurez des problèmes pour certains repo git que vous souhaitez publier en sortie. Donc, si vous voulez désactiver le filtrage de sortie pour un repository git spécifique, créez-y simplement un fichier .git / info / atsortingbutes , avec

    **. Filtre ipynb =

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

  • le code est maintenant maintenu dans son propre repository git

  • Si les instructions ci-dessus ont pour résultat ImportErrors, essayez d’append “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 ceux que je connais: nbssortingpout ( autres variantes ), nbssortingp , jq .

Nous avons un projet collaboratif où le produit est Jupyter Notebooks, et nous utilisons depuis six mois une approche qui fonctionne très bien: nous .ipynb automatiquement l’enregistrement des fichiers .py et .ipynb fichiers .ipynb et les fichiers .py .

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 suffit de regarder les modifications apscopes aux fichiers .py .

Pour les serveurs portables Jupyter , 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 

au fichier jupyter_notebook_config.py et redémarrer le serveur portable.

Si vous ne savez pas dans quel répertoire trouver votre fichier jupyter_notebook_config.py , vous pouvez taper jupyter --config-dir , et si vous ne trouvez pas le fichier, vous pouvez le créer en tapant jupyter notebook --generate-config .

Pour les serveurs portables Ipython 3 , ceci 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 

au fichier ipython_notebook_config.py et redémarrez le serveur portable. Ces lignes proviennent d’un github de réponses à la question @minrk fournie et @dror les inclut également dans sa réponse SO.

Pour les serveurs portables Ipython 2 , cela peut être réalisé en démarrant le serveur en utilisant:

 ipython notebook --script 

ou en ajoutant la ligne

 c.FileNotebookManager.save_script = True 

au fichier ipython_notebook_config.py et redémarrez le serveur portable.

Si vous ne savez pas dans quel répertoire trouver votre fichier ipython_notebook_config.py , vous pouvez taper ipython locate profile default , et si vous ne trouvez pas le fichier, vous pouvez le créer en tapant ipython profile create .

Voici notre projet sur github qui utilise cette approche : voici un exemple github d’exploration des modifications récentes apscopes à un cahier .

Nous avons été très heureux avec cela.

J’ai créé nbssortingpout , basé sur MinRKs gist , qui supporte à la fois Git et Mercurial (grâce à mforbes). Il est destiné à être utilisé soit en mode autonome sur la ligne de commande, soit en tant que filtre, qui est facilement (dés) installé dans le référentiel actuel via nbssortingpout install / nbssortingpout uninstall .

Obtenez-le de PyPI ou simplement

 pip install nbssortingpout 

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

https://github.com/rossant/ipymd

(2017-02)

stratégies

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

outils

Comme indiqué par, le --script est obsolète dans 3.x Cette approche peut être utilisée en appliquant un post-save-hook. 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 .

Malheureusement, je ne connais pas beaucoup Mercurial, mais je peux vous proposer une solution qui fonctionne avec Git, dans l’espoir que vous puissiez traduire mes commandes Git dans leurs équivalents Mercurial.

Pour l’arrière-plan, dans Git, la commande add stocke les modifications apscopes à un fichier dans une zone de transfert. Une fois que vous avez fait cela, les modifications ultérieures apscopes au fichier sont ignorées par Git, à moins que vous ne lui demandiez également de les configurer. Par conséquent, le script suivant, qui, pour chacun des fichiers donnés, supprime toutes les prompt_number sections outputs et d’ prompt_number sections , met en forme le fichier dépouillé, puis restaure l’original:

REMARQUE: Si vous utilisez cette ImportError: No module named IPython.nbformat , vous obtenez un message d’erreur tel que ImportError: No module named IPython.nbformat , puis utilisez ipython pour exécuter le script au lieu 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") # Ssortingp 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 ssortingpped 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 simplement git commit .

J’utilise une approche très pragmatique; qui fonctionnent bien pour plusieurs cahiers, à plusieurs côtés. Et cela me permet même de «transférer» les cahiers. Cela fonctionne à la fois pour Windows comme Unix / MacOS.
Al pense que c’est simple, est résoudre les problèmes ci-dessus …

Concept

Fondamentalement, ne suivez pas les .ipnyb .ipnyb, uniquement les fichiers .py correspondants.
En démarrant le notebook-server avec l’option --script , ce fichier est automatiquement créé / enregistré lorsque le bloc-notes est enregistré.

Ces fichiers .py 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 cahier-serveur pour (re) créer un cahier. Seule la sortie est partie; jusqu’à ce qu’il soit ré-exécuté.

Personnellement, j’utilise mercurial pour suivre les versions des fichiers .py ; et utilisez les commandes normales (ligne de commande) pour append, archiver (ect) pour cela. La plupart des autres VCS le permettront.

C’est simple de suivre l’histoire maintenant; les .py sont petits, textuels et simples à différencier. De temps en temps, nous avons besoin d’un clone (twigz simplement, lancez un second bloc-notes), ou d’une version plus ancienne (check-out et importation dans un ordinateur portable), etc.

Conseils & Astuces

  • Ajoutez * .ipynb à ‘ .hgignore ‘, Mercurial sait donc qu’il peut ignorer ces fichiers
  • Créer un script (bash) pour démarrer le serveur (avec l’option --script ) et faire le suivi de la version
  • L’enregistrement d’un ordinateur portable enregistre le fichier .py , mais ne l’enregistre pas .
    • C’est un inconvénient : on peut oublier que
    • C’est aussi une fonctionnalité : il est possible d’enregistrer un cahier (et de continuer plus tard) sans regrouper l’historique du référentiel.

Vœux

  • Il serait bien d’avoir des boutons pour l’archivage / ajout / etc dans le tableau de bord du cahier
  • Une vérification à (par exemple) file@date+rev.py ) devrait être utile. et peut-être que je le ferai une fois. Jusqu’à présent, je le fais juste à la main.

Pour suivre l’excellent script de Pietro Battiston, si vous obtenez une erreur d’parsing Unicode comme celle-ci:

 Traceback (most recent call last): File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in  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 append au début du script:

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

J’ai fait ce que Albert & Rich a fait – Ne pas éditer les fichiers .ipynb (car ceux-ci peuvent contenir des images, ce qui est compliqué). Au lieu de cela, exécutez toujours ipython notebook --script ou placez c.FileNotebookManager.save_script = True dans votre fichier de configuration, de sorte qu’un fichier (versionable) .py soit toujours créé lorsque vous enregistrez votre bloc-notes.

Pour régénérer des cahiers (après avoir vérifié un repository ou changé de twig), je mets le script py_file_to_notebooks.py dans le répertoire où sont stockés mes cahiers.

Maintenant, après avoir extrait un repository, lancez simplement python py_file_to_notebooks.py pour générer les fichiers ipynb. Après avoir changé de twig, vous devrez peut-être exécuter python py_file_to_notebooks.py -ov pour écraser les fichiers ipynb existants.

Juste pour être du bon côté, il est bon d’append aussi *.ipynb à votre fichier .gitignore .

Edit: Je ne le fais plus parce que (A) vous devez régénérer vos carnets à partir de fichiers py à chaque fois que vous extrayez une twig et (B) vous perdez d’autres choses comme les démarques dans les carnets. Je dépouille plutôt la sortie des cahiers en utilisant un filtre git. Discussion sur la façon de procéder est ici .

Ok, il semble donc que la meilleure solution actuelle, telle que discutée 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é à partir de cette discussion):

J’ai modifié légèrement le fichier nbssortingpout de cfriedline pour donner une erreur d’information lorsque vous ne pouvez pas importer la dernière version d’IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/ssortingp_notebook_output Et l’a ajouté à mon repository, laisse dire dans ./relative/path/to/ssortingp_notebook_output

Ajout du fichier .gitatsortingbutes à la racine du repository, contenant:

 *.ipynb filter=ssortingpoutput 

Et créé un setup_git_filters.sh contenant

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

Et exécuté source setup_git_filters.sh . Le truc de $ (git rev-parse …) est de trouver le chemin local de votre repository sur n’importe quelle machine (Unix).

J’ai construit le paquet python qui résout ce problème

https://github.com/brookisme/gitnb

Il fournit une interface de ligne de commande avec une syntaxe inspirée de git pour suivre / mettre à jour / diff les ordinateurs portables à l’intérieur de votre repository git.

Heres ‘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”, s’engage dans votre repository 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 manière à nécessiter plus ou moins d’informations de la part des utilisateurs à chaque étape, mais c’est l’idée générale.

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

Que diriez-vous de l’idée évoquée dans le post ci-dessous, où la sortie du cahier devrait être conservée, avec l’argument que cela pourrait prendre beaucoup de temps pour le générer, et c’est pratique puisque GitHub peut maintenant rendre des cahiers. Il y a des hooks de sauvegarde automatique ajoutés pour l’exportation du fichier .py, utilisés pour les fichiers diffs et .html pour le partage avec les membres de l’équipe qui n’utilisent pas de notebooks ou de git.

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

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

S’il vous plaît regardez ici

https://github.com/sat28/githubcommit